<commit_before>/*
 *  Copyright (c) 2015, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */
#include "mcrouter_cpp_tests.h"

#include <semaphore.h>
#include <sys/types.h>
#include <sys/wait.h>

#include <algorithm>
#include <condition_variable>
#include <mutex>
#include <string>
#include <vector>

#include <boost/filesystem.hpp>

#include <gtest/gtest.h>

#include <folly/experimental/TestUtil.h>
#include <folly/Singleton.h>
#include <folly/FileUtil.h>
#include <folly/io/async/EventBase.h>

#include "mcrouter/config.h"
#include "mcrouter/McrouterClient.h"
#include "mcrouter/McrouterInstance.h"
#include "mcrouter/options.h"
#include "mcrouter/proxy.h"
#include "mcrouter/ProxyClientCommon.h"
#include "mcrouter/routes/ShardSplitter.h"

using namespace boost::filesystem;
using namespace facebook::memcache::mcrouter;
using namespace std;

using facebook::memcache::createMcMsgRef;
using facebook::memcache::McMsgRef;
using facebook::memcache::McReply;
using facebook::memcache::McrouterOptions;

const std::string kAlreadyRepliedConfig =
  "mcrouter/test/cpp_unit_tests/files/already_replied.json";

#define MEMCACHE_ROUTE "/././"

McMsgRef new_get_req(const char *key) {
  auto msg = createMcMsgRef(key);
  msg->op = mc_op_get;
  return std::move(msg);
}

McMsgRef new_del_req(const char *key) {
  auto msg = createMcMsgRef(key);
  msg->op = mc_op_delete;
  return std::move(msg);
}

namespace {
std::atomic<size_t> gReplies;
std::mutex gMutex;
std::condition_variable gCv;
}

void on_reply(mcrouter_msg_t* router_req,
              void* context) {
  mcrouter_msg_t *r_msg = static_cast<mcrouter_msg_t*>(router_req->context);
  r_msg->reply = std::move(router_req->reply);
  ++gReplies;
  gCv.notify_one();
}

void mcrouter_send_helper(McrouterClient& client,
                          const vector<McMsgRef>& reqs,
                          vector<McReply> &replies) {
  vector<mc_msg_t*> ret;
  int n = reqs.size();
  replies.clear();

  mcrouter_msg_t *r_msgs = new mcrouter_msg_t[n];
  for (int i = 0; i < n; i++) {
    r_msgs[i].req = const_cast<mc_msg_t*>(reqs[i].get());
    r_msgs[i].reply = McReply(mc_res_unknown);
    r_msgs[i].context = &r_msgs[i];
  }

  gReplies = 0;
  client.send(r_msgs, n);
  std::unique_lock<std::mutex> lk(gMutex);
  gCv.wait(lk, [n] () { return gReplies == n; });

  for (size_t i = 0; i < n; ++i) {
    CHECK(r_msgs[i].reply.result() != mc_res_unknown);
    replies.push_back(std::move(r_msgs[i].reply));
  }
  delete [] r_msgs;
}

TEST(mcrouter, start_and_stop) {
  for (int i = 0; i < 2; i++) {
    auto opts = defaultTestOptions();
    opts.config_file = kMemcacheConfig;
    opts.default_route = MEMCACHE_ROUTE;
    auto router = McrouterInstance::init("test_start_and_stop", opts);
    EXPECT_FALSE(router == nullptr);

    {
      auto client = router->createClient(
        (mcrouter_client_callbacks_t){nullptr, nullptr, nullptr},
        nullptr,
        0);
    }
  }
}

void on_disconnect(void* context) {
  sem_post((sem_t*)context);
}

TEST(mcrouter, test_zeroreqs_mcroutersend) {
  sem_t sem_disconnect;
  sem_init(&sem_disconnect, 0, 0);
  auto opts = defaultTestOptions();
  opts.config_file = kMemcacheConfig;
  opts.default_route = MEMCACHE_ROUTE;
  auto router = McrouterInstance::init("test_zeroreqs_mcroutersend", opts);
  {
    auto client = router->createClient(
      (mcrouter_client_callbacks_t){nullptr, nullptr, on_disconnect},
      &sem_disconnect,
      0);

    vector<mcrouter_msg_t> reqs(0);

    client->send(reqs.data(), reqs.size());
    client->send(nullptr, 0);
  }
}

TEST(mcrouter, disconnect_callback) {
  sem_t sem_disconnect;
  sem_init(&sem_disconnect, 0, 0);

  auto opts = defaultTestOptions();
  opts.config_file = kMemcacheConfig;
  opts.default_route = MEMCACHE_ROUTE;
  auto router = McrouterInstance::init("test_disconnect_callback", opts);
  EXPECT_FALSE(router == nullptr);

  {
    auto client = router->createClient(
      (mcrouter_client_callbacks_t){nullptr, nullptr, on_disconnect},
      &sem_disconnect,
      0);

    const char test_key[] = "test_key_disconnect";
    vector<mcrouter_msg_t> reqs(1);
    auto msg = new_get_req(test_key);
    reqs.back().req = const_cast<mc_msg_t*>(msg.get());
    reqs.back().reply = McReply(mc_res_unknown);

    client->send(reqs.data(), reqs.size());

    EXPECT_NE(0, sem_trywait(&sem_disconnect));
  }
  EXPECT_EQ(0, sem_wait(&sem_disconnect));
}

TEST(mcrouter, fork) {
  const char persistence_id[] = "fork";
  auto opts = defaultTestOptions();
  opts.config_file = kMemcacheConfig;
  opts.default_route = MEMCACHE_ROUTE;
  auto router = McrouterInstance::init(persistence_id, opts);
  EXPECT_NE(static_cast<McrouterInstance*>(nullptr), router);

  const char parent_key[] = "libmcrouter_test:fork:parent";
  const char child_key[] = "libmcrouter_test:fork:child";

  {
    auto client = router->createClient(
      (mcrouter_client_callbacks_t){on_reply, nullptr, nullptr},
      nullptr,
      0);

    vector<McMsgRef> preqs;
    vector<McReply> preplies;
    preqs.push_back(new_get_req(parent_key));
    mcrouter_send_helper(*client, preqs, preplies);
  }

  int fds[2];
  pipe(fds);
  folly::SingletonVault::singleton()->destroyInstances();
  pid_t pid = fork();
  folly::SingletonVault::singleton()->reenableInstances();

  router = McrouterInstance::init(persistence_id, opts);
  EXPECT_NE(static_cast<McrouterInstance*>(nullptr), router);

  {
    auto client = router->createClient(
      (mcrouter_client_callbacks_t){on_reply, nullptr, nullptr},
      nullptr,
      0);

    vector<McMsgRef> reqs;
    vector<McReply> replies;
    if (pid) { // parent
      close(fds[1]);
      reqs.push_back(new_get_req(parent_key));
      mcrouter_send_helper(*client, reqs, replies);
    } else {
      router = McrouterInstance::get(persistence_id);
      reqs.push_back(new_get_req(child_key));
      mcrouter_send_helper(*client, reqs, replies);
    }
  }

  if (pid) { // parent
    char buf[100];
    read(fds[0], buf, sizeof(buf));
    close(fds[0]);
    waitpid(pid, nullptr, 0);
  } else {
    write(fds[1], "done", 4);
    close(fds[1]);
    exit(0);
  }
}

TEST(mcrouter, already_replied_failed_delete) {
  folly::test::TemporaryDirectory tmpdir("already_replied_failed_delete");
  mcrouter_client_callbacks_t cb = {
    .on_reply = &on_reply,
    .on_cancel = nullptr,
    .on_disconnect = nullptr
  };
  auto opts = defaultTestOptions();
  std::string configStr;
  EXPECT_TRUE(folly::readFile(kAlreadyRepliedConfig.data(), configStr));
  opts.config_str = configStr;
  opts.async_spool = tmpdir.path().string();
  auto router = McrouterInstance::init("test_already_replied_failed_delete",
                                       opts);
  EXPECT_TRUE(router != nullptr);

  {
    auto client = router->createClient(cb, nullptr, 0);

    vector<McMsgRef> reqs;
    vector<McReply> replies;
    reqs.push_back(new_del_req("abc"));
    mcrouter_send_helper(*client, reqs, replies);

    // Give mcrouter a chance to write the log -- we don't really know when
    // the write completes because we receive the response right away.
    sleep(1);
  }

  // Verify the existance and contents of the file.
  vector<string> files;
  string contents;
  string prefix(R"!(["AS1.0",)!");
  string suffix(R"!(,"C",["127.0.0.1",4242,"delete abc\r\n"]])!" "\n");

  for (recursive_directory_iterator it(tmpdir.path());
       it != recursive_directory_iterator();
       ++it) {
    if (!is_directory(it->symlink_status())) {
      files.push_back(it->path().string());
    }
  }
  EXPECT_EQ(files.size(), 1);

  folly::readFile(files[0].data(), contents);
  EXPECT_GT(contents.size(), prefix.size());
  EXPECT_GT(contents.size(), suffix.size());

  auto p = mismatch(prefix.begin(), prefix.end(), contents.begin());
  EXPECT_TRUE(p.first == prefix.end());

  auto s = mismatch(suffix.rbegin(), suffix.rend(), contents.rbegin());
  EXPECT_TRUE(s.first == suffix.rend());
}
<commit_msg>Fix flaky tests<commit_after>/*
 *  Copyright (c) 2015, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */
#include "mcrouter_cpp_tests.h"

#include <semaphore.h>
#include <sys/types.h>
#include <sys/wait.h>

#include <algorithm>
#include <condition_variable>
#include <mutex>
#include <string>
#include <vector>

#include <boost/filesystem.hpp>

#include <gtest/gtest.h>

#include <folly/experimental/TestUtil.h>
#include <folly/Singleton.h>
#include <folly/FileUtil.h>
#include <folly/io/async/EventBase.h>

#include "mcrouter/config.h"
#include "mcrouter/McrouterClient.h"
#include "mcrouter/McrouterInstance.h"
#include "mcrouter/options.h"
#include "mcrouter/proxy.h"
#include "mcrouter/ProxyClientCommon.h"
#include "mcrouter/routes/ShardSplitter.h"

using namespace boost::filesystem;
using namespace facebook::memcache::mcrouter;
using namespace std;

using facebook::memcache::createMcMsgRef;
using facebook::memcache::McMsgRef;
using facebook::memcache::McReply;
using facebook::memcache::McrouterOptions;

const std::string kAlreadyRepliedConfig =
  "mcrouter/test/cpp_unit_tests/files/already_replied.json";

#define MEMCACHE_ROUTE "/././"

McMsgRef new_get_req(const char *key) {
  auto msg = createMcMsgRef(key);
  msg->op = mc_op_get;
  return std::move(msg);
}

McMsgRef new_del_req(const char *key) {
  auto msg = createMcMsgRef(key);
  msg->op = mc_op_delete;
  return std::move(msg);
}

namespace {
std::atomic<size_t> gReplies;
std::mutex gMutex;
std::condition_variable gCv;
}

void on_reply(mcrouter_msg_t* router_req,
              void* context) {
  mcrouter_msg_t *r_msg = static_cast<mcrouter_msg_t*>(router_req->context);
  r_msg->reply = std::move(router_req->reply);
  ++gReplies;
  gCv.notify_one();
}

void mcrouter_send_helper(McrouterClient& client,
                          const vector<McMsgRef>& reqs,
                          vector<McReply> &replies) {
  vector<mc_msg_t*> ret;
  int n = reqs.size();
  replies.clear();

  mcrouter_msg_t *r_msgs = new mcrouter_msg_t[n];
  for (int i = 0; i < n; i++) {
    r_msgs[i].req = const_cast<mc_msg_t*>(reqs[i].get());
    r_msgs[i].reply = McReply(mc_res_unknown);
    r_msgs[i].context = &r_msgs[i];
  }

  gReplies = 0;
  client.send(r_msgs, n);
  std::unique_lock<std::mutex> lk(gMutex);
  gCv.wait(lk, [n] () { return gReplies == n; });

  for (size_t i = 0; i < n; ++i) {
    CHECK(r_msgs[i].reply.result() != mc_res_unknown);
    replies.push_back(std::move(r_msgs[i].reply));
  }
  delete [] r_msgs;
}

