<commit_before>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/vespalib/testkit/test_kit.h>

#include <vespa/eval/eval/value_type.h>
#include <vespa/searchlib/fef/feature_type.h>
#include <vespa/searchlib/fef/featurenameparser.h>
#include <vespa/searchlib/features/rankingexpressionfeature.h>
#include <vespa/searchlib/fef/test/dummy_dependency_handler.h>
#include <vespa/searchlib/fef/test/indexenvironment.h>
#include <vespa/searchlib/fef/test/queryenvironment.h>

using namespace search::features;
using namespace search::features::rankingexpression;
using namespace search::fef::test;
using namespace search::fef;
using namespace vespalib::eval;

using TypeMap = std::map<vespalib::string,vespalib::string>;

struct DummyExecutor : FeatureExecutor {
    void execute(uint32_t) override {}
};

struct DummyExpression : IntrinsicExpression {
    FeatureType type;
    DummyExpression(const FeatureType &type_in) : type(type_in) {}
    vespalib::string describe_self() const override { return "dummy"; }
    const FeatureType &result_type() const override { return type; }
    void prepare_shared_state(const QueryEnv &, IObjectStore &) const override {
    }
    FeatureExecutor &create_executor(const QueryEnv &, vespalib::Stash &stash) const override {
        return stash.create<DummyExecutor>();
    }
};

struct DummyReplacer : ExpressionReplacer {
    vespalib::string trigger;
    FeatureType type;
    DummyReplacer(const vespalib::string trigger_in, const FeatureType &type_in)
        : trigger(trigger_in),
          type(type_in)
    {}
    IntrinsicExpression::UP maybe_replace(const vespalib::eval::Function &function,
                                          const search::fef::IIndexEnvironment &) const override
    {
        for (size_t i = 0; i < function.num_params(); ++i) {
            if (function.param_name(i) == trigger) {
                return std::make_unique<DummyExpression>(type);
            }
        }
        return IntrinsicExpression::UP(nullptr);
    }
};

ExpressionReplacer::SP make_replacer() {
    auto replacer = std::make_shared<ListExpressionReplacer>();
    replacer->add(std::make_unique<NullExpressionReplacer>());
    replacer->add(std::make_unique<DummyReplacer>("foo", FeatureType::number()));
    replacer->add(std::make_unique<DummyReplacer>("bar", FeatureType::object(ValueType::from_spec("tensor(x[5])"))));
    return replacer;
}

struct SetupResult {
    vespalib::Stash stash;
    IndexEnvironment index_env;
    QueryEnvironment query_env;
    RankingExpressionBlueprint rank;
    DummyDependencyHandler deps;
    bool setup_ok;
    SetupResult(const TypeMap &object_inputs, const vespalib::string &expression);
    ~SetupResult();
};

SetupResult::SetupResult(const TypeMap &object_inputs,
                         const vespalib::string &expression)
    : stash(), index_env(), query_env(&index_env), rank(make_replacer()), deps(rank), setup_ok(false)
{
    rank.setName("self");
    index_env.getProperties().add("self.rankingScript", expression);
    for (const auto &input: object_inputs) {
        deps.define_object_input(input.first, ValueType::from_spec(input.second));
    }
    setup_ok = rank.setup(index_env, {});
    EXPECT_TRUE(!deps.accept_type_mismatch);
}
SetupResult::~SetupResult() {}

void verify_output_type(const TypeMap &object_inputs,
                        const vespalib::string &expression, const FeatureType &expect)
{
    SetupResult result(object_inputs, expression);
    EXPECT_TRUE(result.setup_ok);
    EXPECT_EQUAL(1u, result.deps.output.size());
    ASSERT_EQUAL(1u, result.deps.output_type.size());
    if (expect.is_object()) {
        EXPECT_EQUAL(expect.type(), result.deps.output_type[0].type());
    } else {
        EXPECT_TRUE(!result.deps.output_type[0].is_object());
    }
}

void verify_setup_fail(const TypeMap &object_inputs,
                       const vespalib::string &expression)
{
    SetupResult result(object_inputs, expression);
    EXPECT_TRUE(!result.setup_ok);
    EXPECT_EQUAL(0u, result.deps.output.size());
}

void verify_input_count(const vespalib::string &expression, size_t expect) {
    SetupResult result({}, expression);
    EXPECT_TRUE(result.setup_ok);
    EXPECT_EQUAL(result.deps.input.size(), expect);
}

TEST("require that expression with only number inputs produce number output (compiled)") {
    TEST_DO(verify_output_type({}, "a*b", FeatureType::number()));
}

TEST("require that expression with object input produces object output (interpreted)") {
    TEST_DO(verify_output_type({{"b", "double"}}, "a*b", FeatureType::object(ValueType::double_type())));
}

TEST("require that ranking expression can resolve to concrete complex type") {
    TEST_DO(verify_output_type({{"a", "tensor(x{},y{})"}, {"b", "tensor(y{},z{})"}}, "a*b",
                               FeatureType::object(ValueType::from_spec("tensor(x{},y{},z{})"))));
}

TEST("require that setup fails for incompatible types") {
    TEST_DO(verify_setup_fail({{"a", "tensor(x{},y{})"}, {"b", "tensor(y[10],z{})"}}, "a*b"));
}

TEST("require that replaced expressions have no inputs") {
    TEST_DO(verify_input_count("a*b*c", 3u));
    TEST_DO(verify_input_count("foo*b*c", 0u));
    TEST_DO(verify_input_count("a*b*bar", 0u));
    TEST_DO(verify_input_count("foo*b*bar", 0u));
}

TEST("require that replaced expressions override result type") {
    TEST_DO(verify_output_type({{"b", "tensor(z{})"}}, "a*b*c",
                               FeatureType::object(ValueType::from_spec("tensor(z{})"))));
    TEST_DO(verify_output_type({{"b", "tensor(z{})"}}, "foo*b*c",
                               FeatureType::number()));
    TEST_DO(verify_output_type({{"b", "tensor(z{})"}}, "a*b*bar",
                               FeatureType::object(ValueType::from_spec("tensor(x[5])"))));
    TEST_DO(verify_output_type({{"b", "tensor(z{})"}}, "foo*b*bar",
                               FeatureType::number()));
}

TEST_F("require that replaced expressions create the appropriate executor", SetupResult({}, "foo")) {
    EXPECT_TRUE(f1.setup_ok);
    FeatureExecutor &executor = f1.rank.createExecutor(f1.query_env, f1.stash);
    EXPECT_TRUE(dynamic_cast<DummyExecutor*>(&executor) != nullptr);
}

TEST_MAIN() { TEST_RUN_ALL(); }
<commit_msg>= default<commit_after>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/vespalib/testkit/test_kit.h>

#include <vespa/eval/eval/value_type.h>
#include <vespa/searchlib/fef/feature_type.h>
#include <vespa/searchlib/fef/featurenameparser.h>
#include <vespa/searchlib/features/rankingexpressionfeature.h>
#include <vespa/searchlib/fef/test/dummy_dependency_handler.h>
#include <vespa/searchlib/fef/test/indexenvironment.h>
#include <vespa/searchlib/fef/test/queryenvironment.h>

using namespace search::features;
using namespace search::features::rankingexpression;
using namespace search::fef::test;
using namespace search::fef;
using namespace vespalib::eval;

using TypeMap = std::map<vespalib::string,vespalib::string>;

struct DummyExecutor : FeatureExecutor {
    void execute(uint32_t) override {}
};

struct DummyExpression : IntrinsicExpression {
    FeatureType type;
    DummyExpression(const FeatureType &type_in) : type(type_in) {}
    vespalib::string describe_self() const override { return "dummy"; }
    const FeatureType &result_type() const override { return type; }
    void prepare_shared_state(const QueryEnv &, IObjectStore &) const override {
    }
    FeatureExecutor &create_executor(const QueryEnv &, vespalib::Stash &stash) const override {
        return stash.create<DummyExecutor>();
    }
};

struct DummyReplacer : ExpressionReplacer {
    vespalib::string trigger;
    FeatureType type;
    DummyReplacer(const vespalib::string trigger_in, const FeatureType &type_in)
        : trigger(trigger_in),
          type(type_in)
    {}
    IntrinsicExpression::UP maybe_replace(const vespalib::eval::Function &function,
                                          const search::fef::IIndexEnvironment &) const override
    {
        for (size_t i = 0; i < function.num_params(); ++i) {
            if (function.param_name(i) == trigger) {
                return std::make_unique<DummyExpression>(type);
            }
        }
        return IntrinsicExpression::UP(nullptr);
    }
};

ExpressionReplacer::SP make_replacer() {
    auto replacer = std::make_shared<ListExpressionReplacer>();
    replacer->add(std::make_unique<NullExpressionReplacer>());
    replacer->add(std::make_unique<DummyReplacer>("foo", FeatureType::number()));
    replacer->add(std::make_unique<DummyReplacer>("bar", FeatureType::object(ValueType::from_spec("tensor(x[5])"))));
    return replacer;
}

struct SetupResult {
    vespalib::Stash stash;
    IndexEnvironment index_env;
    QueryEnvironment query_env;
    RankingExpressionBlueprint rank;
    DummyDependencyHandler deps;
    bool setup_ok;
    SetupResult(const TypeMap &object_inputs, const vespalib::string &expression);
    ~SetupResult();
};

SetupResult::SetupResult(const TypeMap &object_inputs,
                         const vespalib::string &expression)
    : stash(), index_env(), query_env(&index_env), rank(make_replacer()), deps(rank), setup_ok(false)
{
    rank.setName("self");
    index_env.getProperties().add("self.rankingScript", expression);
    for (const auto &input: object_inputs) {
        deps.define_object_input(input.first, ValueType::from_spec(input.second));
    }
    setup_ok = rank.setup(index_env, {});
    EXPECT_TRUE(!deps.accept_type_mismatch);
}
SetupResult::~SetupResult() = default;

void verify_output_type(const TypeMap &object_inputs,
                        const vespalib::string &expression, const FeatureType &expect)
{
    SetupResult result(object_inputs, expression);
    EXPECT_TRUE(result.setup_ok);
    EXPECT_EQUAL(1u, result.deps.output.size());
    ASSERT_EQUAL(1u, result.deps.output_type.size());
    if (expect.is_object()) {
        EXPECT_EQUAL(expect.type(), result.deps.output_type[0].type());
    } else {
        EXPECT_TRUE(!result.deps.output_type[0].is_object());
    }
}

void verify_setup_fail(const TypeMap &object_inputs,
                       const vespalib::string &expression)
{
    SetupResult result(object_inputs, expression);
    EXPECT_TRUE(!result.setup_ok);
    EXPECT_EQUAL(0u, result.deps.output.size());
}

void verify_input_count(const vespalib::string &expression, size_t expect) {
    SetupResult result({}, expression);
    EXPECT_TRUE(result.setup_ok);
    EXPECT_EQUAL(result.deps.input.size(), expect);
}

TEST("require that expression with only number inputs produce number output (compiled)") {
    TEST_DO(verify_output_type({}, "a*b", FeatureType::number()));
}

TEST("require that expression with object input produces object output (interpreted)") {
    TEST_DO(verify_output_type({{"b", "double"}}, "a*b", FeatureType::object(ValueType::double_type())));
}

TEST("require that ranking expression can resolve to concrete complex type") {
    TEST_DO(verify_output_type({{"a", "tensor(x{},y{})"}, {"b", "tensor(y{},z{})"}}, "a*b",
                               FeatureType::object(ValueType::from_spec("tensor(x{},y{},z{})"))));
}

TEST("require that setup fails for incompatible types") {
    TEST_DO(verify_setup_fail({{"a", "tensor(x{},y{})"}, {"b", "tensor(y[10],z{})"}}, "a*b"));
}

TEST("require that replaced expressions have no inputs") {
    TEST_DO(verify_input_count("a*b*c", 3u));
    TEST_DO(verify_input_count("foo*b*c", 0u));
    TEST_DO(verify_input_count("a*b*bar", 0u));
    TEST_DO(verify_input_count("foo*b*bar", 0u));
}

TEST("require that replaced expressions override result type") {
    TEST_DO(verify_output_type({{"b", "tensor(z{})"}}, "a*b*c",
                               FeatureType::object(ValueType::from_spec("tensor(z{})"))));
    TEST_DO(verify_output_type({{"b", "tensor(z{})"}}, "foo*b*c",
                               FeatureType::number()));
    TEST_DO(verify_output_type({{"b", "tensor(z{})"}}, "a*b*bar",
                               FeatureType::object(ValueType::from_spec("tensor(x[5])"))));
    TEST_DO(verify_output_type({{"b", "tensor(z{})"}}, "foo*b*bar",
                               FeatureType::number()));
}

TEST_F("require that replaced expressions create the appropriate executor", SetupResult({}, "foo")) {
    EXPECT_TRUE(f1.setup_ok);
    FeatureExecutor &executor = f1.rank.createExecutor(f1.query_env, f1.stash);
    EXPECT_TRUE(dynamic_cast<DummyExecutor*>(&executor) != nullptr);
}

TEST_MAIN() { TEST_RUN_ALL(); }
<|endoftext|>


<commit_before>#include "SharedMessagePool.hpp"
#include "Statistics.hpp"
#include "ConditionVariable.hpp"
#include <stack>

DEFINE_int64(shared_pool_size, 1L << 20, "Size (in bytes) of global SharedMessagePool (on each Core)");

/// Note: max is enforced only for blockable callers, and is enforced early to avoid callers
/// that cannot block (callers from message handlers) to have a greater chance of proceeding.
DEFINE_int64(shared_pool_max, -1, "Maximum number of shared pools allowed to be allocated (per core).");

GRAPPA_DEFINE_STAT(SimpleStatistic<uint64_t>, shared_message_pools_allocated, 0);

namespace Grappa {

  /// type T must have a member: 'T * next'
  template< typename T, void *(*Allocator)(size_t) = malloc >
  class PiggybackStack {
  protected:
    T* pop() {
      T* r = top;
      if (r != nullptr) {
        top = r->next;
        if (top) CHECK(top->allocated == 0);
        r->next = nullptr;
      }
      return r;
    }

    bool is_emergency() { return global_scheduler.in_no_switch_region(); }
  public:
    long allocated;
    const long max_alloc;
    const long emergency_alloc;
    T * top;

    PiggybackStack(long max_alloc = -1, long emergency_alloc = 0): allocated(0), max_alloc(max_alloc), emergency_alloc(emergency_alloc), top(nullptr) {}

    ~PiggybackStack() {
      CHECK(false) << "didn't think this was being cleaned up.";
      while (!empty()) {
        delete pop();
      }
    }

    void release(T * s) {
      DCHECK(s->allocated == 0);
      s->next = top;
      top = s;
    }

    T* take() {
      T* r = nullptr;
      if (!empty()) {
        r = pop();
      } else if ((max_alloc == -1) || (allocated < (max_alloc-emergency_alloc))
                || (is_emergency() && allocated < max_alloc)) {
        allocated++;
        shared_message_pools_allocated++;
        r = new (Allocator(sizeof(T))) T();
      }
      // DCHECK(r != nullptr);
      // new (r) T();
      return r;
    }

    bool empty() { return top == nullptr; }

    long find(T *o) {
      long id = 0;
      for (auto *i = top; i; i = i->next, id++) {
        if (i == o) return id;
      }
      return -1;
    }

    long size() {
      long id = 0;
      for (auto *i = top; i; i = i->next, id++);
      return id;
    }

  };

  SharedMessagePool * shared_pool = nullptr;

  PiggybackStack<SharedMessagePool,locale_alloc<void>> *pool_stack;

  ConditionVariable blocked_senders;

  void* _shared_pool_alloc_message(size_t sz) {
    DCHECK_GE(shared_pool->remaining(), sz);
    DCHECK( !shared_pool->emptying );
    shared_pool->to_send++;
    return shared_pool->PoolAllocator::allocate(sz);
  }

  void init_shared_pool() {
    pool_stack = new PiggybackStack<SharedMessagePool,locale_alloc<void>>(FLAGS_shared_pool_max, FLAGS_shared_pool_max/4);
    shared_pool = pool_stack->take();
  }

  void* _shared_pool_alloc(size_t sz) {
// #ifdef DEBUG
    // auto i = pool_stack->find(shared_pool);
    // if (i >= 0) VLOG(0) << "found: " << shared_pool << ": " << i << " / " << pool_stack->size();
// #endif
    DCHECK(!shared_pool || shared_pool->next == nullptr);
    if (shared_pool && !shared_pool->emptying && shared_pool->remaining() >= sz) {
      return _shared_pool_alloc_message(sz);
    } else {
      // if not emptying already, do it
      auto *o = shared_pool;
      shared_pool = nullptr;
      if (o && !o->emptying) {
        CHECK_GT(o->allocated, 0);
        o->emptying = true;
        if (o->to_send == 0) {
          o->on_empty();
        }
      }
      DCHECK(pool_stack->empty() || pool_stack->top->allocated == 0);
      // find new shared_pool
      SharedMessagePool *p = pool_stack->take();
      if (p) {
        CHECK_EQ(p->allocated, 0) << "not a fresh pool!";
        shared_pool = p;
        return _shared_pool_alloc_message(sz);
      } else {
        // can't allocate any more
        // try to block until a pool frees up
        do {
          Grappa::wait(&blocked_senders);
          if (shared_pool && !shared_pool->emptying && shared_pool->remaining() >= sz) {
            p = shared_pool;
          } else {
            p = pool_stack->take();
          }
        } while (!p);

        // p must be able to allocate
        shared_pool = p;
        return _shared_pool_alloc_message(sz);
      }
    }
  }

  void SharedMessagePool::message_sent(impl::MessageBase* m) {
    DCHECK(this->next == nullptr);
#ifdef DEBUG
    validate_in_pool(m);
#endif
    this->to_send--;
    if (this->emptying && this->to_send == 0) {
      // CHECK(this != shared_pool);
      DCHECK_GT(this->allocated, 0);
      this->on_empty();
    }
  }

  void SharedMessagePool::on_empty() {
    CHECK(this->next == nullptr);
    CHECK(this != shared_pool);

    DCHECK_EQ(this->to_send, 0);
    DVLOG(3) << "empty and emptying, to_send:"<< to_send << ", allocated:" << allocated << "/" << buffer_size << " @ " << this << ", buf:" << (void*)buffer << "  completions_received:" << completions_received << ", allocated_count:" << allocated_count;
// #ifdef DEBUG
    // verify everything sent
    // this->iterate([](impl::MessageBase* m) {
    //   if (!m->is_sent_ || !m->is_delivered_ || !m->is_enqueued_) {
    //     CHECK(false) << "!! message(" << m << ", is_sent_:" << m->is_sent_ << ", is_delivered_:" << m->is_delivered_ << ", m->is_enqueued_:" << m->is_enqueued_ << ", extra:" << m->pool << ")";
    //   }
    // });

    memset(buffer, (0x11*locale_mycore()) | 0xf0, buffer_size); // poison

// #endif
    DCHECK( pool_stack->find(this) == -1 );
    DCHECK( this->next == nullptr );

    this->reset();
    pool_stack->release(this);
    broadcast(&blocked_senders);
  }

} // namespace Grappa
<commit_msg>set reasonably-sensible default shared_pool_max<commit_after>#include "SharedMessagePool.hpp"
#include "Statistics.hpp"
#include "ConditionVariable.hpp"
#include <stack>

DEFINE_int64(shared_pool_size, 1L << 18, "Size (in bytes) of global SharedMessagePool (on each Core)");

/// Note: max is enforced only for blockable callers, and is enforced early to avoid callers
/// that cannot block (callers from message handlers) to have a greater chance of proceeding.
/// In 'init_shared_pool()', 'emergency_alloc' margin is set, currently, to 1/4 of the max.
///
/// Currently setting this max to cap shared_pool usage at ~1GB, though this may be too much
/// for some apps & machines, as it is allocated out of the locale-shared heap. It's also
/// worth noting that under current settings, most apps don't reach this point at steady state.
DEFINE_int64(shared_pool_max, 1L << 12, "Maximum number of shared pools allowed to be allocated (per core).");

GRAPPA_DEFINE_STAT(SimpleStatistic<uint64_t>, shared_message_pools_allocated, 0);

namespace Grappa {

  /// type T must have a member: 'T * next'
  template< typename T, void *(*Allocator)(size_t) = malloc >
  class PiggybackStack {
  protected:
    T* pop() {
      T* r = top;
      if (r != nullptr) {
        top = r->next;
        if (top) CHECK(top->allocated == 0);
        r->next = nullptr;
      }
      return r;
    }

    bool is_emergency() { return global_scheduler.in_no_switch_region(); }
  public:
    long allocated;
    const long max_alloc;
    const long emergency_alloc;
    T * top;

    PiggybackStack(long max_alloc = -1, long emergency_alloc = 0): allocated(0), max_alloc(max_alloc), emergency_alloc(emergency_alloc), top(nullptr) {}

    ~PiggybackStack() {
      CHECK(false) << "didn't think this was being cleaned up.";
      while (!empty()) {
        delete pop();
      }
    }

    void release(T * s) {
      DCHECK(s->allocated == 0);
      s->next = top;
      top = s;
    }

    T* take() {
      T* r = nullptr;
      if (!empty()) {
        r = pop();
      } else if ((max_alloc == -1) || (allocated < (max_alloc-emergency_alloc))
                || (is_emergency() && allocated < max_alloc)) {
        allocated++;
        shared_message_pools_allocated++;
        r = new (Allocator(sizeof(T))) T();
      }
      // DCHECK(r != nullptr);
      // new (r) T();
      return r;
    }

    bool empty() { return top == nullptr; }

    long find(T *o) {
      long id = 0;
      for (auto *i = top; i; i = i->next, id++) {
        if (i == o) return id;
      }
      return -1;
    }

    long size() {
      long id = 0;
      for (auto *i = top; i; i = i->next, id++);
      return id;
    }

  };

  SharedMessagePool * shared_pool = nullptr;

  PiggybackStack<SharedMessagePool,locale_alloc<void>> *pool_stack;

  ConditionVariable blocked_senders;

  void* _shared_pool_alloc_message(size_t sz) {
    DCHECK_GE(shared_pool->remaining(), sz);
    DCHECK( !shared_pool->emptying );
    shared_pool->to_send++;
    return shared_pool->PoolAllocator::allocate(sz);
  }

  void init_shared_pool() {
    pool_stack = new PiggybackStack<SharedMessagePool,locale_alloc<void>>(FLAGS_shared_pool_max, FLAGS_shared_pool_max/4);
    shared_pool = pool_stack->take();
  }