TEST(mcrouter, start_and_stop) {
  for (int i = 0; i < 2; i++) {
    auto opts = defaultTestOptions();
    opts.config_file = kMemcacheConfig;
    opts.default_route = MEMCACHE_ROUTE;
    auto router = McrouterInstance::init("test_start_and_stop", opts);
    EXPECT_FALSE(router == nullptr);

    {
      auto client = router->createClient(
        (mcrouter_client_callbacks_t){nullptr, nullptr, nullptr},
        nullptr,
        0);
    }
  }
}

TEST(mcrouter, test_zeroreqs_mcroutersend) {
  auto opts = defaultTestOptions();
  opts.config_file = kMemcacheConfig;
  opts.default_route = MEMCACHE_ROUTE;
  auto router = McrouterInstance::init("test_zeroreqs_mcroutersend", opts);
  {
    auto client = router->createClient(
      (mcrouter_client_callbacks_t){nullptr, nullptr, nullptr},
      nullptr,
      0);

    vector<mcrouter_msg_t> reqs(0);

    client->send(reqs.data(), reqs.size());
    client->send(nullptr, 0);
  }
}

void on_disconnect(void* context) {
  sem_post((sem_t*)context);
}

TEST(mcrouter, disconnect_callback) {
  sem_t sem_disconnect;
  sem_init(&sem_disconnect, 0, 0);

  auto opts = defaultTestOptions();
  opts.config_file = kMemcacheConfig;
  opts.default_route = MEMCACHE_ROUTE;
  auto router = McrouterInstance::init("test_disconnect_callback", opts);
  EXPECT_FALSE(router == nullptr);

  {
    auto client = router->createClient(
      (mcrouter_client_callbacks_t){nullptr, nullptr, on_disconnect},
      &sem_disconnect,
      0);

    const char test_key[] = "test_key_disconnect";
    vector<mcrouter_msg_t> reqs(1);
    auto msg = new_get_req(test_key);
    reqs.back().req = const_cast<mc_msg_t*>(msg.get());
    reqs.back().reply = McReply(mc_res_unknown);

    client->send(reqs.data(), reqs.size());

    EXPECT_NE(0, sem_trywait(&sem_disconnect));
  }
  EXPECT_EQ(0, sem_wait(&sem_disconnect));
  sem_destroy(&sem_disconnect);
}

TEST(mcrouter, fork) {
  const char persistence_id[] = "fork";
  auto opts = defaultTestOptions();
  opts.config_file = kMemcacheConfig;
  opts.default_route = MEMCACHE_ROUTE;
  auto router = McrouterInstance::init(persistence_id, opts);
  EXPECT_NE(static_cast<McrouterInstance*>(nullptr), router);

  const char parent_key[] = "libmcrouter_test:fork:parent";
  const char child_key[] = "libmcrouter_test:fork:child";

  {
    auto client = router->createClient(
      (mcrouter_client_callbacks_t){on_reply, nullptr, nullptr},
      nullptr,
      0);

    vector<McMsgRef> preqs;
    vector<McReply> preplies;
    preqs.push_back(new_get_req(parent_key));
    mcrouter_send_helper(*client, preqs, preplies);
  }

  int fds[2];
  pipe(fds);
  folly::SingletonVault::singleton()->destroyInstances();
  pid_t pid = fork();
  folly::SingletonVault::singleton()->reenableInstances();

  router = McrouterInstance::init(persistence_id, opts);
  EXPECT_NE(static_cast<McrouterInstance*>(nullptr), router);

  {
    auto client = router->createClient(
      (mcrouter_client_callbacks_t){on_reply, nullptr, nullptr},
      nullptr,
      0);

    vector<McMsgRef> reqs;
    vector<McReply> replies;
    if (pid) { // parent
      close(fds[1]);
      reqs.push_back(new_get_req(parent_key));
      mcrouter_send_helper(*client, reqs, replies);
    } else {
      router = McrouterInstance::get(persistence_id);
      reqs.push_back(new_get_req(child_key));
      mcrouter_send_helper(*client, reqs, replies);
    }
  }

  if (pid) { // parent
    char buf[100];
    read(fds[0], buf, sizeof(buf));
    close(fds[0]);
    waitpid(pid, nullptr, 0);
  } else {
    write(fds[1], "done", 4);
    close(fds[1]);
    exit(0);
  }
}

TEST(mcrouter, already_replied_failed_delete) {
  folly::test::TemporaryDirectory tmpdir("already_replied_failed_delete");
  mcrouter_client_callbacks_t cb = {
    .on_reply = &on_reply,
    .on_cancel = nullptr,
    .on_disconnect = nullptr
  };
  auto opts = defaultTestOptions();
  std::string configStr;
  EXPECT_TRUE(folly::readFile(kAlreadyRepliedConfig.data(), configStr));
  opts.config_str = configStr;
  opts.async_spool = tmpdir.path().string();
  auto router = McrouterInstance::init("test_already_replied_failed_delete",
                                       opts);
  EXPECT_TRUE(router != nullptr);

  {
    auto client = router->createClient(cb, nullptr, 0);

    vector<McMsgRef> reqs;
    vector<McReply> replies;
    reqs.push_back(new_del_req("abc"));
    mcrouter_send_helper(*client, reqs, replies);

    // Give mcrouter a chance to write the log -- we don't really know when
    // the write completes because we receive the response right away.
    sleep(1);
  }

  // Verify the existance and contents of the file.
  vector<string> files;
  string contents;
  string prefix(R"!(["AS1.0",)!");
  string suffix(R"!(,"C",["127.0.0.1",4242,"delete abc\r\n"]])!" "\n");

  for (recursive_directory_iterator it(tmpdir.path());
       it != recursive_directory_iterator();
       ++it) {
    if (!is_directory(it->symlink_status())) {
      files.push_back(it->path().string());
    }
  }
  EXPECT_EQ(files.size(), 1);

  folly::readFile(files[0].data(), contents);
  EXPECT_GT(contents.size(), prefix.size());
  EXPECT_GT(contents.size(), suffix.size());

  auto p = mismatch(prefix.begin(), prefix.end(), contents.begin());
  EXPECT_TRUE(p.first == prefix.end());

  auto s = mismatch(suffix.rbegin(), suffix.rend(), contents.rbegin());
  EXPECT_TRUE(s.first == suffix.rend());
}
<|endoftext|>


<commit_before>#ifndef MJOLNIR_FORCEFIELD_GLOBAL_PAIR_INTEARACTION_HPP
#define MJOLNIR_FORCEFIELD_GLOBAL_PAIR_INTEARACTION_HPP
#include <mjolnir/forcefield/global/ParameterList.hpp>
#include <mjolnir/core/SimulatorTraits.hpp>
#include <mjolnir/core/GlobalInteractionBase.hpp>
#include <mjolnir/core/SpatialPartitionBase.hpp>
#include <mjolnir/math/math.hpp>
#include <mjolnir/util/logger.hpp>
#include <mjolnir/util/string.hpp>
#include <memory>

namespace mjolnir
{

// Normal pair interaction.
//
// For the following potentials, there are specialized definitions to speedup
// calculation.
// - ExcludedVolume
//   - GlobalPairExcludedVolumeInteraction.hpp
// - LennardJones
//   - GlobalPairLennardJonesInteraction.hpp
// By combining potential calculations, we can omit `sqrt()` that is usually
// used to calculate the distance between particles.
//
template<typename traitsT, typename potentialT>
class GlobalPairInteraction final : public GlobalInteractionBase<traitsT>
{
  public:
    using traits_type         = traitsT;
    using potential_type      = potentialT;
    using base_type           = GlobalInteractionBase<traitsT>;
    using real_type           = typename base_type::real_type;
    using coordinate_type     = typename base_type::coordinate_type;
    using system_type         = typename base_type::system_type;
    using topology_type       = typename base_type::topology_type;
    using boundary_type       = typename base_type::boundary_type;
    using partition_type      = SpatialPartition<traits_type, potential_type>;
    using parameter_list_type = ParameterList<traits_type, potential_type>;

  public:

    GlobalPairInteraction(potential_type&& pot, parameter_list_type&& para,
                          partition_type&& part)
        : potential_(std::move(pot)), parameters_(std::move(para)),
          partition_(std::move(part))
    {}
    ~GlobalPairInteraction() override {}

    /*! @brief initialize spatial partition (e.g. CellList)                   *
     *  @details before calling `calc_(force|energy)`, this should be called. */
    void initialize(const system_type& sys, const topology_type& topol) override
    {
        MJOLNIR_GET_DEFAULT_LOGGER();
        MJOLNIR_LOG_FUNCTION();
        MJOLNIR_LOG_INFO("potential is ", this->name());

        this->potential_ .initialize(sys);
        this->parameters_.initialize(sys, topol, potential_);
        this->partition_ .initialize(sys, parameters_.cref());
    }

    /*! @brief update parameters (e.g. temperature, ionic strength, ...)  *
     *  @details A method that change system parameters (e.g. Annealing), *
     *           the method is bound to call this function after changing *
     *           parameters.                                              */
    void update(const system_type& sys, const topology_type& topol) override
    {
        MJOLNIR_GET_DEFAULT_LOGGER();
        MJOLNIR_LOG_FUNCTION();
        MJOLNIR_LOG_INFO("potential is ", this->name());

        this->potential_ .update(sys);
        this->parameters_.update(sys, topol, potential_);
        this->partition_ .initialize(sys, this->parameters_.cref());
    }

    void reduce_margin(const real_type dmargin, const system_type& sys) override
    {
        this->partition_.reduce_margin(dmargin, sys, this->parameters_.cref());
        return;
    }
    void scale_margin(const real_type scale, const system_type& sys) override
    {
        this->partition_.scale_margin(scale, sys, this->parameters_.cref());
        return;
    }

    void      calc_force (system_type&)           const noexcept override;
    real_type calc_energy(const system_type&)     const noexcept override;
    real_type calc_force_and_energy(system_type&) const noexcept override;

    std::string name() const override
    {
        return "Pair:"_s + potential_type::name();
    }

    parameter_list_type const& parameters() const noexcept {return parameters_.cref();}
    parameter_list_type &      parameters()       noexcept {return parameters_.cref();}

    potential_type const& potential() const noexcept {return potential_;}
    potential_type&       potential()       noexcept {return potential_;}

    partition_type const& partition() const noexcept {return partition_;}

    // deep-copy through the base class, GlobalInteractionBase*
    base_type* clone() const override
    {
        return new GlobalPairInteraction(*this);
    }

  private:

    potential_type      potential_;
    parameter_list_type parameters_;
    partition_type      partition_;

#ifdef MJOLNIR_WITH_OPENMP
    // OpenMP implementation uses its own implementation to run it in parallel.
    // So this implementation should not be instanciated with OpenMP Traits.
    static_assert(!is_openmp_simulator_traits<traits_type>::value,
                  "this is the default implementation, not for OpenMP");
#endif
};

template<typename traitsT, typename potT>
void GlobalPairInteraction<traitsT, potT>::calc_force(
        system_type& sys) const noexcept
{
    const auto leading_participants = this->parameters_.leading_participants();
    for(std::size_t idx=0; idx<leading_participants.size(); ++idx)
    {
        const auto i = leading_participants[idx];
        for(const auto& ptnr : this->partition_.partners(i))
        {
            const auto  j    = ptnr.index;
            const auto& para = ptnr.parameter();

            const auto rij = // ri -> rj = rj - ri
                sys.adjust_direction(sys.position(i), sys.position(j));
            const real_type l2 = math::length_sq(rij); // |rij|^2
            const real_type rl = math::rsqrt(l2);      // 1 / |rij|
            const real_type l  = l2 * rl;              // |rij|^2 / |rij|
            const real_type f_mag = potential_.derivative(l, para);

            // if length exceeds cutoff, potential returns just 0.
            if(f_mag == 0.0){continue;}

            const coordinate_type f = rij * (f_mag * rl);
            sys.force(i) += f;
            sys.force(j) -= f;

            // (rj - ri) * Fj = (ri - rj) * Fi
            sys.virial() += math::tensor_product(rij, -f);
        }
    }
    return ;
}

template<typename traitsT, typename potT>
typename GlobalPairInteraction<traitsT, potT>::real_type
GlobalPairInteraction<traitsT, potT>::calc_energy(
        const system_type& sys) const noexcept
{
    real_type E = 0.0;

    const auto leading_participants = this->parameters_.leading_participants();
    for(std::size_t idx=0; idx<leading_participants.size(); ++idx)
    {
        const auto i = leading_participants[idx];
        for(const auto& ptnr : this->partition_.partners(i))
        {
            const auto  j    = ptnr.index;
            const auto& para = ptnr.parameter();

            const real_type l = math::length(
                sys.adjust_direction(sys.position(i), sys.position(j)));
            E += potential_.potential(l, para);
        }
    }
    return E;
}

template<typename traitsT, typename potT>
typename GlobalPairInteraction<traitsT, potT>::real_type
GlobalPairInteraction<traitsT, potT>::calc_force_and_energy(
        system_type& sys) const noexcept
{
    real_type energy = 0.0;
    const auto leading_participants = this->parameters_.leading_participants();
    for(std::size_t idx=0; idx<leading_participants.size(); ++idx)
    {
        const auto i = leading_participants[idx];
        for(const auto& ptnr : this->partition_.partners(i))
        {
            const auto  j    = ptnr.index;
            const auto& para = ptnr.parameter();

            const auto rij =
                sys.adjust_direction(sys.position(i), sys.position(j));
            const real_type l2 = math::length_sq(rij); // |rij|^2
            const real_type rl = math::rsqrt(l2);      // 1 / |rij|
            const real_type l  = l2 * rl;              // |rij|^2 / |rij|
            const real_type f_mag = potential_.derivative(l, para);

            // if length exceeds cutoff, potential returns just 0.
            if(f_mag == 0.0){continue;}

            energy += potential_.potential(l, para);
            const coordinate_type f = rij * (f_mag * rl);
            sys.force(i) += f;
            sys.force(j) -= f;

            // (rj - ri) * Fj = (ri - rj) * Fi
            sys.virial() += math::tensor_product(rij, -f);
        }
    }
    return energy;
}

} // mjolnir

#include <mjolnir/forcefield/global/GlobalPairExcludedVolumeInteraction.hpp>
#include <mjolnir/forcefield/global/GlobalPairLennardJonesInteraction.hpp>

// #ifdef MJOLNIR_SEPARATE_BUILD
// // explicitly specialize BondAngleInteraction with LocalPotentials
// #include <mjolnir/core/BoundaryCondition.hpp>
// #include <mjolnir/core/SimulatorTraits.hpp>
// #include <mjolnir/forcefield/global/DebyeHuckelPotential.hpp>
//
// namespace mjolnir
// {
//
// // EXV, L-J and UL-J have its own specialization, so DO NOT specialize here.
//
// // D-H
// extern template class GlobalPairInteraction<SimulatorTraits<double, UnlimitedBoundary>,        DebyeHuckelPotential<SimulatorTraits<double, UnlimitedBoundary>       >>;
// extern template class GlobalPairInteraction<SimulatorTraits<float,  UnlimitedBoundary>,        DebyeHuckelPotential<SimulatorTraits<float,  UnlimitedBoundary>       >>;
// extern template class GlobalPairInteraction<SimulatorTraits<double, CuboidalPeriodicBoundary>, DebyeHuckelPotential<SimulatorTraits<double, CuboidalPeriodicBoundary>>>;
// extern template class GlobalPairInteraction<SimulatorTraits<float,  CuboidalPeriodicBoundary>, DebyeHuckelPotential<SimulatorTraits<float,  CuboidalPeriodicBoundary>>>;
// } // mjolnir
// #endif // MJOLNIR_SEPARATE_BUILD

#endif /* MJOLNIR_GLOBAL_PAIR_INTEARACTION */
<commit_msg>feat: add calc_force_and_virial_impl to GlobalPair<commit_after>#ifndef MJOLNIR_FORCEFIELD_GLOBAL_PAIR_INTEARACTION_HPP
#define MJOLNIR_FORCEFIELD_GLOBAL_PAIR_INTEARACTION_HPP
#include <mjolnir/forcefield/global/ParameterList.hpp>
#include <mjolnir/core/SimulatorTraits.hpp>
#include <mjolnir/core/GlobalInteractionBase.hpp>
#include <mjolnir/core/SpatialPartitionBase.hpp>
#include <mjolnir/math/math.hpp>
#include <mjolnir/util/logger.hpp>
#include <mjolnir/util/string.hpp>
#include <memory>

namespace mjolnir
{

// Normal pair interaction.
//
// For the following potentials, there are specialized definitions to speedup
// calculation.
// - ExcludedVolume
//   - GlobalPairExcludedVolumeInteraction.hpp
// - LennardJones
//   - GlobalPairLennardJonesInteraction.hpp
// By combining potential calculations, we can omit `sqrt()` that is usually
// used to calculate the distance between particles.
//
template<typename traitsT, typename potentialT>
class GlobalPairInteraction final : public GlobalInteractionBase<traitsT>
{
  public:
    using traits_type         = traitsT;
    using potential_type      = potentialT;
    using base_type           = GlobalInteractionBase<traitsT>;
    using real_type           = typename base_type::real_type;
    using coordinate_type     = typename base_type::coordinate_type;
    using system_type         = typename base_type::system_type;
    using topology_type       = typename base_type::topology_type;
    using boundary_type       = typename base_type::boundary_type;
    using partition_type      = SpatialPartition<traits_type, potential_type>;
    using parameter_list_type = ParameterList<traits_type, potential_type>;

  public:

    GlobalPairInteraction(potential_type&& pot, parameter_list_type&& para,
                          partition_type&& part)
        : potential_(std::move(pot)), parameters_(std::move(para)),
          partition_(std::move(part))
    {}
    ~GlobalPairInteraction() override {}

    /*! @brief initialize spatial partition (e.g. CellList)                   *
     *  @details before calling `calc_(force|energy)`, this should be called. */
    void initialize(const system_type& sys, const topology_type& topol) override
    {
        MJOLNIR_GET_DEFAULT_LOGGER();
        MJOLNIR_LOG_FUNCTION();
        MJOLNIR_LOG_INFO("potential is ", this->name());

        this->potential_ .initialize(sys);
        this->parameters_.initialize(sys, topol, potential_);
        this->partition_ .initialize(sys, parameters_.cref());
    }

    /*! @brief update parameters (e.g. temperature, ionic strength, ...)  *
     *  @details A method that change system parameters (e.g. Annealing), *
     *           the method is bound to call this function after changing *
     *           parameters.                                              */
    void update(const system_type& sys, const topology_type& topol) override
    {
        MJOLNIR_GET_DEFAULT_LOGGER();
        MJOLNIR_LOG_FUNCTION();
        MJOLNIR_LOG_INFO("potential is ", this->name());

        this->potential_ .update(sys);
        this->parameters_.update(sys, topol, potential_);
        this->partition_ .initialize(sys, this->parameters_.cref());
    }

    void reduce_margin(const real_type dmargin, const system_type& sys) override
    {
        this->partition_.reduce_margin(dmargin, sys, this->parameters_.cref());
        return;
    }
    void scale_margin(const real_type scale, const system_type& sys) override
    {
        this->partition_.scale_margin(scale, sys, this->parameters_.cref());
        return;
    }

    void calc_force (system_type& sys)        const noexcept override
    {
        this->template calc_force_and_virial_impl<false>(sys);
    }
    void calc_force_and_virial(system_type& sys) const noexcept override
    {
        this->template calc_force_and_virial_impl<true>(sys);
    }
    real_type calc_energy(const system_type&)     const noexcept override;
    real_type calc_force_and_energy(system_type&) const noexcept override;

    std::string name() const override
    {
        return "Pair:"_s + potential_type::name();
    }

    parameter_list_type const& parameters() const noexcept {return parameters_.cref();}
    parameter_list_type &      parameters()       noexcept {return parameters_.cref();}

    potential_type const& potential() const noexcept {return potential_;}
    potential_type&       potential()       noexcept {return potential_;}

    partition_type const& partition() const noexcept {return partition_;}

    // deep-copy through the base class, GlobalInteractionBase*
    base_type* clone() const override
    {
        return new GlobalPairInteraction(*this);
    }

  private:

    template<bool NeedVirial>
    void calc_force_and_virial_impl(system_type& sys) const noexcept;

  private:

    potential_type      potential_;
    parameter_list_type parameters_;
    partition_type      partition_;

#ifdef MJOLNIR_WITH_OPENMP
    // OpenMP implementation uses its own implementation to run it in parallel.
    // So this implementation should not be instanciated with OpenMP Traits.
    static_assert(!is_openmp_simulator_traits<traits_type>::value,
                  "this is the default implementation, not for OpenMP");
#endif
};

template<typename traitsT, typename potT>
template<bool NeedVirial>
void GlobalPairInteraction<traitsT, potT>::calc_force_and_virial_impl(
        system_type& sys) const noexcept
{
    const auto leading_participants = this->parameters_.leading_participants();
    for(std::size_t idx=0; idx<leading_participants.size(); ++idx)
    {
        const auto i = leading_participants[idx];
        for(const auto& ptnr : this->partition_.partners(i))
        {
            const auto  j    = ptnr.index;
            const auto& para = ptnr.parameter();

            const auto rij = // ri -> rj = rj - ri
                sys.adjust_direction(sys.position(i), sys.position(j));
            const real_type l2 = math::length_sq(rij); // |rij|^2
            const real_type rl = math::rsqrt(l2);      // 1 / |rij|
            const real_type l  = l2 * rl;              // |rij|^2 / |rij|
            const real_type f_mag = potential_.derivative(l, para);

            // if length exceeds cutoff, potential returns just 0.
            if(f_mag == 0.0){continue;}

            const coordinate_type f = rij * (f_mag * rl);
            sys.force(i) += f;
            sys.force(j) -= f;

            if(NeedVirial)
            {
                // (rj - ri) * Fj = (ri - rj) * Fi
                sys.virial() += math::tensor_product(rij, -f);
            }
        }
    }
    return ;
}

template<typename traitsT, typename potT>
typename GlobalPairInteraction<traitsT, potT>::real_type
GlobalPairInteraction<traitsT, potT>::calc_energy(
        const system_type& sys) const noexcept
{
    real_type E = 0.0;

    const auto leading_participants = this->parameters_.leading_participants();
    for(std::size_t idx=0; idx<leading_participants.size(); ++idx)
    {
        const auto i = leading_participants[idx];
        for(const auto& ptnr : this->partition_.partners(i))
        {
            const auto  j    = ptnr.index;
            const auto& para = ptnr.parameter();

            const real_type l = math::length(
                sys.adjust_direction(sys.position(i), sys.position(j)));
            E += potential_.potential(l, para);
        }
    }
    return E;
}

template<typename traitsT, typename potT>
typename GlobalPairInteraction<traitsT, potT>::real_type
GlobalPairInteraction<traitsT, potT>::calc_force_and_energy(
        system_type& sys) const noexcept
{
    real_type energy = 0.0;
    const auto leading_participants = this->parameters_.leading_participants();
    for(std::size_t idx=0; idx<leading_participants.size(); ++idx)
    {
        const auto i = leading_participants[idx];
        for(const auto& ptnr : this->partition_.partners(i))
        {
            const auto  j    = ptnr.index;
            const auto& para = ptnr.parameter();

            const auto rij =
                sys.adjust_direction(sys.position(i), sys.position(j));
            const real_type l2 = math::length_sq(rij); // |rij|^2
            const real_type rl = math::rsqrt(l2);      // 1 / |rij|
            const real_type l  = l2 * rl;              // |rij|^2 / |rij|
            const real_type f_mag = potential_.derivative(l, para);

            // if length exceeds cutoff, potential returns just 0.
            if(f_mag == 0.0){continue;}

            energy += potential_.potential(l, para);
            const coordinate_type f = rij * (f_mag * rl);
            sys.force(i) += f;
            sys.force(j) -= f;

            // (rj - ri) * Fj = (ri - rj) * Fi
            sys.virial() += math::tensor_product(rij, -f);
        }
    }
    return energy;
}

} // mjolnir

#include <mjolnir/forcefield/global/GlobalPairExcludedVolumeInteraction.hpp>
#include <mjolnir/forcefield/global/GlobalPairLennardJonesInteraction.hpp>