  void* _shared_pool_alloc(size_t sz) {
// #ifdef DEBUG
    // auto i = pool_stack->find(shared_pool);
    // if (i >= 0) VLOG(0) << "found: " << shared_pool << ": " << i << " / " << pool_stack->size();
// #endif
    DCHECK(!shared_pool || shared_pool->next == nullptr);
    if (shared_pool && !shared_pool->emptying && shared_pool->remaining() >= sz) {
      return _shared_pool_alloc_message(sz);
    } else {
      // if not emptying already, do it
      auto *o = shared_pool;
      shared_pool = nullptr;
      if (o && !o->emptying) {
        CHECK_GT(o->allocated, 0);
        o->emptying = true;
        if (o->to_send == 0) {
          o->on_empty();
        }
      }
      DCHECK(pool_stack->empty() || pool_stack->top->allocated == 0);
      // find new shared_pool
      SharedMessagePool *p = pool_stack->take();
      if (p) {
        CHECK_EQ(p->allocated, 0) << "not a fresh pool!";
        shared_pool = p;
        return _shared_pool_alloc_message(sz);
      } else {
        // can't allocate any more
        // try to block until a pool frees up
        do {
          Grappa::wait(&blocked_senders);
          if (shared_pool && !shared_pool->emptying && shared_pool->remaining() >= sz) {
            p = shared_pool;
          } else {
            p = pool_stack->take();
          }
        } while (!p);

        // p must be able to allocate
        shared_pool = p;
        return _shared_pool_alloc_message(sz);
      }
    }
  }

  void SharedMessagePool::message_sent(impl::MessageBase* m) {
    DCHECK(this->next == nullptr);
#ifdef DEBUG
    validate_in_pool(m);
#endif
    this->to_send--;
    if (this->emptying && this->to_send == 0) {
      // CHECK(this != shared_pool);
      DCHECK_GT(this->allocated, 0);
      this->on_empty();
    }
  }

  void SharedMessagePool::on_empty() {
    CHECK(this->next == nullptr);
    CHECK(this != shared_pool);

    DCHECK_EQ(this->to_send, 0);
    DVLOG(3) << "empty and emptying, to_send:"<< to_send << ", allocated:" << allocated << "/" << buffer_size << " @ " << this << ", buf:" << (void*)buffer << "  completions_received:" << completions_received << ", allocated_count:" << allocated_count;
// #ifdef DEBUG
    // verify everything sent
    // this->iterate([](impl::MessageBase* m) {
    //   if (!m->is_sent_ || !m->is_delivered_ || !m->is_enqueued_) {
    //     CHECK(false) << "!! message(" << m << ", is_sent_:" << m->is_sent_ << ", is_delivered_:" << m->is_delivered_ << ", m->is_enqueued_:" << m->is_enqueued_ << ", extra:" << m->pool << ")";
    //   }
    // });

    memset(buffer, (0x11*locale_mycore()) | 0xf0, buffer_size); // poison

// #endif
    DCHECK( pool_stack->find(this) == -1 );
    DCHECK( this->next == nullptr );

    this->reset();
    pool_stack->release(this);
    broadcast(&blocked_senders);
  }

} // namespace Grappa
<|endoftext|>