// #ifdef MJOLNIR_SEPARATE_BUILD
// // explicitly specialize BondAngleInteraction with LocalPotentials
// #include <mjolnir/core/BoundaryCondition.hpp>
// #include <mjolnir/core/SimulatorTraits.hpp>
// #include <mjolnir/forcefield/global/DebyeHuckelPotential.hpp>
//
// namespace mjolnir
// {
//
// // EXV, L-J and UL-J have its own specialization, so DO NOT specialize here.
//
// // D-H
// extern template class GlobalPairInteraction<SimulatorTraits<double, UnlimitedBoundary>,        DebyeHuckelPotential<SimulatorTraits<double, UnlimitedBoundary>       >>;
// extern template class GlobalPairInteraction<SimulatorTraits<float,  UnlimitedBoundary>,        DebyeHuckelPotential<SimulatorTraits<float,  UnlimitedBoundary>       >>;
// extern template class GlobalPairInteraction<SimulatorTraits<double, CuboidalPeriodicBoundary>, DebyeHuckelPotential<SimulatorTraits<double, CuboidalPeriodicBoundary>>>;
// extern template class GlobalPairInteraction<SimulatorTraits<float,  CuboidalPeriodicBoundary>, DebyeHuckelPotential<SimulatorTraits<float,  CuboidalPeriodicBoundary>>>;
// } // mjolnir
// #endif // MJOLNIR_SEPARATE_BUILD

#endif /* MJOLNIR_GLOBAL_PAIR_INTEARACTION */
<|endoftext|>


<commit_before>/*
	TUIO C++ Server Demo - part of the reacTIVision project
	http://reactivision.sourceforge.net/

	Copyright (c) 2005-2009 Martin Kaltenbrunner <mkalten@iua.upf.edu>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "NosuchUtil.h"
#include "TuioServer.h"
#include "TuioSharedMemServer.h"
#include "TuioUdpServer.h"
#include "TuioCursor.h"
#include "TuioDevice.h"
#include "morph.h"
#include "xgetopt.h"
#include "tchar.h"

using namespace TUIO;

void
printUsage() {
	std::cout << "usage: tuio3d_* [-v] [-V #] [-a #] [-h host] [-p port] [-m name]\n";
	std::cout << "  -V #         Verbosity level\n";
	std::cout << "  -a #         Number of milliseconds between alive messages\n";
	std::cout << "  -i #         Initial session id\n";
	std::cout << "  -h {host}    Hostname for TUIO output\n";
	std::cout << "  -p #         Port number for TUIO output\n";
	std::cout << "  -m {name}    Shared memory name\n";
}

int main(int argc, const char* argv[])
{
	const char *host = NULL;
	const char *memname = NULL;
	int port = -1;
	int verbose = 0;
	int c;
	int alive_update_interval = 1000; // milliseconds
	int initial_session_id = 11000;   // session id space
	int device_multiplier =   1000;   // in session id's.  I.e. the second device starts at 12000
	bool flipx = false;
	bool flipy = false;

	while ((c = getopt(argc, argv, "vxyV:a:h:i:m:p:")) != EOF) {
		switch (c) {
			case _T('v'):
				verbose = 1;
				break;
			case _T('x'):
				flipx = true;
				break;
			case _T('y'):
				flipy = true;
				break;
			case _T('V'):
				verbose = atoi(optarg);
				break;
			case _T('a'):
				alive_update_interval = atoi(optarg);
				break;
			case _T('h'):
				host = optarg;
				break;
			case _T('i'):
				initial_session_id = atoi(optarg);
				break;
			case _T('m'):
				memname = optarg;
				break;
			case _T('p'):
				port = atoi(optarg);
				break;
			case _T('?'):
				printUsage();
        		return 0;
		}
	}
	int nleft = argc - optind;
	// std::cout << "nleft = " << nleft << " verbose = " << verbose << "\n";
	if ( nleft != 0 ) {
		printUsage();
		return 0;
	}

	if (memname != NULL && host != NULL) {
		NosuchDebug("You can't do both shared-memory and TUIO at the same time!\n");
		return 1;
	}
	if (memname == NULL && host == NULL) {
		std::cout << "Defaulting to TUIO on host 127.0.0.1\n";
		host = "127.0.0.1";
	}

	TuioServer* server = NULL;

	if (host) {
		if (port < 0) {
			port = 3333;
			std::cout << "Assuming port=" << port << "\n";
		}
		server = new TuioUdpServer(host, port, alive_update_interval);
	}
	else if (memname) {
		server = new TuioSharedMemServer(memname);
	}

	server->flipX = flipx;
	server->flipY = flipy;
	server->initial_session_id = initial_session_id;
	server->device_multiplier = device_multiplier;
	server->verbose = verbose;

	TuioDevice *device = new Morph(server);
	if (device->init()) {
		device->run();
	}
	// run() may not actually return
	delete(device);
	delete(server);

	return 0;
}<commit_msg>clean up options<commit_after>/*
	TUIO C++ Server Demo - part of the reacTIVision project
	http://reactivision.sourceforge.net/

	Copyright (c) 2005-2009 Martin Kaltenbrunner <mkalten@iua.upf.edu>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "NosuchUtil.h"
#include "TuioServer.h"
#include "TuioSharedMemServer.h"
#include "TuioUdpServer.h"
#include "TuioCursor.h"
#include "TuioDevice.h"
#include "morph.h"
#include "xgetopt.h"
#include "tchar.h"

using namespace TUIO;

void
printUsage() {
	std::cout << "usage: tuio3d_* [-v] [-V #] [-a #] [-h host] [-p port] [-m name]\n";
	std::cout << "  -v               Verbosity level = 1\n";
	std::cout << "  -V #             Verbosity level = #\n";
	std::cout << "  -a #             Number of milliseconds between alive messages\n";
	std::cout << "  -i #             Initial session id\n";
	std::cout << "  -h {host}        Hostname for TUIO output (assumes port 3333)\n";
	std::cout << "  -h {port}@{host} Port and hostname for TUIO output\n";
	std::cout << "  -m {name}        Shared memory name\n";
}

int main(int argc, const char* argv[])
{
	const char *host = NULL;
	const char *memname = NULL;
	int port = -1;
	int verbose = 0;
	int c;
	int alive_update_interval = 1000; // milliseconds
	int initial_session_id = 11000;   // session id space
	int device_multiplier =   1000;   // in session id's.  I.e. the second device starts at 12000
	bool flipx = false;
	bool flipy = false;

	while ((c = getopt(argc, argv, "vxyV:a:h:i:m:p:")) != EOF) {
		switch (c) {
			case _T('v'):
				verbose = 1;
				break;
			case _T('x'):
				flipx = true;
				break;
			case _T('y'):
				flipy = true;
				break;
			case _T('V'):
				verbose = atoi(optarg);
				break;
			case _T('a'):
				alive_update_interval = atoi(optarg);
				break;
			case _T('h'):
				host = optarg;
				break;
			case _T('i'):
				initial_session_id = atoi(optarg);
				break;
			case _T('m'):
				memname = optarg;
				break;
			case _T('?'):
				printUsage();
        		return 0;
		}
	}
	int nleft = argc - optind;
	// std::cout << "nleft = " << nleft << " verbose = " << verbose << "\n";
	if ( nleft != 0 ) {
		printUsage();
		return 0;
	}

	if (memname != NULL && host != NULL) {
		NosuchDebug("You can't do both shared-memory and TUIO at the same time!\n");
		return 1;
	}
	if (memname == NULL && host == NULL) {
		std::cout << "Defaulting to TUIO on host 127.0.0.1\n";
		host = "127.0.0.1";
	}

	TuioServer* server = NULL;

	if (host) {
		const char* amp = strchr(host, '@');
		if (amp) {
			port = atoi(host);
			host = amp + 1;
		} else {
			port = 3333;
		}
		std::cout << "Sending TUIO output to port " << port << " of host " << host << "\n";
		server = new TuioUdpServer(host, port, alive_update_interval);
	}
	else if (memname) {
		server = new TuioSharedMemServer(memname);
	}

	server->flipX = flipx;
	server->flipY = flipy;
	server->initial_session_id = initial_session_id;
	server->device_multiplier = device_multiplier;
	server->verbose = verbose;

	TuioDevice *device = new Morph(server);
	if (device->init()) {
		device->run();
	}
	// run() may not actually return
	delete(device);
	delete(server);

	return 0;
}<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "encoded_frame.h"
#include "generic_encoder.h"
#include "media_optimization.h"
#include "../../../../engine_configurations.h"
#include "trace_event.h"

namespace webrtc {

//#define DEBUG_ENCODER_BIT_STREAM

VCMGenericEncoder::VCMGenericEncoder(VideoEncoder& encoder, bool internalSource /*= false*/)
:
_encoder(encoder),
_codecType(kVideoCodecUnknown),
_VCMencodedFrameCallback(NULL),
_bitRate(0),
_frameRate(0),
_internalSource(internalSource)
{
}

VCMGenericEncoder::~VCMGenericEncoder()
{
}

int32_t VCMGenericEncoder::Release()
{
    _bitRate = 0;
    _frameRate = 0;
    _VCMencodedFrameCallback = NULL;
    return _encoder.Release();
}

int32_t
VCMGenericEncoder::InitEncode(const VideoCodec* settings,
                              int32_t numberOfCores,
                              uint32_t maxPayloadSize)
{
    _bitRate = settings->startBitrate;
    _frameRate = settings->maxFramerate;
    _codecType = settings->codecType;
    if (_VCMencodedFrameCallback != NULL)
    {
        _VCMencodedFrameCallback->SetCodecType(_codecType);
    }
    return _encoder.InitEncode(settings, numberOfCores, maxPayloadSize);
}

int32_t
VCMGenericEncoder::Encode(const I420VideoFrame& inputFrame,
                          const CodecSpecificInfo* codecSpecificInfo,
                          const std::vector<FrameType>& frameTypes) {
  std::vector<VideoFrameType> video_frame_types(frameTypes.size(),
                                                kDeltaFrame);
  VCMEncodedFrame::ConvertFrameTypes(frameTypes, &video_frame_types);
  return _encoder.Encode(inputFrame, codecSpecificInfo, &video_frame_types);
}

int32_t
VCMGenericEncoder::SetChannelParameters(int32_t packetLoss, int rtt)
{
    return _encoder.SetChannelParameters(packetLoss, rtt);
}

int32_t
VCMGenericEncoder::SetRates(uint32_t newBitRate, uint32_t frameRate)
{
    uint32_t target_bitrate_kbps = (newBitRate + 500) / 1000;
    int32_t ret = _encoder.SetRates(target_bitrate_kbps, frameRate);
    if (ret < 0)
    {
        return ret;
    }
    _bitRate = newBitRate;
    _frameRate = frameRate;
    return VCM_OK;
}

int32_t
VCMGenericEncoder::CodecConfigParameters(uint8_t* buffer, int32_t size)
{
    int32_t ret = _encoder.CodecConfigParameters(buffer, size);
    if (ret < 0)
    {
        return ret;
    }
    return ret;
}

uint32_t VCMGenericEncoder::BitRate() const
{
    return _bitRate;
}

uint32_t VCMGenericEncoder::FrameRate() const
{
    return _frameRate;
}

int32_t
VCMGenericEncoder::SetPeriodicKeyFrames(bool enable)
{
    return _encoder.SetPeriodicKeyFrames(enable);
}

int32_t VCMGenericEncoder::RequestFrame(
    const std::vector<FrameType>& frame_types) {
  I420VideoFrame image;
  std::vector<VideoFrameType> video_frame_types(frame_types.size(),
                                                kDeltaFrame);
  VCMEncodedFrame::ConvertFrameTypes(frame_types, &video_frame_types);
  return _encoder.Encode(image, NULL, &video_frame_types);
}

int32_t
VCMGenericEncoder::RegisterEncodeCallback(VCMEncodedFrameCallback* VCMencodedFrameCallback)
{
   _VCMencodedFrameCallback = VCMencodedFrameCallback;

   _VCMencodedFrameCallback->SetCodecType(_codecType);
   _VCMencodedFrameCallback->SetInternalSource(_internalSource);
   return _encoder.RegisterEncodeCompleteCallback(_VCMencodedFrameCallback);
}

bool
VCMGenericEncoder::InternalSource() const
{
    return _internalSource;
}

 /***************************
  * Callback Implementation
  ***************************/
VCMEncodedFrameCallback::VCMEncodedFrameCallback():
_sendCallback(),
_mediaOpt(NULL),
_encodedBytes(0),
_payloadType(0),
_codecType(kVideoCodecUnknown),
_internalSource(false)
#ifdef DEBUG_ENCODER_BIT_STREAM
, _bitStreamAfterEncoder(NULL)
#endif
{
#ifdef DEBUG_ENCODER_BIT_STREAM
    _bitStreamAfterEncoder = fopen("encoderBitStream.bit", "wb");
#endif
}

VCMEncodedFrameCallback::~VCMEncodedFrameCallback()
{
#ifdef DEBUG_ENCODER_BIT_STREAM
    fclose(_bitStreamAfterEncoder);
#endif
}

int32_t
VCMEncodedFrameCallback::SetTransportCallback(VCMPacketizationCallback* transport)
{
    _sendCallback = transport;
    return VCM_OK;
}

int32_t
VCMEncodedFrameCallback::Encoded(
    EncodedImage &encodedImage,
    const CodecSpecificInfo* codecSpecificInfo,
    const RTPFragmentationHeader* fragmentationHeader)
{
    TRACE_EVENT2("webrtc", "VCM::Encoded", "timestamp", encodedImage._timeStamp,
                 "length", encodedImage._length);
    FrameType frameType = VCMEncodedFrame::ConvertFrameType(encodedImage._frameType);

    uint32_t encodedBytes = 0;
    if (_sendCallback != NULL)
    {
        encodedBytes = encodedImage._length;

#ifdef DEBUG_ENCODER_BIT_STREAM
        if (_bitStreamAfterEncoder != NULL)
        {
            fwrite(encodedImage._buffer, 1, encodedImage._length, _bitStreamAfterEncoder);
        }
#endif

        RTPVideoHeader rtpVideoHeader;
        RTPVideoHeader* rtpVideoHeaderPtr = &rtpVideoHeader;
        if (codecSpecificInfo)
        {
            CopyCodecSpecific(*codecSpecificInfo, &rtpVideoHeaderPtr);
        }
        else
        {
            rtpVideoHeaderPtr = NULL;
        }

        int32_t callbackReturn = _sendCallback->SendData(
            frameType,
            _payloadType,
            encodedImage._timeStamp,
            encodedImage.capture_time_ms_,
            encodedImage._buffer,
            encodedBytes,
            *fragmentationHeader,
            rtpVideoHeaderPtr);
       if (callbackReturn < 0)
       {
           return callbackReturn;
       }
    }
    else
    {
        return VCM_UNINITIALIZED;
    }
    _encodedBytes = encodedBytes;
    if (_mediaOpt != NULL) {
      _mediaOpt->UpdateWithEncodedData(_encodedBytes, encodedImage._timeStamp,
                                       frameType);
      if (_internalSource)
      {
          return _mediaOpt->DropFrame(); // Signal to encoder to drop next frame
      }
    }
    return VCM_OK;
}

uint32_t
VCMEncodedFrameCallback::EncodedBytes()
{
    return _encodedBytes;
}

void
VCMEncodedFrameCallback::SetMediaOpt(
    media_optimization::VCMMediaOptimization *mediaOpt)
{
    _mediaOpt = mediaOpt;
}

void VCMEncodedFrameCallback::CopyCodecSpecific(const CodecSpecificInfo& info,
                                                RTPVideoHeader** rtp) {
    switch (info.codecType) {
        case kVideoCodecVP8: {
            (*rtp)->codecHeader.VP8.InitRTPVideoHeaderVP8();
            (*rtp)->codecHeader.VP8.pictureId =
                info.codecSpecific.VP8.pictureId;
            (*rtp)->codecHeader.VP8.nonReference =
                info.codecSpecific.VP8.nonReference;
            (*rtp)->codecHeader.VP8.temporalIdx =
                info.codecSpecific.VP8.temporalIdx;
            (*rtp)->codecHeader.VP8.layerSync =
                info.codecSpecific.VP8.layerSync;
            (*rtp)->codecHeader.VP8.tl0PicIdx =
                info.codecSpecific.VP8.tl0PicIdx;
            (*rtp)->codecHeader.VP8.keyIdx =
                info.codecSpecific.VP8.keyIdx;
            (*rtp)->simulcastIdx = info.codecSpecific.VP8.simulcastIdx;
            return;
        }
        default: {
            // No codec specific info. Change RTP header pointer to NULL.
            *rtp = NULL;
            return;
        }
    }
}
} // namespace webrtc
<commit_msg>Fixes an issue where the start bitrate is stored in kbps instead of bps.<commit_after>/*
 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "encoded_frame.h"
#include "generic_encoder.h"
#include "media_optimization.h"
#include "../../../../engine_configurations.h"
#include "trace_event.h"

namespace webrtc {

//#define DEBUG_ENCODER_BIT_STREAM

VCMGenericEncoder::VCMGenericEncoder(VideoEncoder& encoder, bool internalSource /*= false*/)
:
_encoder(encoder),
_codecType(kVideoCodecUnknown),
_VCMencodedFrameCallback(NULL),
_bitRate(0),
_frameRate(0),
_internalSource(internalSource)
{
}

VCMGenericEncoder::~VCMGenericEncoder()
{
}

int32_t VCMGenericEncoder::Release()
{
    _bitRate = 0;
    _frameRate = 0;
    _VCMencodedFrameCallback = NULL;
    return _encoder.Release();
}

int32_t
VCMGenericEncoder::InitEncode(const VideoCodec* settings,
                              int32_t numberOfCores,
                              uint32_t maxPayloadSize)
{
    _bitRate = settings->startBitrate * 1000;
    _frameRate = settings->maxFramerate;
    _codecType = settings->codecType;
    if (_VCMencodedFrameCallback != NULL)
    {
        _VCMencodedFrameCallback->SetCodecType(_codecType);
    }
    return _encoder.InitEncode(settings, numberOfCores, maxPayloadSize);
}

int32_t
VCMGenericEncoder::Encode(const I420VideoFrame& inputFrame,
                          const CodecSpecificInfo* codecSpecificInfo,
                          const std::vector<FrameType>& frameTypes) {
  std::vector<VideoFrameType> video_frame_types(frameTypes.size(),
                                                kDeltaFrame);
  VCMEncodedFrame::ConvertFrameTypes(frameTypes, &video_frame_types);
  return _encoder.Encode(inputFrame, codecSpecificInfo, &video_frame_types);
}

int32_t
VCMGenericEncoder::SetChannelParameters(int32_t packetLoss, int rtt)
{
    return _encoder.SetChannelParameters(packetLoss, rtt);
}

int32_t
VCMGenericEncoder::SetRates(uint32_t newBitRate, uint32_t frameRate)
{
    uint32_t target_bitrate_kbps = (newBitRate + 500) / 1000;
    int32_t ret = _encoder.SetRates(target_bitrate_kbps, frameRate);
    if (ret < 0)
    {
        return ret;
    }
    _bitRate = newBitRate;
    _frameRate = frameRate;
    return VCM_OK;
}

int32_t
VCMGenericEncoder::CodecConfigParameters(uint8_t* buffer, int32_t size)
{
    int32_t ret = _encoder.CodecConfigParameters(buffer, size);
    if (ret < 0)
    {
        return ret;
    }
    return ret;
}

uint32_t VCMGenericEncoder::BitRate() const
{
    return _bitRate;
}

uint32_t VCMGenericEncoder::FrameRate() const
{
    return _frameRate;
}

int32_t
VCMGenericEncoder::SetPeriodicKeyFrames(bool enable)
{
    return _encoder.SetPeriodicKeyFrames(enable);
}

int32_t VCMGenericEncoder::RequestFrame(
    const std::vector<FrameType>& frame_types) {
  I420VideoFrame image;
  std::vector<VideoFrameType> video_frame_types(frame_types.size(),
                                                kDeltaFrame);
  VCMEncodedFrame::ConvertFrameTypes(frame_types, &video_frame_types);
  return _encoder.Encode(image, NULL, &video_frame_types);
}

int32_t
VCMGenericEncoder::RegisterEncodeCallback(VCMEncodedFrameCallback* VCMencodedFrameCallback)
{
   _VCMencodedFrameCallback = VCMencodedFrameCallback;

   _VCMencodedFrameCallback->SetCodecType(_codecType);
   _VCMencodedFrameCallback->SetInternalSource(_internalSource);
   return _encoder.RegisterEncodeCompleteCallback(_VCMencodedFrameCallback);
}

bool
VCMGenericEncoder::InternalSource() const
{
    return _internalSource;
}

 /***************************
  * Callback Implementation
  ***************************/
VCMEncodedFrameCallback::VCMEncodedFrameCallback():
_sendCallback(),
_mediaOpt(NULL),
_encodedBytes(0),
_payloadType(0),
_codecType(kVideoCodecUnknown),
_internalSource(false)
#ifdef DEBUG_ENCODER_BIT_STREAM
, _bitStreamAfterEncoder(NULL)
#endif
{
#ifdef DEBUG_ENCODER_BIT_STREAM
    _bitStreamAfterEncoder = fopen("encoderBitStream.bit", "wb");
#endif
}

VCMEncodedFrameCallback::~VCMEncodedFrameCallback()
{
#ifdef DEBUG_ENCODER_BIT_STREAM
    fclose(_bitStreamAfterEncoder);
#endif
}

int32_t
VCMEncodedFrameCallback::SetTransportCallback(VCMPacketizationCallback* transport)
{
    _sendCallback = transport;
    return VCM_OK;
}

int32_t
VCMEncodedFrameCallback::Encoded(
    EncodedImage &encodedImage,
    const CodecSpecificInfo* codecSpecificInfo,
    const RTPFragmentationHeader* fragmentationHeader)
{
    TRACE_EVENT2("webrtc", "VCM::Encoded", "timestamp", encodedImage._timeStamp,
                 "length", encodedImage._length);
    FrameType frameType = VCMEncodedFrame::ConvertFrameType(encodedImage._frameType);

    uint32_t encodedBytes = 0;
    if (_sendCallback != NULL)
    {
        encodedBytes = encodedImage._length;

#ifdef DEBUG_ENCODER_BIT_STREAM
        if (_bitStreamAfterEncoder != NULL)
        {
            fwrite(encodedImage._buffer, 1, encodedImage._length, _bitStreamAfterEncoder);
        }
#endif

        RTPVideoHeader rtpVideoHeader;
        RTPVideoHeader* rtpVideoHeaderPtr = &rtpVideoHeader;
        if (codecSpecificInfo)
        {
            CopyCodecSpecific(*codecSpecificInfo, &rtpVideoHeaderPtr);
        }
        else
        {
            rtpVideoHeaderPtr = NULL;
        }

        int32_t callbackReturn = _sendCallback->SendData(
            frameType,
            _payloadType,
            encodedImage._timeStamp,
            encodedImage.capture_time_ms_,
            encodedImage._buffer,
            encodedBytes,
            *fragmentationHeader,
            rtpVideoHeaderPtr);
       if (callbackReturn < 0)
       {
           return callbackReturn;
       }
    }
    else
    {
        return VCM_UNINITIALIZED;
    }
    _encodedBytes = encodedBytes;
    if (_mediaOpt != NULL) {
      _mediaOpt->UpdateWithEncodedData(_encodedBytes, encodedImage._timeStamp,
                                       frameType);
      if (_internalSource)
      {
          return _mediaOpt->DropFrame(); // Signal to encoder to drop next frame
      }
    }
    return VCM_OK;
}

uint32_t
VCMEncodedFrameCallback::EncodedBytes()
{
    return _encodedBytes;
}

void
VCMEncodedFrameCallback::SetMediaOpt(
    media_optimization::VCMMediaOptimization *mediaOpt)
{
    _mediaOpt = mediaOpt;
}

void VCMEncodedFrameCallback::CopyCodecSpecific(const CodecSpecificInfo& info,
                                                RTPVideoHeader** rtp) {
    switch (info.codecType) {
        case kVideoCodecVP8: {
            (*rtp)->codecHeader.VP8.InitRTPVideoHeaderVP8();
            (*rtp)->codecHeader.VP8.pictureId =
                info.codecSpecific.VP8.pictureId;
            (*rtp)->codecHeader.VP8.nonReference =
                info.codecSpecific.VP8.nonReference;
            (*rtp)->codecHeader.VP8.temporalIdx =
                info.codecSpecific.VP8.temporalIdx;
            (*rtp)->codecHeader.VP8.layerSync =
                info.codecSpecific.VP8.layerSync;
            (*rtp)->codecHeader.VP8.tl0PicIdx =
                info.codecSpecific.VP8.tl0PicIdx;
            (*rtp)->codecHeader.VP8.keyIdx =
                info.codecSpecific.VP8.keyIdx;
            (*rtp)->simulcastIdx = info.codecSpecific.VP8.simulcastIdx;
            return;
        }
        default: {
            // No codec specific info. Change RTP header pointer to NULL.
            *rtp = NULL;
            return;
        }
    }
}
} // namespace webrtc
<|endoftext|>