<commit_before>/*

Copyright (c) 2006, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/date_time/posix_time/ptime.hpp>
#include <boost/bind.hpp>

#include <libtorrent/io.hpp>
#include <libtorrent/invariant_check.hpp>
#include <libtorrent/kademlia/rpc_manager.hpp>
#include <libtorrent/kademlia/logging.hpp>
#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/hasher.hpp>

#include <fstream>

using boost::posix_time::ptime;
using boost::posix_time::time_duration;
using boost::posix_time::microsec_clock;
using boost::posix_time::seconds;
using boost::posix_time::milliseconds;
using boost::shared_ptr;
using boost::bind;

namespace libtorrent { namespace dht
{

namespace io = libtorrent::detail;

#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DEFINE_LOG(rpc)
#endif

node_id generate_id();

rpc_manager::rpc_manager(fun const& f, node_id const& our_id
	, routing_table& table, send_fun const& sf)
	: m_next_transaction_id(rand() % max_transactions)
	, m_oldest_transaction_id(m_next_transaction_id)
	, m_incoming(f)
	, m_send(sf)
	, m_our_id(our_id)
	, m_table(table)
	, m_timer(boost::posix_time::microsec_clock::universal_time())
	, m_random_number(generate_id())
	, m_destructing(false)
{
	std::srand(time(0));
}

rpc_manager::~rpc_manager()
{
	m_destructing = true;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
	TORRENT_LOG(rpc) << "Destructing";
#endif
	std::for_each(m_aborted_transactions.begin(), m_aborted_transactions.end()
		, bind(&observer::abort, _1));

	for (transactions_t::iterator i = m_transactions.begin()
		, end(m_transactions.end()); i != end; ++i)
	{
		if (*i) (*i)->abort();
	}
}

#ifndef NDEBUG
void rpc_manager::check_invariant() const
{
	assert(m_oldest_transaction_id >= 0);
	assert(m_oldest_transaction_id < max_transactions);
	assert(m_next_transaction_id >= 0);
	assert(m_next_transaction_id < max_transactions);
	assert(!m_transactions[m_next_transaction_id]);

	for (int i = (m_next_transaction_id + 1) % max_transactions;
		i != m_oldest_transaction_id; i = (i + 1) % max_transactions)
	{
		assert(!m_transactions[i]);
	}
}
#endif

bool rpc_manager::incoming(msg const& m)
{
	INVARIANT_CHECK;

	if (m_destructing) return false;

	if (m.reply)
	{
		// if we don't have the transaction id in our
		// request list, ignore the packet

		if (m.transaction_id.size() != 2)
		{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
			TORRENT_LOG(rpc) << "Reply with invalid transaction id size: "
				<< m.transaction_id.size() << " from " << m.addr;
#endif
			return false;
		}

		std::string::const_iterator i = m.transaction_id.begin();
		int tid = io::read_uint16(i);

		if (tid >= (int)m_transactions.size()
			|| tid < 0)
		{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
			TORRENT_LOG(rpc) << "Reply with unknown transaction id: "
				<< tid << " from " << m.addr;
#endif
			return false;
		}

		boost::shared_ptr<observer> o = m_transactions[tid];

		if (!o)
		{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
			TORRENT_LOG(rpc) << "Reply with unknown transaction id: "
				<< tid << " from " << m.addr;
#endif
			return false;
		}

		if (m.addr != o->target_addr)
		{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
			TORRENT_LOG(rpc) << "Reply with incorrect address and valid transaction id: "
				<< tid << " from " << m.addr;
#endif
			return false;
		}

#ifdef TORRENT_DHT_VERBOSE_LOGGING
		std::ofstream reply_stats("libtorrent_logs/round_trip_ms.log", std::ios::app);
		reply_stats << m.addr << "\t" << (microsec_clock::universal_time()
			- o->sent).total_milliseconds() << std::endl;
#endif
		o->reply(m);
		m_transactions[tid].reset();

		if (m.piggy_backed_ping)
		{
			// there is a ping request piggy
			// backed in this reply
			msg ph;
			ph.message_id = messages::ping;
			ph.transaction_id = m.ping_transaction_id;
			ph.id = m_our_id;
			ph.addr = m.addr;

			msg empty;

			reply(empty, ph);
		}
		return m_table.node_seen(m.id, m.addr);
	}
	else
	{
		// this is an incoming request
		m_incoming(m);
	}
	return false;
}

time_duration rpc_manager::tick()
{
	INVARIANT_CHECK;

	using boost::posix_time::microsec_clock;

	const int timeout_ms = 10 * 1000;

	//	look for observers that has timed out

	if (m_next_transaction_id == m_oldest_transaction_id) return milliseconds(timeout_ms);

	std::vector<shared_ptr<observer> > timeouts;

	for (;m_next_transaction_id != m_oldest_transaction_id;
		m_oldest_transaction_id = (m_oldest_transaction_id + 1) % max_transactions)
	{
		assert(m_oldest_transaction_id >= 0);
		assert(m_oldest_transaction_id < max_transactions);

		boost::shared_ptr<observer> o = m_transactions[m_oldest_transaction_id];
		if (!o) continue;

		time_duration diff = o->sent + milliseconds(timeout_ms)
			- microsec_clock::universal_time();
		if (diff > seconds(0))
		{
			if (diff < seconds(1)) return seconds(1);
			return diff;
		}

		try
		{
			m_transactions[m_oldest_transaction_id].reset();
			timeouts.push_back(o);
		} catch (std::exception) {}
	}

	std::for_each(timeouts.begin(), timeouts.end(), bind(&observer::timeout, _1));
	timeouts.clear();

	// clear the aborted transactions, will likely
	// generate new requests. We need to swap, since the
	// destrutors may add more observers to the m_aborted_transactions
	std::vector<shared_ptr<observer> >().swap(m_aborted_transactions);
	return milliseconds(timeout_ms);
}

unsigned int rpc_manager::new_transaction_id(shared_ptr<observer> o)
{
	INVARIANT_CHECK;

	unsigned int tid = m_next_transaction_id;
	m_next_transaction_id = (m_next_transaction_id + 1) % max_transactions;
	if (m_transactions[m_next_transaction_id])
	{
		// moving the observer into the set of aborted transactions
		// it will prevent it from spawning new requests right now,
		// since that would break the invariant
		m_aborted_transactions.push_back(m_transactions[m_next_transaction_id]);
		m_transactions[m_next_transaction_id].reset();
		assert(m_oldest_transaction_id == m_next_transaction_id);
	}
	assert(!m_transactions[tid]);
	m_transactions[tid] = o;
	if (m_oldest_transaction_id == m_next_transaction_id)
	{
		m_oldest_transaction_id = (m_oldest_transaction_id + 1) % max_transactions;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
		TORRENT_LOG(rpc) << "WARNING: transaction limit reached! Too many concurrent"
			" messages! limit: " << (int)max_transactions;
#endif
		update_oldest_transaction_id();
	}

	return tid;
}

void rpc_manager::update_oldest_transaction_id()
{
	INVARIANT_CHECK;

	assert(m_oldest_transaction_id != m_next_transaction_id);
	while (!m_transactions[m_oldest_transaction_id])
	{
		m_oldest_transaction_id = (m_oldest_transaction_id + 1)
			% max_transactions;
		if (m_oldest_transaction_id == m_next_transaction_id)
			break;
	}
}

void rpc_manager::invoke(int message_id, udp::endpoint target_addr
	, shared_ptr<observer> o)
{
	INVARIANT_CHECK;

	if (m_destructing)
	{
		o->abort();
		return;
	}

	msg m;
	m.message_id = message_id;
	m.reply = false;
	m.id = m_our_id;
	m.addr = target_addr;
	assert(!m_transactions[m_next_transaction_id]);
	try
	{
		m.transaction_id.clear();
		std::back_insert_iterator<std::string> out(m.transaction_id);
		io::write_uint16(m_next_transaction_id, out);

		o->send(m);

		o->sent = boost::posix_time::microsec_clock::universal_time();
		o->target_addr = target_addr;

	#ifdef TORRENT_DHT_VERBOSE_LOGGING
		TORRENT_LOG(rpc) << "Invoking " << messages::ids[message_id]
			<< " -> " << target_addr;
	#endif
		m_send(m);
		new_transaction_id(o);
	}
	catch (std::exception& e)
	{
#ifndef NDEBUG
		std::cerr << e.what() << "\n";
#endif
		assert(false);
	}
}

void rpc_manager::reply(msg& m, msg const& reply_to)
{
	INVARIANT_CHECK;

	if (m_destructing) return;

	if (m.message_id != messages::error)
		m.message_id = reply_to.message_id;
	m.addr = reply_to.addr;
	m.reply = true;
	m.piggy_backed_ping = false;
	m.id = m_our_id;
	m.transaction_id = reply_to.transaction_id;

	m_send(m);
}

namespace
{
	struct dummy_observer : observer
	{
		virtual void reply(msg const&) {}
		virtual void timeout() {}
		virtual void send(msg&) {}
		void abort() {}
	};
}

void rpc_manager::reply_with_ping(msg& m, msg const& reply_to)
{
	INVARIANT_CHECK;

	if (m_destructing) return;

	if (m.message_id != messages::error)
		m.message_id = reply_to.message_id;
	m.addr = reply_to.addr;
	m.reply = true;
	m.piggy_backed_ping = true;
	m.id = m_our_id;
	m.transaction_id = reply_to.transaction_id;

	try
	{
		m.ping_transaction_id.clear();
		std::back_insert_iterator<std::string> out(m.ping_transaction_id);
		io::write_uint16(m_next_transaction_id, out);

		boost::shared_ptr<observer> o(new dummy_observer);
		assert(!m_transactions[m_next_transaction_id]);
		o->sent = boost::posix_time::microsec_clock::universal_time();
		o->target_addr = m.addr;

		m_send(m);
		new_transaction_id(o);
	}
	catch (std::exception& e)
	{
#ifndef NDEBUG
		std::cerr << e.what() << "\n";
#endif
		assert(false);
	}
}

} } // namespace libtorrent::dht

<commit_msg>removed unnecessary assert(false) from rpc_manager<commit_after>/*

Copyright (c) 2006, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/date_time/posix_time/ptime.hpp>
#include <boost/bind.hpp>

#include <libtorrent/io.hpp>
#include <libtorrent/invariant_check.hpp>
#include <libtorrent/kademlia/rpc_manager.hpp>
#include <libtorrent/kademlia/logging.hpp>
#include <libtorrent/kademlia/routing_table.hpp>
#include <libtorrent/hasher.hpp>

#include <fstream>

using boost::posix_time::ptime;
using boost::posix_time::time_duration;
using boost::posix_time::microsec_clock;
using boost::posix_time::seconds;
using boost::posix_time::milliseconds;
using boost::shared_ptr;
using boost::bind;

namespace libtorrent { namespace dht
{

namespace io = libtorrent::detail;

#ifdef TORRENT_DHT_VERBOSE_LOGGING
TORRENT_DEFINE_LOG(rpc)
#endif

node_id generate_id();

rpc_manager::rpc_manager(fun const& f, node_id const& our_id
	, routing_table& table, send_fun const& sf)
	: m_next_transaction_id(rand() % max_transactions)
	, m_oldest_transaction_id(m_next_transaction_id)
	, m_incoming(f)
	, m_send(sf)
	, m_our_id(our_id)
	, m_table(table)
	, m_timer(boost::posix_time::microsec_clock::universal_time())
	, m_random_number(generate_id())
	, m_destructing(false)
{
	std::srand(time(0));
}

rpc_manager::~rpc_manager()
{
	m_destructing = true;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
	TORRENT_LOG(rpc) << "Destructing";
#endif
	std::for_each(m_aborted_transactions.begin(), m_aborted_transactions.end()
		, bind(&observer::abort, _1));

	for (transactions_t::iterator i = m_transactions.begin()
		, end(m_transactions.end()); i != end; ++i)
	{
		if (*i) (*i)->abort();
	}
}

#ifndef NDEBUG
void rpc_manager::check_invariant() const
{
	assert(m_oldest_transaction_id >= 0);
	assert(m_oldest_transaction_id < max_transactions);
	assert(m_next_transaction_id >= 0);
	assert(m_next_transaction_id < max_transactions);
	assert(!m_transactions[m_next_transaction_id]);

	for (int i = (m_next_transaction_id + 1) % max_transactions;
		i != m_oldest_transaction_id; i = (i + 1) % max_transactions)
	{
		assert(!m_transactions[i]);
	}
}
#endif

bool rpc_manager::incoming(msg const& m)
{
	INVARIANT_CHECK;

	if (m_destructing) return false;

	if (m.reply)
	{
		// if we don't have the transaction id in our
		// request list, ignore the packet

		if (m.transaction_id.size() != 2)
		{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
			TORRENT_LOG(rpc) << "Reply with invalid transaction id size: "
				<< m.transaction_id.size() << " from " << m.addr;
#endif
			return false;
		}

		std::string::const_iterator i = m.transaction_id.begin();
		int tid = io::read_uint16(i);

		if (tid >= (int)m_transactions.size()
			|| tid < 0)
		{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
			TORRENT_LOG(rpc) << "Reply with unknown transaction id: "
				<< tid << " from " << m.addr;
#endif
			return false;
		}

		boost::shared_ptr<observer> o = m_transactions[tid];

		if (!o)
		{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
			TORRENT_LOG(rpc) << "Reply with unknown transaction id: "
				<< tid << " from " << m.addr;
#endif
			return false;
		}

		if (m.addr != o->target_addr)
		{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
			TORRENT_LOG(rpc) << "Reply with incorrect address and valid transaction id: "
				<< tid << " from " << m.addr;
#endif
			return false;
		}

#ifdef TORRENT_DHT_VERBOSE_LOGGING
		std::ofstream reply_stats("libtorrent_logs/round_trip_ms.log", std::ios::app);
		reply_stats << m.addr << "\t" << (microsec_clock::universal_time()
			- o->sent).total_milliseconds() << std::endl;
#endif
		o->reply(m);
		m_transactions[tid].reset();

		if (m.piggy_backed_ping)
		{
			// there is a ping request piggy
			// backed in this reply
			msg ph;
			ph.message_id = messages::ping;
			ph.transaction_id = m.ping_transaction_id;
			ph.id = m_our_id;
			ph.addr = m.addr;

			msg empty;

			reply(empty, ph);
		}
		return m_table.node_seen(m.id, m.addr);
	}
	else
	{
		// this is an incoming request
		m_incoming(m);
	}
	return false;
}

time_duration rpc_manager::tick()
{
	INVARIANT_CHECK;

	using boost::posix_time::microsec_clock;

	const int timeout_ms = 10 * 1000;

	//	look for observers that has timed out

	if (m_next_transaction_id == m_oldest_transaction_id) return milliseconds(timeout_ms);

	std::vector<shared_ptr<observer> > timeouts;

	for (;m_next_transaction_id != m_oldest_transaction_id;
		m_oldest_transaction_id = (m_oldest_transaction_id + 1) % max_transactions)
	{
		assert(m_oldest_transaction_id >= 0);
		assert(m_oldest_transaction_id < max_transactions);

		boost::shared_ptr<observer> o = m_transactions[m_oldest_transaction_id];
		if (!o) continue;

		time_duration diff = o->sent + milliseconds(timeout_ms)
			- microsec_clock::universal_time();
		if (diff > seconds(0))
		{
			if (diff < seconds(1)) return seconds(1);
			return diff;
		}

		try
		{
			m_transactions[m_oldest_transaction_id].reset();
			timeouts.push_back(o);
		} catch (std::exception) {}
	}

	std::for_each(timeouts.begin(), timeouts.end(), bind(&observer::timeout, _1));
	timeouts.clear();

	// clear the aborted transactions, will likely
	// generate new requests. We need to swap, since the
	// destrutors may add more observers to the m_aborted_transactions
	std::vector<shared_ptr<observer> >().swap(m_aborted_transactions);
	return milliseconds(timeout_ms);
}

unsigned int rpc_manager::new_transaction_id(shared_ptr<observer> o)
{
	INVARIANT_CHECK;

	unsigned int tid = m_next_transaction_id;
	m_next_transaction_id = (m_next_transaction_id + 1) % max_transactions;
	if (m_transactions[m_next_transaction_id])
	{
		// moving the observer into the set of aborted transactions
		// it will prevent it from spawning new requests right now,
		// since that would break the invariant
		m_aborted_transactions.push_back(m_transactions[m_next_transaction_id]);
		m_transactions[m_next_transaction_id].reset();
		assert(m_oldest_transaction_id == m_next_transaction_id);
	}
	assert(!m_transactions[tid]);
	m_transactions[tid] = o;
	if (m_oldest_transaction_id == m_next_transaction_id)
	{
		m_oldest_transaction_id = (m_oldest_transaction_id + 1) % max_transactions;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
		TORRENT_LOG(rpc) << "WARNING: transaction limit reached! Too many concurrent"
			" messages! limit: " << (int)max_transactions;
#endif
		update_oldest_transaction_id();
	}

	return tid;
}

void rpc_manager::update_oldest_transaction_id()
{
	INVARIANT_CHECK;

	assert(m_oldest_transaction_id != m_next_transaction_id);
	while (!m_transactions[m_oldest_transaction_id])
	{
		m_oldest_transaction_id = (m_oldest_transaction_id + 1)
			% max_transactions;
		if (m_oldest_transaction_id == m_next_transaction_id)
			break;
	}
}

void rpc_manager::invoke(int message_id, udp::endpoint target_addr
	, shared_ptr<observer> o)
{
	INVARIANT_CHECK;

	if (m_destructing)
	{
		o->abort();
		return;
	}

	msg m;
	m.message_id = message_id;
	m.reply = false;
	m.id = m_our_id;
	m.addr = target_addr;
	assert(!m_transactions[m_next_transaction_id]);
	try
	{
		m.transaction_id.clear();
		std::back_insert_iterator<std::string> out(m.transaction_id);
		io::write_uint16(m_next_transaction_id, out);

		o->send(m);

		o->sent = boost::posix_time::microsec_clock::universal_time();
		o->target_addr = target_addr;

	#ifdef TORRENT_DHT_VERBOSE_LOGGING
		TORRENT_LOG(rpc) << "Invoking " << messages::ids[message_id]
			<< " -> " << target_addr;
	#endif
		m_send(m);
		new_transaction_id(o);
	}
	catch (std::exception& e)
	{
		// m_send may fail with "no route to host"
	}
}

void rpc_manager::reply(msg& m, msg const& reply_to)
{
	INVARIANT_CHECK;

	if (m_destructing) return;

	if (m.message_id != messages::error)
		m.message_id = reply_to.message_id;
	m.addr = reply_to.addr;
	m.reply = true;
	m.piggy_backed_ping = false;
	m.id = m_our_id;
	m.transaction_id = reply_to.transaction_id;

	m_send(m);
}

namespace
{
	struct dummy_observer : observer
	{
		virtual void reply(msg const&) {}
		virtual void timeout() {}
		virtual void send(msg&) {}
		void abort() {}
	};
}

void rpc_manager::reply_with_ping(msg& m, msg const& reply_to)
{
	INVARIANT_CHECK;

	if (m_destructing) return;

	if (m.message_id != messages::error)
		m.message_id = reply_to.message_id;
	m.addr = reply_to.addr;
	m.reply = true;
	m.piggy_backed_ping = true;
	m.id = m_our_id;
	m.transaction_id = reply_to.transaction_id;

	try
	{
		m.ping_transaction_id.clear();
		std::back_insert_iterator<std::string> out(m.ping_transaction_id);
		io::write_uint16(m_next_transaction_id, out);

		boost::shared_ptr<observer> o(new dummy_observer);
		assert(!m_transactions[m_next_transaction_id]);
		o->sent = boost::posix_time::microsec_clock::universal_time();
		o->target_addr = m.addr;

		m_send(m);
		new_transaction_id(o);
	}
	catch (std::exception& e)
	{
		// m_send may fail with "no route to host"
	}
}

} } // namespace libtorrent::dht

<|endoftext|>


<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/command_line.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "chrome/browser/automation/ui_controls.h"
#include "chrome/browser/chromeos/cros/cros_in_process_browser_test.h"
#include "chrome/browser/chromeos/cros/mock_input_method_library.h"
#include "chrome/browser/chromeos/cros/mock_screen_lock_library.h"
#include "chrome/browser/chromeos/login/mock_authenticator.h"
#include "chrome/browser/chromeos/login/screen_locker.h"
#include "chrome/browser/chromeos/login/screen_locker_tester.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/views/browser_dialogs.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/ui_test_utils.h"
#include "content/common/notification_service.h"
#include "content/common/notification_type.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "views/controls/textfield/textfield.h"
#include "views/window/window_gtk.h"

namespace {

// An object that wait for lock state and fullscreen state.
class Waiter : public NotificationObserver {
 public:
  explicit Waiter(Browser* browser)
      : browser_(browser),
        running_(false) {
    registrar_.Add(this,
                   NotificationType::SCREEN_LOCK_STATE_CHANGED,
                   NotificationService::AllSources());
    handler_id_ = g_signal_connect(
        G_OBJECT(browser_->window()->GetNativeHandle()),
        "window-state-event",
        G_CALLBACK(OnWindowStateEventThunk),
        this);
  }

  ~Waiter() {
    g_signal_handler_disconnect(
        G_OBJECT(browser_->window()->GetNativeHandle()),
        handler_id_);
  }

  virtual void Observe(NotificationType type,
                       const NotificationSource& source,
                       const NotificationDetails& details) {
    DCHECK(type == NotificationType::SCREEN_LOCK_STATE_CHANGED);
    if (running_)
      MessageLoop::current()->Quit();
  }

  // Wait until the two conditions are met.
  void Wait(bool locker_state, bool fullscreen) {
    running_ = true;
    scoped_ptr<chromeos::test::ScreenLockerTester>
        tester(chromeos::ScreenLocker::GetTester());
    while (tester->IsLocked() != locker_state ||
           browser_->window()->IsFullscreen() != fullscreen) {
      ui_test_utils::RunMessageLoop();
    }
    // Make sure all pending tasks are executed.
    ui_test_utils::RunAllPendingInMessageLoop();
    running_ = false;
  }

  CHROMEGTK_CALLBACK_1(Waiter, gboolean, OnWindowStateEvent,
                       GdkEventWindowState*);

 private:
  Browser* browser_;
  gulong handler_id_;
  NotificationRegistrar registrar_;

  // Are we currently running the message loop?
  bool running_;

  DISALLOW_COPY_AND_ASSIGN(Waiter);
};

gboolean Waiter::OnWindowStateEvent(GtkWidget* widget,
                                    GdkEventWindowState* event) {
  MessageLoop::current()->Quit();
  return false;
}

}  // namespace

namespace chromeos {

class ScreenLockerTest : public CrosInProcessBrowserTest {
 public:
  ScreenLockerTest() : mock_screen_lock_library_(NULL),
                       mock_input_method_library_(NULL) {
  }

 protected:
  MockScreenLockLibrary *mock_screen_lock_library_;
  MockInputMethodLibrary *mock_input_method_library_;

  // Test the no password mode with different unlock scheme given by
  // |unlock| function.
  void TestNoPassword(void (unlock)(views::Widget*)) {
    EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockRequested())
        .Times(1)
        .RetiresOnSaturation();
    EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
        .Times(1)
        .RetiresOnSaturation();
    UserManager::Get()->OffTheRecordUserLoggedIn();
    ScreenLocker::Show();
    scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
    tester->EmulateWindowManagerReady();
    if (!chromeos::ScreenLocker::GetTester()->IsLocked())
      ui_test_utils::WaitForNotification(
          NotificationType::SCREEN_LOCK_STATE_CHANGED);
    EXPECT_TRUE(tester->IsLocked());
    tester->InjectMockAuthenticator("", "");

    unlock(tester->GetWidget());

    ui_test_utils::RunAllPendingInMessageLoop();
    EXPECT_TRUE(tester->IsLocked());

    // Emulate LockScreen request from PowerManager (via SessionManager).
    ScreenLocker::Hide();
    ui_test_utils::RunAllPendingInMessageLoop();
    EXPECT_FALSE(tester->IsLocked());
  }

  void LockScreenWithUser(test::ScreenLockerTester* tester,
                          const std::string& user) {
    UserManager::Get()->UserLoggedIn(user);
    ScreenLocker::Show();
    tester->EmulateWindowManagerReady();
    if (!tester->IsLocked()) {
      ui_test_utils::WaitForNotification(
          NotificationType::SCREEN_LOCK_STATE_CHANGED);
    }
    EXPECT_TRUE(tester->IsLocked());
  }

 private:
  virtual void SetUpInProcessBrowserTestFixture() {
    cros_mock_->InitStatusAreaMocks();
    cros_mock_->InitMockScreenLockLibrary();
    mock_screen_lock_library_ = cros_mock_->mock_screen_lock_library();
    mock_input_method_library_ = cros_mock_->mock_input_method_library();
    EXPECT_CALL(*mock_screen_lock_library_, AddObserver(testing::_))
        .Times(1)
        .RetiresOnSaturation();
    EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockCompleted())
        .Times(1)
        .RetiresOnSaturation();
    // Expectations for the status are on the screen lock window.
    cros_mock_->SetStatusAreaMocksExpectations();
    // Expectations for the status area on the browser window.
    cros_mock_->SetStatusAreaMocksExpectations();
  }

  virtual void SetUpCommandLine(CommandLine* command_line) {
    command_line->AppendSwitchASCII(switches::kLoginProfile, "user");
    command_line->AppendSwitch(switches::kNoFirstRun);
  }

  DISALLOW_COPY_AND_ASSIGN(ScreenLockerTest);
};

IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestBasic) {
  EXPECT_CALL(*mock_input_method_library_, GetNumActiveInputMethods())
      .Times(1)
      .WillRepeatedly((testing::Return(0)))
      .RetiresOnSaturation();
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockRequested())
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
      .Times(1)
      .RetiresOnSaturation();
  UserManager::Get()->UserLoggedIn("user");
  ScreenLocker::Show();
  scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
  tester->EmulateWindowManagerReady();
  if (!chromeos::ScreenLocker::GetTester()->IsLocked())
    ui_test_utils::WaitForNotification(
        NotificationType::SCREEN_LOCK_STATE_CHANGED);

  // Test to make sure that the widget is actually appearing and is of
  // reasonable size, preventing a regression of
  // http://code.google.com/p/chromium-os/issues/detail?id=5987
  gfx::Rect lock_bounds = tester->GetChildWidget()->GetWindowScreenBounds();
  EXPECT_GT(lock_bounds.width(), 10);
  EXPECT_GT(lock_bounds.height(), 10);

  tester->InjectMockAuthenticator("user", "pass");
  EXPECT_TRUE(tester->IsLocked());
  tester->EnterPassword("fail");
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_TRUE(tester->IsLocked());
  tester->EnterPassword("pass");
  ui_test_utils::RunAllPendingInMessageLoop();
  // Successful authentication simply send a unlock request to PowerManager.
  EXPECT_TRUE(tester->IsLocked());

  // Emulate LockScreen request from PowerManager (via SessionManager).
  // TODO(oshima): Find out better way to handle this in mock.
  ScreenLocker::Hide();
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_FALSE(tester->IsLocked());
}

IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestFullscreenExit) {
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockRequested())
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
      .Times(1)
      .RetiresOnSaturation();
  scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
  {
    Waiter waiter(browser());
    browser()->ToggleFullscreenMode();
    waiter.Wait(false /* not locked */, true /* full screen */);
    EXPECT_TRUE(browser()->window()->IsFullscreen());
    EXPECT_FALSE(tester->IsLocked());
  }
  {
    Waiter waiter(browser());
    UserManager::Get()->UserLoggedIn("user");
    ScreenLocker::Show();
    tester->EmulateWindowManagerReady();
    waiter.Wait(true /* locked */, false /* full screen */);
    EXPECT_FALSE(browser()->window()->IsFullscreen());
    EXPECT_TRUE(tester->IsLocked());
  }
  tester->InjectMockAuthenticator("user", "pass");
  tester->EnterPassword("pass");
  ui_test_utils::RunAllPendingInMessageLoop();
  ScreenLocker::Hide();
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_FALSE(tester->IsLocked());
}

void MouseMove(views::Widget* widget) {
  ui_controls::SendMouseMove(10, 10);
}

IN_PROC_BROWSER_TEST_F(ScreenLockerTest, DISABLED_TestNoPasswordWithMouseMove) {
  TestNoPassword(MouseMove);
}

void MouseClick(views::Widget* widget) {
  ui_controls::SendMouseClick(ui_controls::RIGHT);
}

IN_PROC_BROWSER_TEST_F(ScreenLockerTest,
    DISABLED_TestNoPasswordWithMouseClick) {
  TestNoPassword(MouseClick);
}

void KeyPress(views::Widget* widget) {
  ui_controls::SendKeyPress(GTK_WINDOW(widget->GetNativeView()),
                            ui::VKEY_SPACE, false, false, false, false);
}

IN_PROC_BROWSER_TEST_F(ScreenLockerTest, DISABLED_TestNoPasswordWithKeyPress) {
  TestNoPassword(KeyPress);
}

// See http://crbug.com/78764.
IN_PROC_BROWSER_TEST_F(ScreenLockerTest, DISABLED_TestShowTwice) {
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
      .Times(2)
      .RetiresOnSaturation();
  scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
  LockScreenWithUser(tester.get(), "user");

  // Ensure there's a profile or this test crashes.
  ProfileManager::GetDefaultProfile();

  // Calling Show again simply send LockCompleted signal.
  ScreenLocker::Show();
  EXPECT_TRUE(tester->IsLocked());

  // Close the locker to match expectations.
  ScreenLocker::Hide();
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_FALSE(tester->IsLocked());
}

// See http://crbug.com/78764.
IN_PROC_BROWSER_TEST_F(ScreenLockerTest, DISABLED_TestEscape) {
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
      .Times(1)
      .RetiresOnSaturation();
  scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
  LockScreenWithUser(tester.get(), "user");

  // Ensure there's a profile or this test crashes.
  ProfileManager::GetDefaultProfile();

  tester->SetPassword("password");
  EXPECT_EQ("password", tester->GetPassword());
  // Escape clears the password.
  ui_controls::SendKeyPress(GTK_WINDOW(tester->GetWidget()->GetNativeView()),
                            ui::VKEY_ESCAPE, false, false, false, false);
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_EQ("", tester->GetPassword());

  // Close the locker to match expectations.
  ScreenLocker::Hide();
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_FALSE(tester->IsLocked());
}

}  // namespace chromeos
<commit_msg>Add documentation for why tests are disabled per  http://www.chromium.org/developers/tree-sheriffs/sheriff-details-chromium/handling-a-failing-test<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/command_line.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "chrome/browser/automation/ui_controls.h"
#include "chrome/browser/chromeos/cros/cros_in_process_browser_test.h"
#include "chrome/browser/chromeos/cros/mock_input_method_library.h"
#include "chrome/browser/chromeos/cros/mock_screen_lock_library.h"
#include "chrome/browser/chromeos/login/mock_authenticator.h"
#include "chrome/browser/chromeos/login/screen_locker.h"
#include "chrome/browser/chromeos/login/screen_locker_tester.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/views/browser_dialogs.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/ui_test_utils.h"
#include "content/common/notification_service.h"
#include "content/common/notification_type.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "views/controls/textfield/textfield.h"
#include "views/window/window_gtk.h"

namespace {

// An object that wait for lock state and fullscreen state.
class Waiter : public NotificationObserver {
 public:
  explicit Waiter(Browser* browser)
      : browser_(browser),
        running_(false) {
    registrar_.Add(this,
                   NotificationType::SCREEN_LOCK_STATE_CHANGED,
                   NotificationService::AllSources());
    handler_id_ = g_signal_connect(
        G_OBJECT(browser_->window()->GetNativeHandle()),
        "window-state-event",
        G_CALLBACK(OnWindowStateEventThunk),
        this);
  }

  ~Waiter() {
    g_signal_handler_disconnect(
        G_OBJECT(browser_->window()->GetNativeHandle()),
        handler_id_);
  }

  virtual void Observe(NotificationType type,
                       const NotificationSource& source,
                       const NotificationDetails& details) {
    DCHECK(type == NotificationType::SCREEN_LOCK_STATE_CHANGED);
    if (running_)
      MessageLoop::current()->Quit();
  }