<commit_before>/*******************************************************************************
 * Copyright (c) 2015 Matthijs Kooijman
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * This the HAL to run LMIC on top of the Arduino environment.
 *******************************************************************************/

#include <Arduino.h>
#include <SPI.h>
#include "../lmic.h"
#include "hal.h"
#include <stdio.h>

// -----------------------------------------------------------------------------
// I/O

static const lmic_pinmap *plmic_pins;

static void hal_interrupt_init(); // Fwd declaration

static void hal_io_init () {
    // NSS and DIO0 are required, DIO1 is required for LoRa, DIO2 for FSK
    ASSERT(plmic_pins->nss != LMIC_UNUSED_PIN);
    ASSERT(plmic_pins->dio[0] != LMIC_UNUSED_PIN);
    ASSERT(plmic_pins->dio[1] != LMIC_UNUSED_PIN || plmic_pins->dio[2] != LMIC_UNUSED_PIN);

//    Serial.print("nss: "); Serial.println(plmic_pins->nss);
//    Serial.print("rst: "); Serial.println(plmic_pins->rst);
//    Serial.print("dio[0]: "); Serial.println(plmic_pins->dio[0]);
//    Serial.print("dio[1]: "); Serial.println(plmic_pins->dio[1]);
//    Serial.print("dio[2]: "); Serial.println(plmic_pins->dio[2]);

    pinMode(plmic_pins->nss, OUTPUT);
    if (plmic_pins->rxtx != LMIC_UNUSED_PIN)
        pinMode(plmic_pins->rxtx, OUTPUT);
    if (plmic_pins->rst != LMIC_UNUSED_PIN)
        pinMode(plmic_pins->rst, OUTPUT);

    hal_interrupt_init();
}

// val == 1  => tx
void hal_pin_rxtx (u1_t val) {
    if (plmic_pins->rxtx != LMIC_UNUSED_PIN)
        digitalWrite(plmic_pins->rxtx, val != plmic_pins->rxtx_rx_active);
}

// set radio RST pin to given value (or keep floating!)
void hal_pin_rst (u1_t val) {
    if (plmic_pins->rst == LMIC_UNUSED_PIN)
        return;

    if(val == 0 || val == 1) { // drive pin
        pinMode(plmic_pins->rst, OUTPUT);
        digitalWrite(plmic_pins->rst, val);
    } else { // keep pin floating
        pinMode(plmic_pins->rst, INPUT);
    }
}

s1_t hal_getRssiCal (void) {
    return plmic_pins->rssi_cal;
}

#if !defined(LMIC_USE_INTERRUPTS)
static void hal_interrupt_init() {
    pinMode(plmic_pins->dio[0], INPUT);
    if (plmic_pins->dio[1] != LMIC_UNUSED_PIN)
        pinMode(plmic_pins->dio[1], INPUT);
    if (plmic_pins->dio[2] != LMIC_UNUSED_PIN)
        pinMode(plmic_pins->dio[2], INPUT);
}

static bool dio_states[NUM_DIO] = {0};
static void hal_io_check() {
    uint8_t i;
    for (i = 0; i < NUM_DIO; ++i) {
        if (plmic_pins->dio[i] == LMIC_UNUSED_PIN)
            continue;

        if (dio_states[i] != digitalRead(plmic_pins->dio[i])) {
            dio_states[i] = !dio_states[i];
            if (dio_states[i])
                radio_irq_handler(i);
        }
    }
}

#else
// Interrupt handlers
static ostime_t interrupt_time[NUM_DIO] = {0};

static void hal_isrPin0() {
    ostime_t now = os_getTime();
    interrupt_time[0] = now ? now : 1;
}
static void hal_isrPin1() {
    ostime_t now = os_getTime();
    interrupt_time[1] = now ? now : 1;
}
static void hal_isrPin2() {
    ostime_t now = os_getTime();
    interrupt_time[2] = now ? now : 1;
}

typedef void (*isr_t)();
static isr_t interrupt_fns[NUM_DIO] = {hal_isrPin0, hal_isrPin1, hal_isrPin2};

static void hal_interrupt_init() {
  for (uint8_t i = 0; i < NUM_DIO; ++i) {
      if (plmic_pins->dio[i] == LMIC_UNUSED_PIN)
          continue;

      attachInterrupt(digitalPinToInterrupt(plmic_pins->dio[i]), interrupt_fns[i], RISING);
  }
}

static void hal_io_check() {
    uint8_t i;
    for (i = 0; i < NUM_DIO; ++i) {
	ostime_t iTime;
        if (plmic_pins->dio[i] == LMIC_UNUSED_PIN)
            continue;

	iTime = interrupt_time[i];
        if (iTime) {
            interrupt_time[i] = 0;
            radio_irq_handler_v2(i, iTime);
        }
    }
}
#endif // LMIC_USE_INTERRUPTS

// -----------------------------------------------------------------------------
// SPI

static void hal_spi_init () {
    SPI.begin();
}

void hal_pin_nss (u1_t val) {
    if (!val) {
        uint32_t spi_freq;

        if ((spi_freq = plmic_pins->spi_freq) == 0)
            spi_freq = LMIC_SPI_FREQ;

        SPISettings settings(spi_freq, MSBFIRST, SPI_MODE0);
        SPI.beginTransaction(settings);
    } else {
        SPI.endTransaction();
    }

    //Serial.println(val?">>":"<<");
    digitalWrite(plmic_pins->nss, val);
}

// perform SPI transaction with radio
u1_t hal_spi (u1_t out) {
    u1_t res = SPI.transfer(out);
/*
    Serial.print(">");
    Serial.print(out, HEX);
    Serial.print("<");
    Serial.println(res, HEX);
    */
    return res;
}

// -----------------------------------------------------------------------------
// TIME

static void hal_time_init () {
    // Nothing to do
}

u4_t hal_ticks () {
    // Because micros() is scaled down in this function, micros() will
    // overflow before the tick timer should, causing the tick timer to
    // miss a significant part of its values if not corrected. To fix
    // this, the "overflow" serves as an overflow area for the micros()
    // counter. It consists of three parts:
    //  - The US_PER_OSTICK upper bits are effectively an extension for
    //    the micros() counter and are added to the result of this
    //    function.
    //  - The next bit overlaps with the most significant bit of
    //    micros(). This is used to detect micros() overflows.
    //  - The remaining bits are always zero.
    //
    // By comparing the overlapping bit with the corresponding bit in
    // the micros() return value, overflows can be detected and the
    // upper bits are incremented. This is done using some clever
    // bitwise operations, to remove the need for comparisons and a
    // jumps, which should result in efficient code. By avoiding shifts
    // other than by multiples of 8 as much as possible, this is also
    // efficient on AVR (which only has 1-bit shifts).
    static uint8_t overflow = 0;

    // Scaled down timestamp. The top US_PER_OSTICK_EXPONENT bits are 0,
    // the others will be the lower bits of our return value.
    uint32_t scaled = micros() >> US_PER_OSTICK_EXPONENT;
    // Most significant byte of scaled
    uint8_t msb = scaled >> 24;
    // Mask pointing to the overlapping bit in msb and overflow.
    const uint8_t mask = (1 << (7 - US_PER_OSTICK_EXPONENT));
    // Update overflow. If the overlapping bit is different
    // between overflow and msb, it is added to the stored value,
    // so the overlapping bit becomes equal again and, if it changed
    // from 1 to 0, the upper bits are incremented.
    overflow += (msb ^ overflow) & mask;

    // Return the scaled value with the upper bits of stored added. The
    // overlapping bit will be equal and the lower bits will be 0, so
    // bitwise or is a no-op for them.
    return scaled | ((uint32_t)overflow << 24);

    // 0 leads to correct, but overly complex code (it could just return
    // micros() unmodified), 8 leaves no room for the overlapping bit.
    static_assert(US_PER_OSTICK_EXPONENT > 0 && US_PER_OSTICK_EXPONENT < 8, "Invalid US_PER_OSTICK_EXPONENT value");
}

// Returns the number of ticks until time. Negative values indicate that
// time has already passed.
static s4_t delta_time(u4_t time) {
    return (s4_t)(time - hal_ticks());
}

void hal_waitUntil (u4_t time) {
    s4_t delta = delta_time(time);
    // From delayMicroseconds docs: Currently, the largest value that
    // will produce an accurate delay is 16383.
    while (delta > (16000 / US_PER_OSTICK)) {
        delay(16);
        delta -= (16000 / US_PER_OSTICK);
    }
    if (delta > 0)
        delayMicroseconds(delta * US_PER_OSTICK);
}

// check and rewind for target time
u1_t hal_checkTimer (u4_t time) {
    // No need to schedule wakeup, since we're not sleeping
    return delta_time(time) <= 0;
}

static uint8_t irqlevel = 0;

void hal_disableIRQs () {
    noInterrupts();
    irqlevel++;
}

void hal_enableIRQs () {
    if(--irqlevel == 0) {
        interrupts();

        // Instead of using proper interrupts (which are a bit tricky
        // and/or not available on all pins on AVR), just poll the pin
        // values. Since os_runloop disables and re-enables interrupts,
        // putting this here makes sure we check at least once every
        // loop.
        //
        // As an additional bonus, this prevents the can of worms that
        // we would otherwise get for running SPI transfers inside ISRs
        hal_io_check();
    }
}

void hal_sleep () {
    // Not implemented
}

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

#if defined(LMIC_PRINTF_TO)
#if defined(ESP8266) || defined(ESP31B) || defined(ESP32)
//ESPXX specific PRINTF, only tested with ESP32 Haltec LoRA Wifi so far
static ssize_t uart_putchar (void *, const char *buf, size_t len) {
    return LMIC_PRINTF_TO.write((const uint8_t *)buf, len);
}

static cookie_io_functions_t functions =
 {
     .read = NULL,
     .write = uart_putchar,
     .seek = NULL,
     .close = NULL
 };

void hal_printf_init() {
    stdout = fopencookie(NULL, "w", functions);
}
#else // !defined(ESP8266) || defined(ESP31B) || defined(ESP32)
	 // all else, like AVR
static int uart_putchar (char c, FILE *)
{
    LMIC_PRINTF_TO.write(c) ;
    return 0 ;
}

void hal_printf_init() {
    // create a FILE structure to reference our UART output function
    static FILE uartout;
    memset(&uartout, 0, sizeof(uartout));

    // fill in the UART file descriptor with pointer to writer.
    fdev_setup_stream (&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE);

    // The uart is the standard output device STDOUT.
    stdout = &uartout ;
}

#endif // !defined(ESP8266) || defined(ESP31B) || defined(ESP32)
#endif // defined(LMIC_PRINTF_TO)

void hal_init (void) {
    hal_init_ex(&lmic_pins);
}

void hal_init_ex (const void *pContext) {
    plmic_pins = (const lmic_pinmap *)pContext;

    // configure radio I/O and interrupt handler
    hal_io_init();
    // configure radio SPI
    hal_spi_init();
    // configure timer and interrupt handler
    hal_time_init();
#if defined(LMIC_PRINTF_TO)
    // printf support
    hal_printf_init();
#endif
}

void hal_failed (const char *file, u2_t line) {
#if defined(LMIC_FAILURE_TO)
    LMIC_FAILURE_TO.println("FAILURE ");
    LMIC_FAILURE_TO.print(file);
    LMIC_FAILURE_TO.print(':');
    LMIC_FAILURE_TO.println(line);
    LMIC_FAILURE_TO.flush();
#endif
    hal_disableIRQs();
    while(1);
}
<commit_msg>Make LMIC_PRINTF_TO work on GCC<commit_after>/*******************************************************************************
 * Copyright (c) 2015 Matthijs Kooijman
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * This the HAL to run LMIC on top of the Arduino environment.
 *******************************************************************************/

#include <Arduino.h>
#include <SPI.h>
#include "../lmic.h"
#include "hal.h"
#include <stdio.h>