  // Wait until the two conditions are met.
  void Wait(bool locker_state, bool fullscreen) {
    running_ = true;
    scoped_ptr<chromeos::test::ScreenLockerTester>
        tester(chromeos::ScreenLocker::GetTester());
    while (tester->IsLocked() != locker_state ||
           browser_->window()->IsFullscreen() != fullscreen) {
      ui_test_utils::RunMessageLoop();
    }
    // Make sure all pending tasks are executed.
    ui_test_utils::RunAllPendingInMessageLoop();
    running_ = false;
  }

  CHROMEGTK_CALLBACK_1(Waiter, gboolean, OnWindowStateEvent,
                       GdkEventWindowState*);

 private:
  Browser* browser_;
  gulong handler_id_;
  NotificationRegistrar registrar_;

  // Are we currently running the message loop?
  bool running_;

  DISALLOW_COPY_AND_ASSIGN(Waiter);
};

gboolean Waiter::OnWindowStateEvent(GtkWidget* widget,
                                    GdkEventWindowState* event) {
  MessageLoop::current()->Quit();
  return false;
}

}  // namespace

namespace chromeos {

class ScreenLockerTest : public CrosInProcessBrowserTest {
 public:
  ScreenLockerTest() : mock_screen_lock_library_(NULL),
                       mock_input_method_library_(NULL) {
  }

 protected:
  MockScreenLockLibrary *mock_screen_lock_library_;
  MockInputMethodLibrary *mock_input_method_library_;

  // Test the no password mode with different unlock scheme given by
  // |unlock| function.
  void TestNoPassword(void (unlock)(views::Widget*)) {
    EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockRequested())
        .Times(1)
        .RetiresOnSaturation();
    EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
        .Times(1)
        .RetiresOnSaturation();
    UserManager::Get()->OffTheRecordUserLoggedIn();
    ScreenLocker::Show();
    scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
    tester->EmulateWindowManagerReady();
    if (!chromeos::ScreenLocker::GetTester()->IsLocked())
      ui_test_utils::WaitForNotification(
          NotificationType::SCREEN_LOCK_STATE_CHANGED);
    EXPECT_TRUE(tester->IsLocked());
    tester->InjectMockAuthenticator("", "");

    unlock(tester->GetWidget());

    ui_test_utils::RunAllPendingInMessageLoop();
    EXPECT_TRUE(tester->IsLocked());

    // Emulate LockScreen request from PowerManager (via SessionManager).
    ScreenLocker::Hide();
    ui_test_utils::RunAllPendingInMessageLoop();
    EXPECT_FALSE(tester->IsLocked());
  }

  void LockScreenWithUser(test::ScreenLockerTester* tester,
                          const std::string& user) {
    UserManager::Get()->UserLoggedIn(user);
    ScreenLocker::Show();
    tester->EmulateWindowManagerReady();
    if (!tester->IsLocked()) {
      ui_test_utils::WaitForNotification(
          NotificationType::SCREEN_LOCK_STATE_CHANGED);
    }
    EXPECT_TRUE(tester->IsLocked());
  }

 private:
  virtual void SetUpInProcessBrowserTestFixture() {
    cros_mock_->InitStatusAreaMocks();
    cros_mock_->InitMockScreenLockLibrary();
    mock_screen_lock_library_ = cros_mock_->mock_screen_lock_library();
    mock_input_method_library_ = cros_mock_->mock_input_method_library();
    EXPECT_CALL(*mock_screen_lock_library_, AddObserver(testing::_))
        .Times(1)
        .RetiresOnSaturation();
    EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockCompleted())
        .Times(1)
        .RetiresOnSaturation();
    // Expectations for the status are on the screen lock window.
    cros_mock_->SetStatusAreaMocksExpectations();
    // Expectations for the status area on the browser window.
    cros_mock_->SetStatusAreaMocksExpectations();
  }

  virtual void SetUpCommandLine(CommandLine* command_line) {
    command_line->AppendSwitchASCII(switches::kLoginProfile, "user");
    command_line->AppendSwitch(switches::kNoFirstRun);
  }

  DISALLOW_COPY_AND_ASSIGN(ScreenLockerTest);
};

IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestBasic) {
  EXPECT_CALL(*mock_input_method_library_, GetNumActiveInputMethods())
      .Times(1)
      .WillRepeatedly((testing::Return(0)))
      .RetiresOnSaturation();
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockRequested())
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
      .Times(1)
      .RetiresOnSaturation();
  UserManager::Get()->UserLoggedIn("user");
  ScreenLocker::Show();
  scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
  tester->EmulateWindowManagerReady();
  if (!chromeos::ScreenLocker::GetTester()->IsLocked())
    ui_test_utils::WaitForNotification(
        NotificationType::SCREEN_LOCK_STATE_CHANGED);

  // Test to make sure that the widget is actually appearing and is of
  // reasonable size, preventing a regression of
  // http://code.google.com/p/chromium-os/issues/detail?id=5987
  gfx::Rect lock_bounds = tester->GetChildWidget()->GetWindowScreenBounds();
  EXPECT_GT(lock_bounds.width(), 10);
  EXPECT_GT(lock_bounds.height(), 10);

  tester->InjectMockAuthenticator("user", "pass");
  EXPECT_TRUE(tester->IsLocked());
  tester->EnterPassword("fail");
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_TRUE(tester->IsLocked());
  tester->EnterPassword("pass");
  ui_test_utils::RunAllPendingInMessageLoop();
  // Successful authentication simply send a unlock request to PowerManager.
  EXPECT_TRUE(tester->IsLocked());

  // Emulate LockScreen request from PowerManager (via SessionManager).
  // TODO(oshima): Find out better way to handle this in mock.
  ScreenLocker::Hide();
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_FALSE(tester->IsLocked());
}

IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestFullscreenExit) {
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenUnlockRequested())
      .Times(1)
      .RetiresOnSaturation();
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
      .Times(1)
      .RetiresOnSaturation();
  scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
  {
    Waiter waiter(browser());
    browser()->ToggleFullscreenMode();
    waiter.Wait(false /* not locked */, true /* full screen */);
    EXPECT_TRUE(browser()->window()->IsFullscreen());
    EXPECT_FALSE(tester->IsLocked());
  }
  {
    Waiter waiter(browser());
    UserManager::Get()->UserLoggedIn("user");
    ScreenLocker::Show();
    tester->EmulateWindowManagerReady();
    waiter.Wait(true /* locked */, false /* full screen */);
    EXPECT_FALSE(browser()->window()->IsFullscreen());
    EXPECT_TRUE(tester->IsLocked());
  }
  tester->InjectMockAuthenticator("user", "pass");
  tester->EnterPassword("pass");
  ui_test_utils::RunAllPendingInMessageLoop();
  ScreenLocker::Hide();
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_FALSE(tester->IsLocked());
}

void MouseMove(views::Widget* widget) {
  ui_controls::SendMouseMove(10, 10);
}

// Crashes on chromeos.  http://crbug.com/79164
IN_PROC_BROWSER_TEST_F(ScreenLockerTest, DISABLED_TestNoPasswordWithMouseMove) {
  TestNoPassword(MouseMove);
}

void MouseClick(views::Widget* widget) {
  ui_controls::SendMouseClick(ui_controls::RIGHT);
}

// Crashes on chromeos.  http://crbug.com/79164
IN_PROC_BROWSER_TEST_F(ScreenLockerTest,
    DISABLED_TestNoPasswordWithMouseClick) {
  TestNoPassword(MouseClick);
}

void KeyPress(views::Widget* widget) {
  ui_controls::SendKeyPress(GTK_WINDOW(widget->GetNativeView()),
                            ui::VKEY_SPACE, false, false, false, false);
}

// Crashes on chromeos.  http://crbug.com/79164
IN_PROC_BROWSER_TEST_F(ScreenLockerTest, DISABLED_TestNoPasswordWithKeyPress) {
  TestNoPassword(KeyPress);
}

// See http://crbug.com/78764.
IN_PROC_BROWSER_TEST_F(ScreenLockerTest, DISABLED_TestShowTwice) {
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
      .Times(2)
      .RetiresOnSaturation();
  scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
  LockScreenWithUser(tester.get(), "user");

  // Ensure there's a profile or this test crashes.
  ProfileManager::GetDefaultProfile();

  // Calling Show again simply send LockCompleted signal.
  ScreenLocker::Show();
  EXPECT_TRUE(tester->IsLocked());

  // Close the locker to match expectations.
  ScreenLocker::Hide();
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_FALSE(tester->IsLocked());
}

// See http://crbug.com/78764.
IN_PROC_BROWSER_TEST_F(ScreenLockerTest, DISABLED_TestEscape) {
  EXPECT_CALL(*mock_screen_lock_library_, NotifyScreenLockCompleted())
      .Times(1)
      .RetiresOnSaturation();
  scoped_ptr<test::ScreenLockerTester> tester(ScreenLocker::GetTester());
  LockScreenWithUser(tester.get(), "user");

  // Ensure there's a profile or this test crashes.
  ProfileManager::GetDefaultProfile();

  tester->SetPassword("password");
  EXPECT_EQ("password", tester->GetPassword());
  // Escape clears the password.
  ui_controls::SendKeyPress(GTK_WINDOW(tester->GetWidget()->GetNativeView()),
                            ui::VKEY_ESCAPE, false, false, false, false);
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_EQ("", tester->GetPassword());

  // Close the locker to match expectations.
  ScreenLocker::Hide();
  ui_test_utils::RunAllPendingInMessageLoop();
  EXPECT_FALSE(tester->IsLocked());
}

}  // namespace chromeos
<|endoftext|>


<commit_before>/** \brief Utility for removing unreferences authority records
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2015-2017 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <stdexcept>
#include <unordered_set>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include "FileUtil.h"
#include "Leader.h"
#include "MarcReader.h"
#include "MarcRecord.h"
#include "MarcWriter.h"
#include "RegexMatcher.h"
#include "StringUtil.h"
#include "util.h"

namespace {

void Usage() __attribute__((noreturn));

void Usage() {
    std::cerr << "Usage: " << ::progname << " title_data authority_data filtered_authority_data\n";
    std::exit(EXIT_FAILURE);
}

void CollectGNDReferences(MarcReader * const marc_reader, std::unordered_set<std::string> * const gnd_numbers) {
    std::string err_msg;
    RegexMatcher * const matcher(RegexMatcher::RegexMatcherFactory("\x1F""0\\(DE-588\\)([^\x1F]+).*\x1F""2gnd",
                                                                   &err_msg));
    if (matcher == nullptr)
        logger->error("failed to compile a regex in CollectGNDReferences: " + err_msg);

    unsigned record_count(0);
    while (const MarcRecord record = marc_reader->read()) {
        ++record_count;

        for (const auto field_contents : record) {
            if (matcher->matched(field_contents))
                gnd_numbers->emplace((*matcher)[1]);
        }
    }

    std::cout << "Extracted " << gnd_numbers->size() << " GND number(s) from " <<  record_count
              << " title record(s).\n";
}

std::string GetGNDNumber(const MarcRecord &record) {
    std::vector<size_t> _035_indices;
    record.getFieldIndices("035", &_035_indices);

    for (const auto index : _035_indices) {
        const Subfields _035_subfields(record.getFieldData(index));
        const std::string _035a_contents(_035_subfields.getFirstSubfieldValue('a'));
        if (StringUtil::StartsWith(_035a_contents, "(DE-588)"))
            return _035a_contents.substr(__builtin_strlen("(DE-588)"));
    }

    return "";
}

const std::string DROPPED_GND_LIST_FILE("/usr/local/var/log/tuelib/dropped_gnd_numbers.list");

void FilterAuthorityData(MarcReader * const marc_reader, MarcWriter * const marc_writer,
                         const std::unordered_set<std::string> &gnd_numbers)
{
    std::unique_ptr<File> gnd_list_file(FileUtil::OpenOutputFileOrDie(DROPPED_GND_LIST_FILE));
    unsigned record_count(0), dropped_count(0);
    while (const MarcRecord record = marc_reader->read()) {
        ++record_count;

        const std::string gnd_number(GetGNDNumber(record));
        if (gnd_number.empty())
            std::cerr << "Did not find a GND number for authirity record witnb PPN " << record.getControlNumber()
                      << ".\n";
        else if (gnd_numbers.find(gnd_number) != gnd_numbers.cend()) {
            gnd_list_file->write(gnd_number + "\n");
            ++dropped_count;
        } else
            marc_writer->write(record);
    }

    std::cerr << "Read " << record_count << " authority record(s) of which " << dropped_count << " were dropped.\n";
}

} // unnamed namespace

int main(int argc, char *argv[]) {
    ::progname = argv[0];

    if (argc != 4)
        Usage();

    try {
        std::unique_ptr<MarcReader> marc_title_reader(MarcReader::Factory(argv[1]));
        std::unique_ptr<MarcReader> marc_authority_reader(MarcReader::Factory(argv[2]));
        std::unique_ptr<MarcWriter> marc_authority_writer(MarcWriter::Factory(argv[3]));

        std::unordered_set<std::string> gnd_numbers;
        CollectGNDReferences(marc_title_reader.get(), &gnd_numbers);
        FilterAuthorityData(marc_authority_reader.get(), marc_authority_writer.get(), gnd_numbers);
    } catch (const std::exception &e) {
        logger->error("Caught exception: " + std::string(e.what()));
    }
}
<commit_msg>Update remove_unused_authority_records.cc<commit_after>/** \brief Utility for removing unreferences authority records
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2015-2017 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <stdexcept>
#include <unordered_set>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include "FileUtil.h"
#include "Leader.h"
#include "MarcReader.h"
#include "MarcRecord.h"
#include "MarcWriter.h"
#include "RegexMatcher.h"
#include "StringUtil.h"
#include "util.h"

namespace {

void Usage() __attribute__((noreturn));

void Usage() {
    std::cerr << "Usage: " << ::progname << " title_data authority_data filtered_authority_data\n";
    std::exit(EXIT_FAILURE);
}

void CollectGNDReferences(MarcReader * const marc_reader, std::unordered_set<std::string> * const gnd_numbers) {
    std::string err_msg;
    RegexMatcher * const matcher(RegexMatcher::RegexMatcherFactory("\x1F""0\\(DE-588\\)([^\x1F]+).*\x1F""2gnd",
                                                                   &err_msg));
    if (matcher == nullptr)
        logger->error("failed to compile a regex in CollectGNDReferences: " + err_msg);

    unsigned record_count(0);
    while (const MarcRecord record = marc_reader->read()) {
        ++record_count;

        for (const auto field_contents : record) {
            if (matcher->matched(field_contents))
                gnd_numbers->emplace((*matcher)[1]);
        }
    }

    std::cout << "Extracted " << gnd_numbers->size() << " GND number(s) from " <<  record_count
              << " title record(s).\n";
}

std::string GetGNDNumber(const MarcRecord &record) {
    std::vector<size_t> _035_indices;
    record.getFieldIndices("035", &_035_indices);

    for (const auto index : _035_indices) {
        const Subfields _035_subfields(record.getFieldData(index));
        const std::string _035a_contents(_035_subfields.getFirstSubfieldValue('a'));
        if (StringUtil::StartsWith(_035a_contents, "(DE-588)"))
            return _035a_contents.substr(__builtin_strlen("(DE-588)"));
    }

    return "";
}

const std::string DROPPED_GND_LIST_FILE("/usr/local/var/log/tufind/dropped_gnd_numbers.list");

void FilterAuthorityData(MarcReader * const marc_reader, MarcWriter * const marc_writer,
                         const std::unordered_set<std::string> &gnd_numbers)
{
    std::unique_ptr<File> gnd_list_file(FileUtil::OpenOutputFileOrDie(DROPPED_GND_LIST_FILE));
    unsigned record_count(0), dropped_count(0);
    while (const MarcRecord record = marc_reader->read()) {
        ++record_count;

        const std::string gnd_number(GetGNDNumber(record));
        if (gnd_number.empty())
            std::cerr << "Did not find a GND number for authirity record witnb PPN " << record.getControlNumber()
                      << ".\n";
        else if (gnd_numbers.find(gnd_number) != gnd_numbers.cend()) {
            gnd_list_file->write(gnd_number + "\n");
            ++dropped_count;
        } else
            marc_writer->write(record);
    }

    std::cerr << "Read " << record_count << " authority record(s) of which " << dropped_count << " were dropped.\n";
}

} // unnamed namespace

int main(int argc, char *argv[]) {
    ::progname = argv[0];

    if (argc != 4)
        Usage();

    try {
        std::unique_ptr<MarcReader> marc_title_reader(MarcReader::Factory(argv[1]));
        std::unique_ptr<MarcReader> marc_authority_reader(MarcReader::Factory(argv[2]));
        std::unique_ptr<MarcWriter> marc_authority_writer(MarcWriter::Factory(argv[3]));

        std::unordered_set<std::string> gnd_numbers;
        CollectGNDReferences(marc_title_reader.get(), &gnd_numbers);
        FilterAuthorityData(marc_authority_reader.get(), marc_authority_writer.get(), gnd_numbers);
    } catch (const std::exception &e) {
        logger->error("Caught exception: " + std::string(e.what()));
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/disk_cache/file.h"

#include <fcntl.h>

#include "base/logging.h"
#include "base/threading/worker_pool.h"
#include "net/base/net_errors.h"
#include "net/disk_cache/disk_cache.h"
#include "net/disk_cache/in_flight_io.h"

namespace {

// This class represents a single asynchronous IO operation while it is being
// bounced between threads.
class FileBackgroundIO : public disk_cache::BackgroundIO {
 public:
  // Other than the actual parameters for the IO operation (including the
  // |callback| that must be notified at the end), we need the controller that
  // is keeping track of all operations. When done, we notify the controller
  // (we do NOT invoke the callback), in the worker thead that completed the
  // operation.
  FileBackgroundIO(disk_cache::File* file, const void* buf, size_t buf_len,
                   size_t offset, disk_cache::FileIOCallback* callback,
                   disk_cache::InFlightIO* controller)
      : disk_cache::BackgroundIO(controller), callback_(callback), file_(file),
        buf_(buf), buf_len_(buf_len), offset_(offset) {
  }

  disk_cache::FileIOCallback* callback() {
    return callback_;
  }

  disk_cache::File* file() {
    return file_;
  }

  // Read and Write are the operations that can be performed asynchronously.
  // The actual parameters for the operation are setup in the constructor of
  // the object. Both methods should be called from a worker thread, by posting
  // a task to the WorkerPool (they are RunnableMethods). When finished,
  // controller->OnIOComplete() is called.
  void Read();
  void Write();

 private:
  ~FileBackgroundIO() {}

  disk_cache::FileIOCallback* callback_;

  disk_cache::File* file_;
  const void* buf_;
  size_t buf_len_;
  size_t offset_;

  DISALLOW_COPY_AND_ASSIGN(FileBackgroundIO);
};

// The specialized controller that keeps track of current operations.
class FileInFlightIO : public disk_cache::InFlightIO {
 public:
  FileInFlightIO() {}
  ~FileInFlightIO() {}

  // These methods start an asynchronous operation. The arguments have the same
  // semantics of the File asynchronous operations, with the exception that the
  // operation never finishes synchronously.
  void PostRead(disk_cache::File* file, void* buf, size_t buf_len,
                size_t offset, disk_cache::FileIOCallback* callback);
  void PostWrite(disk_cache::File* file, const void* buf, size_t buf_len,
                 size_t offset, disk_cache::FileIOCallback* callback);

 protected:
  // Invokes the users' completion callback at the end of the IO operation.
  // |cancel| is true if the actual task posted to the thread is still
  // queued (because we are inside WaitForPendingIO), and false if said task is
  // the one performing the call.
  virtual void OnOperationComplete(disk_cache::BackgroundIO* operation,
                                   bool cancel);

 private:
  DISALLOW_COPY_AND_ASSIGN(FileInFlightIO);
};

// ---------------------------------------------------------------------------

// Runs on a worker thread.
void FileBackgroundIO::Read() {
  if (file_->Read(const_cast<void*>(buf_), buf_len_, offset_)) {
    result_ = static_cast<int>(buf_len_);
  } else {
    result_ = net::ERR_CACHE_READ_FAILURE;
  }
  controller_->OnIOComplete(this);
}

// Runs on a worker thread.
void FileBackgroundIO::Write() {
  bool rv = file_->Write(buf_, buf_len_, offset_);

  result_ = rv ? static_cast<int>(buf_len_) : net::ERR_CACHE_WRITE_FAILURE;
  controller_->OnIOComplete(this);
}

// ---------------------------------------------------------------------------

void FileInFlightIO::PostRead(disk_cache::File *file, void* buf, size_t buf_len,
                          size_t offset, disk_cache::FileIOCallback *callback) {
  scoped_refptr<FileBackgroundIO> operation(
      new FileBackgroundIO(file, buf, buf_len, offset, callback, this));
  file->AddRef();  // Balanced on OnOperationComplete()

  base::WorkerPool::PostTask(FROM_HERE,
      NewRunnableMethod(operation.get(), &FileBackgroundIO::Read), true);
  OnOperationPosted(operation);
}

void FileInFlightIO::PostWrite(disk_cache::File* file, const void* buf,
                           size_t buf_len, size_t offset,
                           disk_cache::FileIOCallback* callback) {
  scoped_refptr<FileBackgroundIO> operation(
      new FileBackgroundIO(file, buf, buf_len, offset, callback, this));
  file->AddRef();  // Balanced on OnOperationComplete()

  base::WorkerPool::PostTask(FROM_HERE,
      NewRunnableMethod(operation.get(), &FileBackgroundIO::Write), true);
  OnOperationPosted(operation);
}

// Runs on the IO thread.
void FileInFlightIO::OnOperationComplete(disk_cache::BackgroundIO* operation,
                                         bool cancel) {
  FileBackgroundIO* op = static_cast<FileBackgroundIO*>(operation);

  disk_cache::FileIOCallback* callback = op->callback();
  int bytes = operation->result();

  // Release the references acquired in PostRead / PostWrite.
  op->file()->Release();
  callback->OnFileIOComplete(bytes);
}

// A static object tha will broker all async operations.
FileInFlightIO* s_file_operations = NULL;

// Returns the current FileInFlightIO.
FileInFlightIO* GetFileInFlightIO() {
  if (!s_file_operations) {
    s_file_operations = new FileInFlightIO;
  }
  return s_file_operations;
}

// Deletes the current FileInFlightIO.
void DeleteFileInFlightIO() {
  DCHECK(s_file_operations);
  delete s_file_operations;
  s_file_operations = NULL;
}

}  // namespace

namespace disk_cache {

File::File(base::PlatformFile file)
    : init_(true),
      mixed_(true),
      platform_file_(file),
      sync_platform_file_(base::kInvalidPlatformFileValue) {
}

bool File::Init(const FilePath& name) {
  if (init_)
    return false;

  int flags = base::PLATFORM_FILE_OPEN |
              base::PLATFORM_FILE_READ |
              base::PLATFORM_FILE_WRITE;
  platform_file_ = base::CreatePlatformFile(name, flags, NULL, NULL);
  if (platform_file_ < 0) {
    platform_file_ = 0;
    return false;
  }

  init_ = true;
  return true;
}

base::PlatformFile File::platform_file() const {
  return platform_file_;
}

bool File::IsValid() const {
  if (!init_)
    return false;
  return (base::kInvalidPlatformFileValue != platform_file_);
}

bool File::Read(void* buffer, size_t buffer_len, size_t offset) {
  DCHECK(init_);
  if (buffer_len > ULONG_MAX || offset > LONG_MAX)
    return false;

  int ret = pread(platform_file_, buffer, buffer_len, offset);
  return (static_cast<size_t>(ret) == buffer_len);
}

bool File::Write(const void* buffer, size_t buffer_len, size_t offset) {
  DCHECK(init_);
  if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
    return false;

  int ret = pwrite(platform_file_, buffer, buffer_len, offset);
  return (static_cast<size_t>(ret) == buffer_len);
}

// We have to increase the ref counter of the file before performing the IO to
// prevent the completion to happen with an invalid handle (if the file is
// closed while the IO is in flight).
bool File::Read(void* buffer, size_t buffer_len, size_t offset,
                FileIOCallback* callback, bool* completed) {
  DCHECK(init_);
  if (!callback) {
    if (completed)
      *completed = true;
    return Read(buffer, buffer_len, offset);
  }

  if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
    return false;

  GetFileInFlightIO()->PostRead(this, buffer, buffer_len, offset, callback);

  *completed = false;
  return true;
}

bool File::Write(const void* buffer, size_t buffer_len, size_t offset,
                 FileIOCallback* callback, bool* completed) {
  DCHECK(init_);
  if (!callback) {
    if (completed)
      *completed = true;
    return Write(buffer, buffer_len, offset);
  }

  return AsyncWrite(buffer, buffer_len, offset, callback, completed);
}

bool File::SetLength(size_t length) {
  DCHECK(init_);
  if (length > ULONG_MAX)
    return false;

  return 0 == ftruncate(platform_file_, length);
}

size_t File::GetLength() {
  DCHECK(init_);
  off_t ret = lseek(platform_file_, 0, SEEK_END);
  if (ret < 0)
    return 0;
  return ret;
}

// Static.
void File::WaitForPendingIO(int* num_pending_io) {
  // We may be running unit tests so we should allow be able to reset the
  // message loop.
  GetFileInFlightIO()->WaitForPendingIO();
  DeleteFileInFlightIO();
}

File::~File() {
  if (IsValid())
    close(platform_file_);
}

bool File::AsyncWrite(const void* buffer, size_t buffer_len, size_t offset,
                      FileIOCallback* callback, bool* completed) {
  DCHECK(init_);
  if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
    return false;

  GetFileInFlightIO()->PostWrite(this, buffer, buffer_len, offset, callback);

  if (completed)
    *completed = false;
  return true;
}

}  // namespace disk_cache
<commit_msg>Disk cache: Use platform_file functions for POSIX.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/disk_cache/file.h"

#include <fcntl.h>

#include "base/logging.h"
#include "base/threading/worker_pool.h"
#include "net/base/net_errors.h"
#include "net/disk_cache/disk_cache.h"
#include "net/disk_cache/in_flight_io.h"

namespace {

// This class represents a single asynchronous IO operation while it is being
// bounced between threads.
class FileBackgroundIO : public disk_cache::BackgroundIO {
 public:
  // Other than the actual parameters for the IO operation (including the
  // |callback| that must be notified at the end), we need the controller that
  // is keeping track of all operations. When done, we notify the controller
  // (we do NOT invoke the callback), in the worker thead that completed the
  // operation.
  FileBackgroundIO(disk_cache::File* file, const void* buf, size_t buf_len,
                   size_t offset, disk_cache::FileIOCallback* callback,
                   disk_cache::InFlightIO* controller)
      : disk_cache::BackgroundIO(controller), callback_(callback), file_(file),
        buf_(buf), buf_len_(buf_len), offset_(offset) {
  }

  disk_cache::FileIOCallback* callback() {
    return callback_;
  }

  disk_cache::File* file() {
    return file_;
  }

  // Read and Write are the operations that can be performed asynchronously.
  // The actual parameters for the operation are setup in the constructor of
  // the object. Both methods should be called from a worker thread, by posting
  // a task to the WorkerPool (they are RunnableMethods). When finished,
  // controller->OnIOComplete() is called.
  void Read();
  void Write();

 private:
  ~FileBackgroundIO() {}

  disk_cache::FileIOCallback* callback_;

  disk_cache::File* file_;
  const void* buf_;
  size_t buf_len_;
  size_t offset_;

  DISALLOW_COPY_AND_ASSIGN(FileBackgroundIO);
};

// The specialized controller that keeps track of current operations.
class FileInFlightIO : public disk_cache::InFlightIO {
 public:
  FileInFlightIO() {}
  ~FileInFlightIO() {}

  // These methods start an asynchronous operation. The arguments have the same
  // semantics of the File asynchronous operations, with the exception that the
  // operation never finishes synchronously.
  void PostRead(disk_cache::File* file, void* buf, size_t buf_len,
                size_t offset, disk_cache::FileIOCallback* callback);
  void PostWrite(disk_cache::File* file, const void* buf, size_t buf_len,
                 size_t offset, disk_cache::FileIOCallback* callback);

 protected:
  // Invokes the users' completion callback at the end of the IO operation.
  // |cancel| is true if the actual task posted to the thread is still
  // queued (because we are inside WaitForPendingIO), and false if said task is
  // the one performing the call.
  virtual void OnOperationComplete(disk_cache::BackgroundIO* operation,
                                   bool cancel);

 private:
  DISALLOW_COPY_AND_ASSIGN(FileInFlightIO);
};

// ---------------------------------------------------------------------------

// Runs on a worker thread.
void FileBackgroundIO::Read() {
  if (file_->Read(const_cast<void*>(buf_), buf_len_, offset_)) {
    result_ = static_cast<int>(buf_len_);
  } else {
    result_ = net::ERR_CACHE_READ_FAILURE;
  }
  controller_->OnIOComplete(this);
}

// Runs on a worker thread.
void FileBackgroundIO::Write() {
  bool rv = file_->Write(buf_, buf_len_, offset_);

  result_ = rv ? static_cast<int>(buf_len_) : net::ERR_CACHE_WRITE_FAILURE;
  controller_->OnIOComplete(this);
}

// ---------------------------------------------------------------------------

void FileInFlightIO::PostRead(disk_cache::File *file, void* buf, size_t buf_len,
                          size_t offset, disk_cache::FileIOCallback *callback) {
  scoped_refptr<FileBackgroundIO> operation(
      new FileBackgroundIO(file, buf, buf_len, offset, callback, this));
  file->AddRef();  // Balanced on OnOperationComplete()

  base::WorkerPool::PostTask(FROM_HERE,
      NewRunnableMethod(operation.get(), &FileBackgroundIO::Read), true);
  OnOperationPosted(operation);
}

void FileInFlightIO::PostWrite(disk_cache::File* file, const void* buf,
                           size_t buf_len, size_t offset,
                           disk_cache::FileIOCallback* callback) {
  scoped_refptr<FileBackgroundIO> operation(
      new FileBackgroundIO(file, buf, buf_len, offset, callback, this));
  file->AddRef();  // Balanced on OnOperationComplete()

  base::WorkerPool::PostTask(FROM_HERE,
      NewRunnableMethod(operation.get(), &FileBackgroundIO::Write), true);
  OnOperationPosted(operation);
}

// Runs on the IO thread.
void FileInFlightIO::OnOperationComplete(disk_cache::BackgroundIO* operation,
                                         bool cancel) {
  FileBackgroundIO* op = static_cast<FileBackgroundIO*>(operation);

  disk_cache::FileIOCallback* callback = op->callback();
  int bytes = operation->result();

  // Release the references acquired in PostRead / PostWrite.
  op->file()->Release();
  callback->OnFileIOComplete(bytes);
}

// A static object tha will broker all async operations.
FileInFlightIO* s_file_operations = NULL;

// Returns the current FileInFlightIO.
FileInFlightIO* GetFileInFlightIO() {
  if (!s_file_operations) {
    s_file_operations = new FileInFlightIO;
  }
  return s_file_operations;
}

// Deletes the current FileInFlightIO.
void DeleteFileInFlightIO() {
  DCHECK(s_file_operations);
  delete s_file_operations;
  s_file_operations = NULL;
}

}  // namespace

namespace disk_cache {

File::File(base::PlatformFile file)
    : init_(true),
      mixed_(true),
      platform_file_(file),
      sync_platform_file_(base::kInvalidPlatformFileValue) {
}

bool File::Init(const FilePath& name) {
  if (init_)
    return false;

  int flags = base::PLATFORM_FILE_OPEN |
              base::PLATFORM_FILE_READ |
              base::PLATFORM_FILE_WRITE;
  platform_file_ = base::CreatePlatformFile(name, flags, NULL, NULL);
  if (platform_file_ < 0) {
    platform_file_ = 0;
    return false;
  }

  init_ = true;
  return true;
}

base::PlatformFile File::platform_file() const {
  return platform_file_;
}

bool File::IsValid() const {
  if (!init_)
    return false;
  return (base::kInvalidPlatformFileValue != platform_file_);
}

bool File::Read(void* buffer, size_t buffer_len, size_t offset) {
  DCHECK(init_);
  if (buffer_len > static_cast<size_t>(kint32max) ||
      offset > static_cast<size_t>(kint32max))
    return false;

  int ret = base::ReadPlatformFile(platform_file_, offset,
                                   static_cast<char*>(buffer), buffer_len);
  return (static_cast<size_t>(ret) == buffer_len);
}

bool File::Write(const void* buffer, size_t buffer_len, size_t offset) {
  DCHECK(init_);
  if (buffer_len > static_cast<size_t>(kint32max) ||
      offset > static_cast<size_t>(kint32max))
    return false;

  int ret = base::WritePlatformFile(platform_file_, offset,
                                    static_cast<const char*>(buffer),
                                    buffer_len);
  return (static_cast<size_t>(ret) == buffer_len);
}

// We have to increase the ref counter of the file before performing the IO to
// prevent the completion to happen with an invalid handle (if the file is
// closed while the IO is in flight).
bool File::Read(void* buffer, size_t buffer_len, size_t offset,
                FileIOCallback* callback, bool* completed) {
  DCHECK(init_);
  if (!callback) {
    if (completed)
      *completed = true;
    return Read(buffer, buffer_len, offset);
  }

  if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
    return false;

  GetFileInFlightIO()->PostRead(this, buffer, buffer_len, offset, callback);

  *completed = false;
  return true;
}

bool File::Write(const void* buffer, size_t buffer_len, size_t offset,
                 FileIOCallback* callback, bool* completed) {
  DCHECK(init_);
  if (!callback) {
    if (completed)
      *completed = true;
    return Write(buffer, buffer_len, offset);
  }

  return AsyncWrite(buffer, buffer_len, offset, callback, completed);
}

bool File::SetLength(size_t length) {
  DCHECK(init_);
  if (length > ULONG_MAX)
    return false;

  return base::TruncatePlatformFile(platform_file_, length);
}

size_t File::GetLength() {
  DCHECK(init_);
  off_t ret = lseek(platform_file_, 0, SEEK_END);
  if (ret < 0)
    return 0;
  return ret;
}

// Static.
void File::WaitForPendingIO(int* num_pending_io) {
  // We may be running unit tests so we should allow be able to reset the
  // message loop.
  GetFileInFlightIO()->WaitForPendingIO();
  DeleteFileInFlightIO();
}

File::~File() {
  if (IsValid())
    base::ClosePlatformFile(platform_file_);
}

bool File::AsyncWrite(const void* buffer, size_t buffer_len, size_t offset,
                      FileIOCallback* callback, bool* completed) {
  DCHECK(init_);
  if (buffer_len > ULONG_MAX || offset > ULONG_MAX)
    return false;

  GetFileInFlightIO()->PostWrite(this, buffer, buffer_len, offset, callback);

  if (completed)
    *completed = false;
  return true;
}

}  // namespace disk_cache
<|endoftext|>