// -----------------------------------------------------------------------------
// I/O

static const lmic_pinmap *plmic_pins;

static void hal_interrupt_init(); // Fwd declaration

static void hal_io_init () {
    // NSS and DIO0 are required, DIO1 is required for LoRa, DIO2 for FSK
    ASSERT(plmic_pins->nss != LMIC_UNUSED_PIN);
    ASSERT(plmic_pins->dio[0] != LMIC_UNUSED_PIN);
    ASSERT(plmic_pins->dio[1] != LMIC_UNUSED_PIN || plmic_pins->dio[2] != LMIC_UNUSED_PIN);

//    Serial.print("nss: "); Serial.println(plmic_pins->nss);
//    Serial.print("rst: "); Serial.println(plmic_pins->rst);
//    Serial.print("dio[0]: "); Serial.println(plmic_pins->dio[0]);
//    Serial.print("dio[1]: "); Serial.println(plmic_pins->dio[1]);
//    Serial.print("dio[2]: "); Serial.println(plmic_pins->dio[2]);

    pinMode(plmic_pins->nss, OUTPUT);
    if (plmic_pins->rxtx != LMIC_UNUSED_PIN)
        pinMode(plmic_pins->rxtx, OUTPUT);
    if (plmic_pins->rst != LMIC_UNUSED_PIN)
        pinMode(plmic_pins->rst, OUTPUT);

    hal_interrupt_init();
}

// val == 1  => tx
void hal_pin_rxtx (u1_t val) {
    if (plmic_pins->rxtx != LMIC_UNUSED_PIN)
        digitalWrite(plmic_pins->rxtx, val != plmic_pins->rxtx_rx_active);
}

// set radio RST pin to given value (or keep floating!)
void hal_pin_rst (u1_t val) {
    if (plmic_pins->rst == LMIC_UNUSED_PIN)
        return;

    if(val == 0 || val == 1) { // drive pin
        pinMode(plmic_pins->rst, OUTPUT);
        digitalWrite(plmic_pins->rst, val);
    } else { // keep pin floating
        pinMode(plmic_pins->rst, INPUT);
    }
}

s1_t hal_getRssiCal (void) {
    return plmic_pins->rssi_cal;
}

#if !defined(LMIC_USE_INTERRUPTS)
static void hal_interrupt_init() {
    pinMode(plmic_pins->dio[0], INPUT);
    if (plmic_pins->dio[1] != LMIC_UNUSED_PIN)
        pinMode(plmic_pins->dio[1], INPUT);
    if (plmic_pins->dio[2] != LMIC_UNUSED_PIN)
        pinMode(plmic_pins->dio[2], INPUT);
}

static bool dio_states[NUM_DIO] = {0};
static void hal_io_check() {
    uint8_t i;
    for (i = 0; i < NUM_DIO; ++i) {
        if (plmic_pins->dio[i] == LMIC_UNUSED_PIN)
            continue;

        if (dio_states[i] != digitalRead(plmic_pins->dio[i])) {
            dio_states[i] = !dio_states[i];
            if (dio_states[i])
                radio_irq_handler(i);
        }
    }
}

#else
// Interrupt handlers
static ostime_t interrupt_time[NUM_DIO] = {0};

static void hal_isrPin0() {
    ostime_t now = os_getTime();
    interrupt_time[0] = now ? now : 1;
}
static void hal_isrPin1() {
    ostime_t now = os_getTime();
    interrupt_time[1] = now ? now : 1;
}
static void hal_isrPin2() {
    ostime_t now = os_getTime();
    interrupt_time[2] = now ? now : 1;
}

typedef void (*isr_t)();
static isr_t interrupt_fns[NUM_DIO] = {hal_isrPin0, hal_isrPin1, hal_isrPin2};

static void hal_interrupt_init() {
  for (uint8_t i = 0; i < NUM_DIO; ++i) {
      if (plmic_pins->dio[i] == LMIC_UNUSED_PIN)
          continue;

      attachInterrupt(digitalPinToInterrupt(plmic_pins->dio[i]), interrupt_fns[i], RISING);
  }
}

static void hal_io_check() {
    uint8_t i;
    for (i = 0; i < NUM_DIO; ++i) {
	ostime_t iTime;
        if (plmic_pins->dio[i] == LMIC_UNUSED_PIN)
            continue;

	iTime = interrupt_time[i];
        if (iTime) {
            interrupt_time[i] = 0;
            radio_irq_handler_v2(i, iTime);
        }
    }
}
#endif // LMIC_USE_INTERRUPTS

// -----------------------------------------------------------------------------
// SPI

static void hal_spi_init () {
    SPI.begin();
}

void hal_pin_nss (u1_t val) {
    if (!val) {
        uint32_t spi_freq;

        if ((spi_freq = plmic_pins->spi_freq) == 0)
            spi_freq = LMIC_SPI_FREQ;

        SPISettings settings(spi_freq, MSBFIRST, SPI_MODE0);
        SPI.beginTransaction(settings);
    } else {
        SPI.endTransaction();
    }

    //Serial.println(val?">>":"<<");
    digitalWrite(plmic_pins->nss, val);
}

// perform SPI transaction with radio
u1_t hal_spi (u1_t out) {
    u1_t res = SPI.transfer(out);
/*
    Serial.print(">");
    Serial.print(out, HEX);
    Serial.print("<");
    Serial.println(res, HEX);
    */
    return res;
}

// -----------------------------------------------------------------------------
// TIME

static void hal_time_init () {
    // Nothing to do
}

u4_t hal_ticks () {
    // Because micros() is scaled down in this function, micros() will
    // overflow before the tick timer should, causing the tick timer to
    // miss a significant part of its values if not corrected. To fix
    // this, the "overflow" serves as an overflow area for the micros()
    // counter. It consists of three parts:
    //  - The US_PER_OSTICK upper bits are effectively an extension for
    //    the micros() counter and are added to the result of this
    //    function.
    //  - The next bit overlaps with the most significant bit of
    //    micros(). This is used to detect micros() overflows.
    //  - The remaining bits are always zero.
    //
    // By comparing the overlapping bit with the corresponding bit in
    // the micros() return value, overflows can be detected and the
    // upper bits are incremented. This is done using some clever
    // bitwise operations, to remove the need for comparisons and a
    // jumps, which should result in efficient code. By avoiding shifts
    // other than by multiples of 8 as much as possible, this is also
    // efficient on AVR (which only has 1-bit shifts).
    static uint8_t overflow = 0;

    // Scaled down timestamp. The top US_PER_OSTICK_EXPONENT bits are 0,
    // the others will be the lower bits of our return value.
    uint32_t scaled = micros() >> US_PER_OSTICK_EXPONENT;
    // Most significant byte of scaled
    uint8_t msb = scaled >> 24;
    // Mask pointing to the overlapping bit in msb and overflow.
    const uint8_t mask = (1 << (7 - US_PER_OSTICK_EXPONENT));
    // Update overflow. If the overlapping bit is different
    // between overflow and msb, it is added to the stored value,
    // so the overlapping bit becomes equal again and, if it changed
    // from 1 to 0, the upper bits are incremented.
    overflow += (msb ^ overflow) & mask;

    // Return the scaled value with the upper bits of stored added. The
    // overlapping bit will be equal and the lower bits will be 0, so
    // bitwise or is a no-op for them.
    return scaled | ((uint32_t)overflow << 24);

    // 0 leads to correct, but overly complex code (it could just return
    // micros() unmodified), 8 leaves no room for the overlapping bit.
    static_assert(US_PER_OSTICK_EXPONENT > 0 && US_PER_OSTICK_EXPONENT < 8, "Invalid US_PER_OSTICK_EXPONENT value");
}

// Returns the number of ticks until time. Negative values indicate that
// time has already passed.
static s4_t delta_time(u4_t time) {
    return (s4_t)(time - hal_ticks());
}

void hal_waitUntil (u4_t time) {
    s4_t delta = delta_time(time);
    // From delayMicroseconds docs: Currently, the largest value that
    // will produce an accurate delay is 16383.
    while (delta > (16000 / US_PER_OSTICK)) {
        delay(16);
        delta -= (16000 / US_PER_OSTICK);
    }
    if (delta > 0)
        delayMicroseconds(delta * US_PER_OSTICK);
}

// check and rewind for target time
u1_t hal_checkTimer (u4_t time) {
    // No need to schedule wakeup, since we're not sleeping
    return delta_time(time) <= 0;
}

static uint8_t irqlevel = 0;

void hal_disableIRQs () {
    noInterrupts();
    irqlevel++;
}

void hal_enableIRQs () {
    if(--irqlevel == 0) {
        interrupts();

        // Instead of using proper interrupts (which are a bit tricky
        // and/or not available on all pins on AVR), just poll the pin
        // values. Since os_runloop disables and re-enables interrupts,
        // putting this here makes sure we check at least once every
        // loop.
        //
        // As an additional bonus, this prevents the can of worms that
        // we would otherwise get for running SPI transfers inside ISRs
        hal_io_check();
    }
}

void hal_sleep () {
    // Not implemented
}

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

#if defined(LMIC_PRINTF_TO)
#if !defined(__AVR)
static ssize_t uart_putchar (void *, const char *buf, size_t len) {
    return LMIC_PRINTF_TO.write((const uint8_t *)buf, len);
}

static cookie_io_functions_t functions =
 {
     .read = NULL,
     .write = uart_putchar,
     .seek = NULL,
     .close = NULL
 };

void hal_printf_init() {
    stdout = fopencookie(NULL, "w", functions);
    if (stdout != nullptr) {
	setvbuf(stdout, NULL, _IONBF, 0);
    }
}
#else // defined(__AVR)
static int uart_putchar (char c, FILE *)
{
    LMIC_PRINTF_TO.write(c) ;
    return 0 ;
}

void hal_printf_init() {
    // create a FILE structure to reference our UART output function
    static FILE uartout;
    memset(&uartout, 0, sizeof(uartout));

    // fill in the UART file descriptor with pointer to writer.
    fdev_setup_stream (&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE);

    // The uart is the standard output device STDOUT.
    stdout = &uartout ;
}

#endif // !defined(ESP8266) || defined(ESP31B) || defined(ESP32)
#endif // defined(LMIC_PRINTF_TO)

void hal_init (void) {
    hal_init_ex(&lmic_pins);
}

void hal_init_ex (const void *pContext) {
    plmic_pins = (const lmic_pinmap *)pContext;

    // configure radio I/O and interrupt handler
    hal_io_init();
    // configure radio SPI
    hal_spi_init();
    // configure timer and interrupt handler
    hal_time_init();
#if defined(LMIC_PRINTF_TO)
    // printf support
    hal_printf_init();
#endif
}

void hal_failed (const char *file, u2_t line) {
#if defined(LMIC_FAILURE_TO)
    LMIC_FAILURE_TO.println("FAILURE ");
    LMIC_FAILURE_TO.print(file);
    LMIC_FAILURE_TO.print(':');
    LMIC_FAILURE_TO.println(line);
    LMIC_FAILURE_TO.flush();
#endif
    hal_disableIRQs();
    while(1);
}
<|endoftext|>


<commit_before>//=======================================================================
// Copyright (c) 2014 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include <iostream>
#include <chrono>
#include <random>

#include "etl/fast_vector.hpp"
#include "etl/fast_matrix.hpp"

typedef std::chrono::high_resolution_clock timer_clock;
typedef std::chrono::milliseconds milliseconds;
typedef std::chrono::microseconds microseconds;

namespace {

template<typename T>
void randomize_double(T& container){
    static std::default_random_engine rand_engine(std::time(nullptr));
    static std::uniform_real_distribution<double> real_distribution(-1000.0, 1000.0);
    static auto generator = std::bind(real_distribution, rand_engine);

    for(auto& v : container){
        v = generator();
    }
}

template<typename T1>
void randomize(T1& container){
    randomize_double(container);
}

template<typename T1, typename... TT>
void randomize(T1& container, TT&... containers){
    randomize_double(container);
    randomize(containers...);
}

template<typename Functor, typename... T>
void measure(const std::string& title, Functor&& functor, T&... references){
    for(std::size_t i = 0; i < 100; ++i){
        randomize(references...);
        functor();
    }

    std::size_t duration_acc = 0;

    for(std::size_t i = 0; i < 1000; ++i){
        randomize(references...);
        auto start_time = timer_clock::now();
        functor();
        auto end_time = timer_clock::now();
        auto duration = std::chrono::duration_cast<microseconds>(end_time - start_time);
        duration_acc += duration.count();
    }

    std::cout << title << " took " << duration_acc << "us" << std::endl;
}

} //end of anonymous namespace

etl::fast_vector<double, 4096> double_vector_a;
etl::fast_vector<double, 4096> double_vector_b;
etl::fast_vector<double, 4096> double_vector_c;

etl::fast_matrix<double, 16, 256> double_matrix_a;
etl::fast_matrix<double, 16, 256> double_matrix_b;
etl::fast_matrix<double, 16, 256> double_matrix_c;

etl::fast_matrix<double, 256, 128> double_matrix_d;
etl::fast_matrix<double, 256, 128> double_matrix_e;
etl::fast_matrix<double, 256, 128> double_matrix_f;

int main(){
    measure("fast_vector_simple(4096)", [](){
        double_vector_c = 3.5 * double_vector_a + etl::sigmoid(1.0 + double_vector_b);
    }, double_vector_a, double_vector_b);

    measure("fast_matrix_simple(16,256)(4096)", [](){
        double_matrix_c = 3.5 * double_matrix_a + etl::sigmoid(1.0 + double_matrix_b);
    }, double_matrix_a, double_matrix_b);

    measure("fast_matrix_simple(256,128)", [](){
        double_matrix_f = 3.5 * double_matrix_d + etl::sigmoid(1.0 + double_matrix_e);
    }, double_matrix_d, double_matrix_e);

    return 0;
}
<commit_msg>Cleanup<commit_after>//=======================================================================
// Copyright (c) 2014 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include <iostream>
#include <chrono>
#include <random>

#include "etl/fast_vector.hpp"
#include "etl/fast_matrix.hpp"

typedef std::chrono::high_resolution_clock timer_clock;
typedef std::chrono::milliseconds milliseconds;
typedef std::chrono::microseconds microseconds;

namespace {

template<typename T>
void randomize_double(T& container){
    static std::default_random_engine rand_engine(std::time(nullptr));
    static std::uniform_real_distribution<double> real_distribution(-1000.0, 1000.0);
    static auto generator = std::bind(real_distribution, rand_engine);

    for(auto& v : container){
        v = generator();
    }
}

template<typename T1>
void randomize(T1& container){
    randomize_double(container);
}

template<typename T1, typename... TT>
void randomize(T1& container, TT&... containers){
    randomize_double(container);
    randomize(containers...);
}

std::string duration_str(std::size_t duration_us){
    double duration = duration_us;

    if(duration > 1000){
        return std::to_string(duration / 1000.0) + "ms";
    } else {
        return std::to_string(duration_us) + "us";
    }
}

template<typename Functor, typename... T>
void measure(const std::string& title, const std::string& reference, Functor&& functor, T&... references){
    for(std::size_t i = 0; i < 100; ++i){
        randomize(references...);
        functor();
    }

    std::size_t duration_acc = 0;

    for(std::size_t i = 0; i < 1000; ++i){
        randomize(references...);
        auto start_time = timer_clock::now();
        functor();
        auto end_time = timer_clock::now();
        auto duration = std::chrono::duration_cast<microseconds>(end_time - start_time);
        duration_acc += duration.count();
    }

    std::cout << title << " took " << duration_str(duration_acc) << " (reference: " << reference << ")" << std::endl;
}

} //end of anonymous namespace

etl::fast_vector<double, 4096> double_vector_a;
etl::fast_vector<double, 4096> double_vector_b;
etl::fast_vector<double, 4096> double_vector_c;

etl::fast_matrix<double, 16, 256> double_matrix_a;
etl::fast_matrix<double, 16, 256> double_matrix_b;
etl::fast_matrix<double, 16, 256> double_matrix_c;

etl::fast_matrix<double, 256, 128> double_matrix_d;
etl::fast_matrix<double, 256, 128> double_matrix_e;
etl::fast_matrix<double, 256, 128> double_matrix_f;

int main(){
    measure("fast_vector_simple(4096)", "72ms", [](){
        double_vector_c = 3.5 * double_vector_a + etl::sigmoid(1.0 + double_vector_b);
    }, double_vector_a, double_vector_b);

    measure("fast_matrix_simple(16,256)(4096)", "72ms", [](){
        double_matrix_c = 3.5 * double_matrix_a + etl::sigmoid(1.0 + double_matrix_b);
    }, double_matrix_a, double_matrix_b);

    measure("fast_matrix_simple(256,128)", "580ms", [](){
        double_matrix_f = 3.5 * double_matrix_d + etl::sigmoid(1.0 + double_matrix_e);
    }, double_matrix_d, double_matrix_e);

    return 0;
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Kaveh Vahedipour
////////////////////////////////////////////////////////////////////////////////

#include "Inception.h"

#include "Agency/Agent.h"
#include "Agency/GossipCallback.h"
#include "Basics/ConditionLocker.h"

#include <chrono>
#include <thread>

using namespace arangodb::consensus;

Inception::Inception() : Thread("Inception"), _agent(nullptr) {}

Inception::Inception(Agent* agent) : Thread("Inception"), _agent(agent) {}

// Shutdown if not already
Inception::~Inception() { shutdown(); }

/// Gossip to others
/// - Get snapshot of gossip peers and agent pool
/// - Create outgoing gossip.
/// - Send to all peers

void Inception::gossip() {
  auto s = std::chrono::system_clock::now();
  std::chrono::seconds timeout(120);
  size_t i = 0;

  CONDITION_LOCKER(guard, _cv);

  while (!this->isStopping() && !_agent->isStopping()) {
    config_t config = _agent->config();  // get a copy of conf

    query_t out = std::make_shared<Builder>();
    out->openObject();
    out->add("endpoint", VPackValue(config.endpoint()));
    out->add("id", VPackValue(config.id()));
    out->add("pool", VPackValue(VPackValueType::Object));
    for (auto const& i : config.pool()) {
      out->add(i.first, VPackValue(i.second));
    }
    out->close();
    out->close();

    std::string path = "/_api/agency_priv/gossip";

    for (auto const& p : config.gossipPeers()) { // gossip peers
      if (p != config.endpoint()) {
        std::string clientid = config.id() + std::to_string(i++);
        auto hf =
            std::make_unique<std::unordered_map<std::string, std::string>>();
        arangodb::ClusterComm::instance()->asyncRequest(
          clientid, 1, p, rest::RequestType::POST, path,
          std::make_shared<std::string>(out->toJson()), hf,
          std::make_shared<GossipCallback>(_agent), 1.0, true);
      }
    }

    for (auto const& pair : config.pool()) { // pool entries
      if (pair.second != config.endpoint()) {
        std::string clientid = config.id() + std::to_string(i++);
        auto hf =
            std::make_unique<std::unordered_map<std::string, std::string>>();
        arangodb::ClusterComm::instance()->asyncRequest(
          clientid, 1, pair.second, rest::RequestType::POST, path,
          std::make_shared<std::string>(out->toJson()), hf,
          std::make_shared<GossipCallback>(_agent), 1.0, true);
      }
    }

    _cv.wait(100000);

    if ((std::chrono::system_clock::now() - s) > timeout) {
      if (config.poolComplete()) {
        LOG_TOPIC(DEBUG, Logger::AGENCY) << "Stopping active gossipping!";
      } else {
        LOG_TOPIC(ERR, Logger::AGENCY)
            << "Failed to find complete pool of agents. Giving up!";
      }
      break;
    }

    if (config.poolComplete()) {
      _agent->startConstituent();
      break;
    }
  }
}

void Inception::activeAgency() {  // Do we have an active agency?

  auto config = _agent->config();
  std::string const path = "/api/agency_priv/activeAgency";

  if (config.poolComplete()) {
    for (auto const& pair : config.pool()) { // pool entries
      if (pair.first != config.id()) {
        std::string clientId = config.id();
        //      auto comres = syncRequest(
        //  clientId, 1, pair.second, rest::RequestType::POST, path, out->toJson(),
        //  std::unordered_map<std::string, std::string>(), 10.0);
      }
    }
  }

}

void Inception::run() {
  //activeAgency();

  config_t config = _agent->config();
  if (!config.poolComplete()) {
    gossip();
  }
}

void Inception::beginShutdown() {
  Thread::beginShutdown();
  CONDITION_LOCKER(guard, _cv);
  guard.broadcast();
}
<commit_msg>semi-fix for long delays in multi-host-agency collection creation/drop<commit_after>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Kaveh Vahedipour
////////////////////////////////////////////////////////////////////////////////

#include "Inception.h"

#include "Agency/Agent.h"
#include "Agency/GossipCallback.h"
#include "Basics/ConditionLocker.h"
#include "Cluster/ClusterComm.h"

#include <chrono>
#include <thread>

using namespace arangodb::consensus;

Inception::Inception() : Thread("Inception"), _agent(nullptr) {}

Inception::Inception(Agent* agent) : Thread("Inception"), _agent(agent) {}

// Shutdown if not already
Inception::~Inception() { shutdown(); }

/// Gossip to others
/// - Get snapshot of gossip peers and agent pool
/// - Create outgoing gossip.
/// - Send to all peers

void Inception::gossip() {
  auto s = std::chrono::system_clock::now();
  std::chrono::seconds timeout(120);
  size_t i = 0;

  CONDITION_LOCKER(guard, _cv);

  while (!this->isStopping() && !_agent->isStopping()) {
    config_t config = _agent->config();  // get a copy of conf

    query_t out = std::make_shared<Builder>();
    out->openObject();
    out->add("endpoint", VPackValue(config.endpoint()));
    out->add("id", VPackValue(config.id()));
    out->add("pool", VPackValue(VPackValueType::Object));
    for (auto const& i : config.pool()) {
      out->add(i.first, VPackValue(i.second));
    }
    out->close();
    out->close();

    std::string path = "/_api/agency_priv/gossip";

    for (auto const& p : config.gossipPeers()) { // gossip peers
      if (p != config.endpoint()) {
        std::string clientid = config.id() + std::to_string(i++);
        auto hf =
            std::make_unique<std::unordered_map<std::string, std::string>>();
        arangodb::ClusterComm::instance()->asyncRequest(
          clientid, 1, p, rest::RequestType::POST, path,
          std::make_shared<std::string>(out->toJson()), hf,
          std::make_shared<GossipCallback>(_agent), 1.0, true);
      }
    }

    for (auto const& pair : config.pool()) { // pool entries
      if (pair.second != config.endpoint()) {
        std::string clientid = config.id() + std::to_string(i++);
        auto hf =
            std::make_unique<std::unordered_map<std::string, std::string>>();
        arangodb::ClusterComm::instance()->asyncRequest(
          clientid, 1, pair.second, rest::RequestType::POST, path,
          std::make_shared<std::string>(out->toJson()), hf,
          std::make_shared<GossipCallback>(_agent), 1.0, true);
      }
    }

    _cv.wait(100000);

    if ((std::chrono::system_clock::now() - s) > timeout) {
      if (config.poolComplete()) {
        LOG_TOPIC(DEBUG, Logger::AGENCY) << "Stopping active gossipping!";
      } else {
        LOG_TOPIC(ERR, Logger::AGENCY)
            << "Failed to find complete pool of agents. Giving up!";
      }
      break;
    }

    if (config.poolComplete()) {
      _agent->startConstituent();
      break;
    }
  }
}

void Inception::activeAgency() {  // Do we have an active agency?

  auto config = _agent->config();
  std::string const path = "/api/agency_priv/activeAgency";
  auto out = std::make_shared<Builder>();

  if (config.poolComplete()) {
    for (auto const& pair : config.pool()) { // pool entries
      if (pair.first != config.id()) {
        std::string clientId = config.id();
        auto comres = arangodb::ClusterComm::instance()->syncRequest(
          clientId, 1, pair.second, rest::RequestType::POST, path, out->toJson(),
          std::unordered_map<std::string, std::string>(), 10.0);
      }
    }
  }

}

void Inception::run() {
  //activeAgency();

  config_t config = _agent->config();
  if (!config.poolComplete()) {
    gossip();
  }
}

void Inception::beginShutdown() {
  Thread::beginShutdown();
  CONDITION_LOCKER(guard, _cv);
  guard.broadcast();
}
<|endoftext|>