<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 "Agency/MeasureCallback.h"
#include "Basics/ConditionLocker.h"
#include "Cluster/ClusterComm.h"

#include <chrono>
#include <numeric>
#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

    // Build gossip message
    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 = privApiPrefix + "gossip";

    // gossip peers
    for (auto const& p : config.gossipPeers()) {
      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, 0.5);
      }
    }

    // pool entries
    for (auto const& pair : config.pool()) {
      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, 0.5);
      }
    }

    // don't panic
    _cv.wait(100000);

    // Timed out? :(
    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;
    }

    // We're done
    if (config.poolComplete()) {
      _agent->startConstituent();
      break;
    }

  }

}

// @brief Active agency from persisted database
bool Inception::activeAgencyFromPersistence() {

  auto myConfig = _agent->config();
  std::string const path = pubApiPrefix + "config";

  // Can only be done responcibly, if we are complete
  if (myConfig.poolComplete()) {

    // Contact hosts on pool in hopes of finding a leader Id
    for (auto const& pair : myConfig.pool()) {

      if (pair.first != myConfig.id()) {

        auto comres = arangodb::ClusterComm::instance()->syncRequest(
          myConfig.id(), 1, pair.second, rest::RequestType::GET, path,
          std::string(), std::unordered_map<std::string, std::string>(), 1.0);

        if (comres->status == CL_COMM_SENT) {

          auto body = comres->result->getBodyVelocyPack();
          auto theirConfig = body->slice();

          std::string leaderId;

          // LeaderId in configuration?
          try {
            leaderId = theirConfig.get("leaderId").copyString();
          } catch (std::exception const& e) {
            LOG_TOPIC(DEBUG, Logger::AGENCY)
              << "Failed to get leaderId from" << pair.second << ": "
              << e.what();
          }

          if (leaderId != "") { // Got leaderId. Let's get do it.

            try {
              LOG_TOPIC(DEBUG, Logger::AGENCY)
                << "Found active agency with leader " << leaderId
                << " at endpoint "
                << theirConfig.get("configuration").get(
                  "pool").get(leaderId).copyString();
            } catch (std::exception const& e) {
              LOG_TOPIC(DEBUG, Logger::AGENCY)
                << "Failed to get leaderId from" << pair.second << ": "
                << e.what();
            }

            auto agency = std::make_shared<Builder>();
            agency->openObject();
            agency->add("term", theirConfig.get("term"));
            agency->add("id", VPackValue(leaderId));
            agency->add("active", theirConfig.get("configuration").get("active"));
            agency->add("pool", theirConfig.get("configuration").get("pool"));
            agency->close();
            _agent->notify(agency);

            return true;

          } else { // No leaderId. Move on.

            LOG_TOPIC(DEBUG, Logger::AGENCY)
              << "Failed to get leaderId from" << pair.second;

          }

        }

      }

    }
  }

  return false;

}

bool Inception::restartingActiveAgent() {

  auto myConfig = _agent->config();
  std::string const path = pubApiPrefix + "config";

  auto s = std::chrono::system_clock::now();
  std::chrono::seconds timeout(60);

  // Can only be done responcibly, if we are complete
  if (myConfig.poolComplete()) {

    auto pool = myConfig.pool();
    auto active = myConfig.active();

    CONDITION_LOCKER(guard, _cv);

    while (!this->isStopping() && !_agent->isStopping()) {

      active.erase(
        std::remove(active.begin(), active.end(), myConfig.id()), active.end());
      active.erase(
        std::remove(active.begin(), active.end(), ""), active.end());

      if (active.empty()) {
        return true;
      }

      for (auto& i : active) {

        if (i != myConfig.id() && i != "") {

          auto clientId = myConfig.id();
          auto comres   = arangodb::ClusterComm::instance()->syncRequest(
            clientId, 1, pool.at(i), rest::RequestType::GET, path, std::string(),
            std::unordered_map<std::string, std::string>(), 2.0);

          if (comres->status == CL_COMM_SENT) {

            try {

              auto theirActive = comres->result->getBodyVelocyPack()->
                slice().get("configuration").get("active").toJson();
              auto myActive = myConfig.activeToBuilder()->toJson();

              if (theirActive != myActive) {
                LOG_TOPIC(FATAL, Logger::AGENCY)
                  << "Assumed active RAFT peer and I disagree on active membership."
                  << "Administrative intervention needed.";
                FATAL_ERROR_EXIT();
                return false;
              } else {
                i = "";
              }

            } catch (std::exception const& e) {
              LOG_TOPIC(FATAL, Logger::AGENCY)
                << "Assumed active RAFT peer has no active agency list: " << e.what()
                << "Administrative intervention needed.";
              FATAL_ERROR_EXIT();
              return false;
            }
          }
        }

      }

      // Timed out? :(
      if ((std::chrono::system_clock::now() - s) > timeout) {
        if (myConfig.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;
      }

      _cv.wait(500000);

    }
  }

  return false;

}

inline static int64_t timeStamp() {
  using namespace std::chrono;
  return duration_cast<microseconds>(
    steady_clock::now().time_since_epoch()).count();
}

void Inception::reportIn(std::string const& peerId, uint64_t start) {
  MUTEX_LOCKER(lock, _pLock);
  _pings.push_back(1.0e-3*(double)(timeStamp()-start));
}

void Inception::reportIn(query_t const& query) {

  VPackSlice slice = query->slice();

  TRI_ASSERT(slice.isObject());
  TRI_ASSERT(slice.hasKey("mean"));
  TRI_ASSERT(slice.hasKey("stdev"));
  TRI_ASSERT(slice.hasKey("min"));
  TRI_ASSERT(slice.hasKey("max"));

  MUTEX_LOCKER(lock, _mLock);
  _measurements.push_back(
    std::vector<double>(
      {slice.get("mean").getDouble(), slice.get("stdev").getDouble(),
          slice.get("max").getDouble(), slice.get("min").getDouble()} ));

}

bool Inception::estimateRAFTInterval() {

  using namespace std::chrono;

  std::string path("/_api/agency/config");
  auto pool = _agent->config().pool();
  auto myid = _agent->id();

  for (size_t i = 0; i < 25; ++i) {
    for (auto const& peer : pool) {
      if (peer.first != myid) {
        std::string clientid = peer.first + std::to_string(i);
        auto hf =
          std::make_unique<std::unordered_map<std::string, std::string>>();
        arangodb::ClusterComm::instance()->asyncRequest(
          clientid, 1, peer.second, rest::RequestType::GET, path,
          std::make_shared<std::string>(), hf,
          std::make_shared<MeasureCallback>(this, peer.second, timeStamp()),
          2.0, true);
      }
    }
    std::this_thread::sleep_for(std::chrono::duration<double,std::milli>(5));
  }

  auto s = system_clock::now();
  seconds timeout(3);

  CONDITION_LOCKER(guard, _cv);

  while (true) {

    _cv.wait(50000);

    {
      MUTEX_LOCKER(lock, _pLock);
      if (_pings.size() == 25*(pool.size()-1)) {
        LOG_TOPIC(DEBUG, Logger::AGENCY) << "All pings are in";
        break;
      }
    }

    if ((system_clock::now() - s) > timeout) {
      LOG_TOPIC(DEBUG, Logger::AGENCY) << "Timed out waiting for pings";
      break;
    }

  }

  if (! _pings.empty()) {

    double mean, stdev, mx, mn;

    MUTEX_LOCKER(lock, _pLock);
    size_t num = _pings.size();
    mean   = std::accumulate(_pings.begin(), _pings.end(), 0.0) / num;
    mx     = *std::max_element(_pings.begin(), _pings.end());
    mn     = *std::min_element(_pings.begin(), _pings.end());
    std::transform(_pings.begin(), _pings.end(), _pings.begin(),
                   std::bind2nd(std::minus<double>(), mean));
    stdev =
      std::sqrt(
        std::inner_product(
          _pings.begin(), _pings.end(), _pings.begin(), 0.0) / num);

    LOG_TOPIC(DEBUG, Logger::AGENCY)
      << "mean(" << mean << ") stdev(" << stdev<< ")";

    Builder measurement;
    measurement.openObject();
    measurement.add("mean", VPackValue(mean));
    measurement.add("stdev", VPackValue(stdev));
    measurement.add("min", VPackValue(mn));
    measurement.add("max", VPackValue(mx));
    measurement.close();
    std::string measjson = measurement.toJson();

    path = privApiPrefix + "measure";
    for (auto const& peer : pool) {
      if (peer.first != myid) {
        auto clientId = "1";
        auto comres   = arangodb::ClusterComm::instance()->syncRequest(
          clientId, 1, peer.second, rest::RequestType::POST, path,
          measjson, std::unordered_map<std::string, std::string>(), 2.0);
      }
    }

    {
      MUTEX_LOCKER(lock, _mLock);
      _measurements.push_back(std::vector<double>({mean, stdev, mx, mn}));
    }
    s = system_clock::now();
    while (true) {

      _cv.wait(50000);

      {
        MUTEX_LOCKER(lock, _mLock);
        if (_measurements.size() == pool.size()) {
          LOG_TOPIC(DEBUG, Logger::AGENCY) << "All measurements are in";
          break;
        }
      }

      if ((system_clock::now() - s) > timeout) {
        LOG_TOPIC(WARN, Logger::AGENCY)
          << "Timed out waiting for other measurements. Auto-adaptation failed! Will stick to command line arguments";
        return false;
      }

    }

    double maxmean  = .0;
    double maxstdev = .0;
    for (auto const& meas : _measurements) {
      if (maxmean < meas[0]) {
        maxmean = meas[0];
      }
      if (maxstdev < meas[1]) {
        maxstdev = meas[1];
      }
    }

    maxmean = 1.e-3*std::ceil(1.e3*(.1 + 1.0e-3*(maxmean+maxstdev)));

    LOG_TOPIC(INFO, Logger::AGENCY)
      << "Auto-adapting RAFT timing to: {" << maxmean
      << ", " << 5.0*maxmean << "}s";

    _agent->resetRAFTTimes(maxmean, 5.0*maxmean);

  }

  return true;

}

// @brief Active agency from persisted database
bool Inception::activeAgencyFromCommandLine() {
  return false;
}

// @brief Thread main
void Inception::run() {

  // 1. If active agency, do as you're told
  if (activeAgencyFromPersistence()) {
    _agent->ready(true);
  }

  // 2. If we think that we used to be active agent
  if (!_agent->ready() && restartingActiveAgent()) {
    _agent->ready(true);
  }

  // 3. Else gossip
  config_t config = _agent->config();
  if (!_agent->ready() && !config.poolComplete()) {
    gossip();
  }

  // 4. If still incomplete bail out :(
  config = _agent->config();
  if (!_agent->ready() && !config.poolComplete()) {
    LOG_TOPIC(FATAL, Logger::AGENCY)
      << "Failed to build environment for RAFT algorithm. Bailing out!";
    FATAL_ERROR_EXIT();
  }

  estimateRAFTInterval();

  _agent->ready(true);

}

// @brief Graceful shutdown
void Inception::beginShutdown() {
  Thread::beginShutdown();
  CONDITION_LOCKER(guard, _cv);
  guard.broadcast();
}
<commit_msg>well tested auto adaptation<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 "Agency/MeasureCallback.h"
#include "Basics/ConditionLocker.h"
#include "Cluster/ClusterComm.h"

#include <chrono>
#include <numeric>
#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

    // Build gossip message
    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 = privApiPrefix + "gossip";

    // gossip peers
    for (auto const& p : config.gossipPeers()) {
      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, 0.5);
      }
    }

    // pool entries
    for (auto const& pair : config.pool()) {
      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, 0.5);
      }
    }

    // don't panic
    _cv.wait(100000);

    // Timed out? :(
    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;
    }

    // We're done
    if (config.poolComplete()) {
      _agent->startConstituent();
      break;
    }

  }

}

// @brief Active agency from persisted database
bool Inception::activeAgencyFromPersistence() {

  auto myConfig = _agent->config();
  std::string const path = pubApiPrefix + "config";

  // Can only be done responcibly, if we are complete
  if (myConfig.poolComplete()) {

    // Contact hosts on pool in hopes of finding a leader Id
    for (auto const& pair : myConfig.pool()) {

      if (pair.first != myConfig.id()) {

        auto comres = arangodb::ClusterComm::instance()->syncRequest(
          myConfig.id(), 1, pair.second, rest::RequestType::GET, path,
          std::string(), std::unordered_map<std::string, std::string>(), 1.0);

        if (comres->status == CL_COMM_SENT) {

          auto body = comres->result->getBodyVelocyPack();
          auto theirConfig = body->slice();

          std::string leaderId;

          // LeaderId in configuration?
          try {
            leaderId = theirConfig.get("leaderId").copyString();
          } catch (std::exception const& e) {
            LOG_TOPIC(DEBUG, Logger::AGENCY)
              << "Failed to get leaderId from" << pair.second << ": "
              << e.what();
          }

          if (leaderId != "") { // Got leaderId. Let's get do it.

            try {
              LOG_TOPIC(DEBUG, Logger::AGENCY)
                << "Found active agency with leader " << leaderId
                << " at endpoint "
                << theirConfig.get("configuration").get(
                  "pool").get(leaderId).copyString();
            } catch (std::exception const& e) {
              LOG_TOPIC(DEBUG, Logger::AGENCY)
                << "Failed to get leaderId from" << pair.second << ": "
                << e.what();
            }

            auto agency = std::make_shared<Builder>();
            agency->openObject();
            agency->add("term", theirConfig.get("term"));
            agency->add("id", VPackValue(leaderId));
            agency->add("active", theirConfig.get("configuration").get("active"));
            agency->add("pool", theirConfig.get("configuration").get("pool"));
            agency->close();
            _agent->notify(agency);

            return true;

          } else { // No leaderId. Move on.

            LOG_TOPIC(DEBUG, Logger::AGENCY)
              << "Failed to get leaderId from" << pair.second;

          }

        }

      }

    }
  }

  return false;

}

bool Inception::restartingActiveAgent() {

  auto myConfig = _agent->config();
  std::string const path = pubApiPrefix + "config";

  auto s = std::chrono::system_clock::now();
  std::chrono::seconds timeout(60);

  // Can only be done responcibly, if we are complete
  if (myConfig.poolComplete()) {

    auto pool = myConfig.pool();
    auto active = myConfig.active();

    CONDITION_LOCKER(guard, _cv);

    while (!this->isStopping() && !_agent->isStopping()) {

      active.erase(
        std::remove(active.begin(), active.end(), myConfig.id()), active.end());
      active.erase(
        std::remove(active.begin(), active.end(), ""), active.end());

      if (active.empty()) {
        return true;
      }

      for (auto& i : active) {

        if (i != myConfig.id() && i != "") {

          auto clientId = myConfig.id();
          auto comres   = arangodb::ClusterComm::instance()->syncRequest(
            clientId, 1, pool.at(i), rest::RequestType::GET, path, std::string(),
            std::unordered_map<std::string, std::string>(), 2.0);

          if (comres->status == CL_COMM_SENT) {

            try {

              auto theirActive = comres->result->getBodyVelocyPack()->
                slice().get("configuration").get("active").toJson();
              auto myActive = myConfig.activeToBuilder()->toJson();

              if (theirActive != myActive) {
                LOG_TOPIC(FATAL, Logger::AGENCY)
                  << "Assumed active RAFT peer and I disagree on active membership."
                  << "Administrative intervention needed.";
                FATAL_ERROR_EXIT();
                return false;
              } else {
                i = "";
              }

            } catch (std::exception const& e) {
              LOG_TOPIC(FATAL, Logger::AGENCY)
                << "Assumed active RAFT peer has no active agency list: " << e.what()
                << "Administrative intervention needed.";
              FATAL_ERROR_EXIT();
              return false;
            }
          }
        }

      }

      // Timed out? :(
      if ((std::chrono::system_clock::now() - s) > timeout) {
        if (myConfig.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;
      }

      _cv.wait(500000);

    }
  }

  return false;

}

inline static int64_t timeStamp() {
  using namespace std::chrono;
  return duration_cast<microseconds>(
    steady_clock::now().time_since_epoch()).count();
}

void Inception::reportIn(std::string const& peerId, uint64_t start) {
  MUTEX_LOCKER(lock, _pLock);
  _pings.push_back(1.0e-3*(double)(timeStamp()-start));
}

void Inception::reportIn(query_t const& query) {

  VPackSlice slice = query->slice();

  TRI_ASSERT(slice.isObject());
  TRI_ASSERT(slice.hasKey("mean"));
  TRI_ASSERT(slice.hasKey("stdev"));
  TRI_ASSERT(slice.hasKey("min"));
  TRI_ASSERT(slice.hasKey("max"));

  MUTEX_LOCKER(lock, _mLock);
  _measurements.push_back(
    std::vector<double>(
      {slice.get("mean").getDouble(), slice.get("stdev").getDouble(),
          slice.get("max").getDouble(), slice.get("min").getDouble()} ));

}

bool Inception::estimateRAFTInterval() {

  using namespace std::chrono;

  std::string path("/_api/agency/config");
  auto pool = _agent->config().pool();
  auto myid = _agent->id();

  for (size_t i = 0; i < 25; ++i) {
    for (auto const& peer : pool) {
      if (peer.first != myid) {
        std::string clientid = peer.first + std::to_string(i);
        auto hf =
          std::make_unique<std::unordered_map<std::string, std::string>>();
        arangodb::ClusterComm::instance()->asyncRequest(
          clientid, 1, peer.second, rest::RequestType::GET, path,
          std::make_shared<std::string>(), hf,
          std::make_shared<MeasureCallback>(this, peer.second, timeStamp()),
          2.0, true);
      }
    }
    std::this_thread::sleep_for(std::chrono::duration<double,std::milli>(5));
  }

  auto s = system_clock::now();
  seconds timeout(3);

  CONDITION_LOCKER(guard, _cv);

  while (true) {

    _cv.wait(50000);

    {
      MUTEX_LOCKER(lock, _pLock);
      if (_pings.size() == 25*(pool.size()-1)) {
        LOG_TOPIC(DEBUG, Logger::AGENCY) << "All pings are in";
        break;
      }
    }

    if ((system_clock::now() - s) > timeout) {
      LOG_TOPIC(DEBUG, Logger::AGENCY) << "Timed out waiting for pings";
      break;
    }

  }

  if (! _pings.empty()) {

    double mean, stdev, mx, mn;

    MUTEX_LOCKER(lock, _pLock);
    size_t num = _pings.size();
    mean   = std::accumulate(_pings.begin(), _pings.end(), 0.0) / num;
    mx     = *std::max_element(_pings.begin(), _pings.end());
    mn     = *std::min_element(_pings.begin(), _pings.end());
    std::transform(_pings.begin(), _pings.end(), _pings.begin(),
                   std::bind2nd(std::minus<double>(), mean));
    stdev =
      std::sqrt(
        std::inner_product(
          _pings.begin(), _pings.end(), _pings.begin(), 0.0) / num);

    LOG_TOPIC(DEBUG, Logger::AGENCY)
      << "mean(" << mean << ") stdev(" << stdev<< ")";

    Builder measurement;
    measurement.openObject();
    measurement.add("mean", VPackValue(mean));
    measurement.add("stdev", VPackValue(stdev));
    measurement.add("min", VPackValue(mn));
    measurement.add("max", VPackValue(mx));
    measurement.close();
    std::string measjson = measurement.toJson();

    path = privApiPrefix + "measure";
    for (auto const& peer : pool) {
      if (peer.first != myid) {
        auto clientId = "1";
        auto comres   = arangodb::ClusterComm::instance()->syncRequest(
          clientId, 1, peer.second, rest::RequestType::POST, path,
          measjson, std::unordered_map<std::string, std::string>(), 2.0);
      }
    }

    {
      MUTEX_LOCKER(lock, _mLock);
      _measurements.push_back(std::vector<double>({mean, stdev, mx, mn}));
    }
    s = system_clock::now();
    while (true) {

      _cv.wait(50000);

      {
        MUTEX_LOCKER(lock, _mLock);
        if (_measurements.size() == pool.size()) {
          LOG_TOPIC(DEBUG, Logger::AGENCY) << "All measurements are in";
          break;
        }
      }

      if ((system_clock::now() - s) > timeout) {
        LOG_TOPIC(WARN, Logger::AGENCY)
          << "Timed out waiting for other measurements. Auto-adaptation failed! Will stick to command line arguments";
        return false;
      }

    }

    double maxmean  = .0;
    double maxstdev = .0;
    for (auto const& meas : _measurements) {
      if (maxmean < meas[0]) {
        maxmean = meas[0];
      }
      if (maxstdev < meas[1]) {
        maxstdev = meas[1];
      }
    }

    maxmean = 1.e-3*std::ceil(1.e3*(.1 + 1.0e-3*(maxmean+3*maxstdev)));

    LOG_TOPIC(INFO, Logger::AGENCY)
      << "Auto-adapting RAFT timing to: {" << maxmean
      << ", " << 5.0*maxmean << "}s";

    _agent->resetRAFTTimes(maxmean, 5.0*maxmean);

  }

  return true;

}

// @brief Active agency from persisted database
bool Inception::activeAgencyFromCommandLine() {
  return false;
}

// @brief Thread main
void Inception::run() {

  // 1. If active agency, do as you're told
  if (activeAgencyFromPersistence()) {
    _agent->ready(true);
  }

  // 2. If we think that we used to be active agent
  if (!_agent->ready() && restartingActiveAgent()) {
    _agent->ready(true);
  }

  // 3. Else gossip
  config_t config = _agent->config();
  if (!_agent->ready() && !config.poolComplete()) {
    gossip();
  }

  // 4. If still incomplete bail out :(
  config = _agent->config();
  if (!_agent->ready() && !config.poolComplete()) {
    LOG_TOPIC(FATAL, Logger::AGENCY)
      << "Failed to build environment for RAFT algorithm. Bailing out!";
    FATAL_ERROR_EXIT();
  }

  estimateRAFTInterval();

  _agent->ready(true);

}

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


<commit_before>//
// Matrix/instance_method_set.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2014 Rick Yang (rick68 at gmail dot com)
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
//

#ifndef EIGENJS_MATRIX_INSTANCE_METHOD_SET_HPP
#define EIGENJS_MATRIX_INSTANCE_METHOD_SET_HPP

namespace EigenJS {

EIGENJS_INSTANCE_METHOD(Matrix, set,
{
  Matrix* obj = node::ObjectWrap::Unwrap<Matrix>(args.This());
  typename Matrix::value_type& matrix = **obj;

  NanScope();

  if (args.Length() == 1 && args[0]->IsArray()) {
    const v8::Local<v8::Array>& array = args[0].As<v8::Array>();
    uint32_t len = array->Length();
    const typename Matrix::value_type::Index& rows = matrix.rows();
    const typename Matrix::value_type::Index& cols = matrix.cols();
    const typename Matrix::value_type::Index& elems = rows * cols;

    if (len != elems) {
      len < rows * cols
        ? NanThrowError("Too few coefficients")
        : NanThrowError("Too many coefficients");
      NanReturnUndefined();
    }

    for (uint32_t i = 0; i < len; ++i) {
      v8::Local<v8::Value> elem = array->Get(i);
      matrix(i / cols, i % cols) = elem->NumberValue();
    }
  } else if (args.Length() == 3 &&
             args[0]->IsNumber() &&
             args[1]->IsNumber() &&
             Matrix::is_scalar(args[2])
  ) {
    const typename Matrix::value_type::Index& row = args[0]->Int32Value();
    const typename Matrix::value_type::Index& col = args[1]->Int32Value();
    const typename Matrix::scalar_type& value = args[2]->NumberValue();

    if (Matrix::is_out_of_range(matrix, row, col)) {
      NanReturnUndefined();
    }

    matrix(row, col) = value;
  } else if (true) {
    EIGENJS_THROW_ERROR_INVALID_ARGUMENT()
    NanReturnUndefined();
  }

  NanReturnValue(args.This());
})

}  // namespace EigenJS

#endif  // EIGENJS_MATRIX_INSTANCE_METHOD_SET_HPP
<commit_msg>src: abstract code<commit_after>//
// Matrix/instance_method_set.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2014 Rick Yang (rick68 at gmail dot com)
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
//

#ifndef EIGENJS_MATRIX_INSTANCE_METHOD_SET_HPP
#define EIGENJS_MATRIX_INSTANCE_METHOD_SET_HPP

namespace EigenJS {

EIGENJS_INSTANCE_METHOD(Matrix, set,
{
  T* obj = node::ObjectWrap::Unwrap<T>(args.This());
  typename T::value_type& matrix = **obj;

  NanScope();

  if (args.Length() == 1 && args[0]->IsArray()) {
    const v8::Local<v8::Array>& array = args[0].As<v8::Array>();
    uint32_t len = array->Length();
    const typename T::value_type::Index& rows = matrix.rows();
    const typename T::value_type::Index& cols = matrix.cols();
    const typename T::value_type::Index& elems = rows * cols;

    if (len != elems) {
      len < rows * cols
        ? NanThrowError("Too few coefficients")
        : NanThrowError("Too many coefficients");
      NanReturnUndefined();
    }

    for (uint32_t i = 0; i < len; ++i) {
      v8::Local<v8::Value> elem = array->Get(i);
      matrix(i / cols, i % cols) = elem->NumberValue();
    }
  } else if (args.Length() == 3 &&
             args[0]->IsNumber() &&
             args[1]->IsNumber() &&
             Matrix::is_scalar(args[2])
  ) {
    const typename T::value_type::Index& row = args[0]->Int32Value();
    const typename T::value_type::Index& col = args[1]->Int32Value();
    const typename T::scalar_type& value = args[2]->NumberValue();

    if (T::is_out_of_range(matrix, row, col)) {
      NanReturnUndefined();
    }

    matrix(row, col) = value;
  } else if (true) {
    EIGENJS_THROW_ERROR_INVALID_ARGUMENT()
    NanReturnUndefined();
  }

  NanReturnValue(args.This());
})

}  // namespace EigenJS

#endif  // EIGENJS_MATRIX_INSTANCE_METHOD_SET_HPP
<|endoftext|>


<commit_before>/*
 * Copyright 2017 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

// ok is an experimental test harness, maybe to replace DM.  Key features:
//   * work is balanced across separate processes for stability and isolation;
//   * ok is entirely opt-in.  No more maintaining huge --blacklists.

#include "SkGraphics.h"
#include "SkOSFile.h"
#include "SkPicture.h"
#include "SkSurface.h"
#include "gm.h"
#include <chrono>
#include <functional>
#include <future>
#include <list>
#include <map>
#include <memory>
#include <regex>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <thread>

enum class Status { OK, Failed, Crashed, Skipped, None };

struct Engine {
    virtual ~Engine() {}
    virtual bool spawn(std::function<Status(void)>) = 0;
    virtual Status wait_one() = 0;
};

struct SerialEngine : Engine {
    Status last = Status::None;

    bool spawn(std::function<Status(void)> fn) override {
        last = fn();
        return true;
    }

    Status wait_one() override {
        Status s = last;
        last = Status::None;
        return s;
    }
};

struct ThreadEngine : Engine {
    std::list<std::future<Status>> live;

    bool spawn(std::function<Status(void)> fn) override {
        live.push_back(std::async(std::launch::async, fn));
        return true;
    }

    Status wait_one() override {
        if (live.empty()) {
            return Status::None;
        }

        for (;;) {
            for (auto it = live.begin(); it != live.end(); it++) {
                if (it->wait_for(std::chrono::seconds::zero()) == std::future_status::ready) {
                    Status s = it->get();
                    live.erase(it);
                    return s;
                }
            }
        }
    }
};

#if defined(_MSC_VER)
    using ForkEngine = ThreadEngine;
#else
    #include <sys/wait.h>
    #include <unistd.h>

    struct ForkEngine : Engine {
        bool spawn(std::function<Status(void)> fn) override {
            switch (fork()) {
                case  0: exit((int)fn());
                case -1: return false;
                default: return true;
            }
        }

        Status wait_one() override {
            do {
                int status;
                if (wait(&status) > 0) {
                    return WIFEXITED(status) ? (Status)WEXITSTATUS(status)
                                             : Status::Crashed;
                }
            } while (errno == EINTR);
            return Status::None;
        }
    };
#endif

struct Src {
    virtual ~Src() {}
    virtual std::string name() = 0;
    virtual SkISize     size() = 0;
    virtual void draw(SkCanvas*) = 0;
};

struct Stream {
    virtual ~Stream() {}
    virtual std::unique_ptr<Src> next() = 0;
};

struct Options {
    std::map<std::string, std::string> kv;

    explicit Options(std::string str) {
        std::string k,v, *curr = &k;
        for (auto c : str) {
            switch(c) {
                case ',': kv[k] = v;
                          curr = &(k = "");
                          break;
                case '=': curr = &(v = "");
                          break;
                default: *curr += c;
            }
        }
        kv[k] = v;
    }

    std::string lookup(std::string k, std::string fallback) {
        for (auto it = kv.find(k); it != kv.end(); ) {
            return it->second;
        }
        return fallback;
    }
};

struct GMStream : Stream {
    const skiagm::GMRegistry* registry = skiagm::GMRegistry::Head();

    struct GMSrc : Src {
        skiagm::GM* (*factory)(void*);
        std::unique_ptr<skiagm::GM> gm;

        std::string name() override {
            gm.reset(factory(nullptr));
            return gm->getName();
        }

        SkISize size() override {
            return gm->getISize();
        }

        void draw(SkCanvas* canvas) override {
            canvas->clear(0xffffffff);
            canvas->concat(gm->getInitialTransform());
            gm->draw(canvas);
        }
    };

    std::unique_ptr<Src> next() override {
        if (!registry) {
            return nullptr;
        }
        GMSrc src;
        src.factory = registry->factory();
        registry = registry->next();
        return std::unique_ptr<Src>{new GMSrc{std::move(src)}};
    }
};

struct SKPStream : Stream {
    std::string dir;
    std::vector<std::string> skps;

    explicit SKPStream(Options options) : dir(options.lookup("dir", "skps")) {
        SkOSFile::Iter it{dir.c_str(), ".skp"};
        for (SkString path; it.next(&path); ) {
            skps.push_back(path.c_str());
        }
    }

    struct SKPSrc : Src {
        std::string dir, path;
        sk_sp<SkPicture> pic;

        std::string name() override {
            return path;
        }

        SkISize size() override {
            auto skp = SkData::MakeFromFileName((dir+"/"+path).c_str());
            pic = SkPicture::MakeFromData(skp.get());
            return pic->cullRect().roundOut().size();
        }

        void draw(SkCanvas* canvas) override {
            canvas->clear(0xffffffff);
            pic->playback(canvas);
        }
    };

    std::unique_ptr<Src> next() override {
        if (skps.empty()) {
            return nullptr;
        }
        SKPSrc src;
        src.dir  = dir;
        src.path = skps.back();
        skps.pop_back();
        return std::unique_ptr<Src>{new SKPSrc{std::move(src)}};
    }
};

struct {
    const char* name;
    std::unique_ptr<Stream> (*factory)(Options);
} streams[] = {
    {"gm",  [](Options options) { return std::unique_ptr<Stream>{new GMStream}; }},
    {"skp", [](Options options) { return std::unique_ptr<Stream>{new SKPStream{options}}; }},
};

int main(int argc, char** argv) {
    SkGraphics::Init();

    int         jobs        {1};
    std::regex  match       {".*"};
    std::regex  search      {".*"};
    std::string write_dir   {""};

    std::unique_ptr<Stream> stream;

    auto help = [&] {
        std::string stream_types;
        for (auto st : streams) {
            if (!stream_types.empty()) {
                stream_types += ", ";
            }
            stream_types += st.name;
        }

        printf("%s [-j N] [-m regex] [-s regex] [-w dir] [-h] stream[:k=v,k=v,...]  \n"
                "     -j: Run at most N processes at any time.                      \n"
                "         If <0, use -N threads instead.                            \n"
                "         If 0, use one thread in one process.                      \n"
                "         If 1 (default) or -1, auto-detect N.                      \n"
                "     -m: Run only names matching regex exactly.                    \n"
                "     -s: Run only names matching regex anywhere.                   \n"
                "     -w: If set, write .pngs into dir.                             \n"
                "     -h: Print this message and exit.                              \n"
                " stream: content to draw:  %s                                      \n"
                "         Some streams have options, e.g. skp:dir=skps              \n",
                argv[0], stream_types.c_str());
        return 1;
    };

    for (int i = 1; i < argc; i++) {
        if (0 == strcmp("-j", argv[i])) { jobs      = atoi(argv[++i]); }
        if (0 == strcmp("-m", argv[i])) { match     =      argv[++i] ; }
        if (0 == strcmp("-s", argv[i])) { search    =      argv[++i] ; }
        if (0 == strcmp("-w", argv[i])) { write_dir =      argv[++i] ; }
        if (0 == strcmp("-h", argv[i])) { return help(); }

        for (auto st : streams) {
            size_t len = strlen(st.name);
            if (0 == strncmp(st.name, argv[i], len)) {
                switch (argv[i][len]) {
                    case  ':': len++;
                    case '\0': stream = st.factory(Options{argv[i]+len});
                }
            }
        }
    }
    if (!stream) { return help(); }

    std::unique_ptr<Engine> engine;
    if (jobs == 0) { engine.reset(new SerialEngine);               }
    if (jobs  > 0) { engine.reset(new   ForkEngine);               }
    if (jobs  < 0) { engine.reset(new ThreadEngine); jobs = -jobs; }

    if (jobs == 1) { jobs = std::thread::hardware_concurrency(); }

    if (!write_dir.empty()) {
        sk_mkdir(write_dir.c_str());
    }

    int ok = 0, failed = 0, crashed = 0, skipped = 0;

    auto update_stats = [&](Status s) {
        switch (s) {
            case Status::OK:      ok++;      break;
            case Status::Failed:  failed++;  break;
            case Status::Crashed: crashed++; break;
            case Status::Skipped: skipped++; break;
            case Status::None:              return;
        }
        const char* leader = "\r";
        auto print = [&](int count, const char* label) {
            if (count) {
                printf("%s%d %s", leader, count, label);
                leader = ", ";
            }
        };
        print(ok,      "ok");
        print(failed,  "failed");
        print(crashed, "crashed");
        print(skipped, "skipped");
        fflush(stdout);
    };

    auto spawn = [&](std::function<Status(void)> fn) {
        if (--jobs < 0) {
            update_stats(engine->wait_one());
        }
        while (!engine->spawn(fn)) {
            update_stats(engine->wait_one());
        }
    };

    for (std::unique_ptr<Src> owned = stream->next(); owned; owned = stream->next()) {
        Src* raw = owned.release();  // Can't move std::unique_ptr into a lambda in C++11. :(
        spawn([=] {
            std::unique_ptr<Src> src{raw};

            auto name = src->name();
            if (!std::regex_match (name, match) ||
                !std::regex_search(name, search)) {
                return Status::Skipped;
            }

            auto size = src->size();
            auto surface = SkSurface::MakeRasterN32Premul(size.width(), size.height());
            src->draw(surface->getCanvas());

            if (!write_dir.empty()) {
                auto image = surface->makeImageSnapshot();
                sk_sp<SkData> png{image->encode()};

                std::string path = write_dir + "/" + name + ".png";
                SkFILEWStream{path.c_str()}.write(png->data(), png->size());
            }
            return Status::OK;
        });
    }

    for (Status s = Status::OK; s != Status::None; ) {
        s = engine->wait_one();
        update_stats(s);
    }
    printf("\n");
    return (failed || crashed) ? 1 : 0;
}
<commit_msg>ok: basic crash handling and stack trace dumps<commit_after>/*
 * Copyright 2017 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

// ok is an experimental test harness, maybe to replace DM.  Key features:
//   * work is balanced across separate processes for stability and isolation;
//   * ok is entirely opt-in.  No more maintaining huge --blacklists.

#include "SkGraphics.h"
#include "SkOSFile.h"
#include "SkPicture.h"
#include "SkSurface.h"
#include "gm.h"
#include <chrono>
#include <functional>
#include <future>
#include <list>
#include <map>
#include <memory>
#include <regex>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <thread>

#if !defined(__has_include)
    #define  __has_include(x) 0
#endif

static thread_local const char* tls_name = "";

#if __has_include(<execinfo.h>) && __has_include(<fcntl.h>) && __has_include(<signal.h>)
    #include <execinfo.h>
    #include <fcntl.h>
    #include <signal.h>

    static int crash_stacktrace_fd = 2/*stderr*/;

    static void setup_crash_handler() {
        static void (*original_handlers[32])(int);

        for (int sig : std::vector<int>{ SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV }) {
            original_handlers[sig] = signal(sig, [](int sig) {
                auto ez_write = [](const char* str) {
                    write(crash_stacktrace_fd, str, strlen(str));
                };
                ez_write("\ncaught signal ");
                switch (sig) {
                #define CASE(s) case s: ez_write(#s); break
                    CASE(SIGABRT);
                    CASE(SIGBUS);
                    CASE(SIGFPE);
                    CASE(SIGILL);
                    CASE(SIGSEGV);
                #undef CASE
                }
                ez_write(" while running '");
                ez_write(tls_name);
                ez_write("'\n");

                void* stack[128];
                int frames = backtrace(stack, sizeof(stack)/sizeof(*stack));
                backtrace_symbols_fd(stack, frames, crash_stacktrace_fd);
                signal(sig, original_handlers[sig]);
                raise(sig);
            });
        }
    }

    static void defer_crash_stacktraces() {
        crash_stacktrace_fd = fileno(tmpfile());
        atexit([] {
            lseek(crash_stacktrace_fd, 0, SEEK_SET);
            char buf[1024];
            while (size_t bytes = read(crash_stacktrace_fd, buf, sizeof(buf))) {
                write(2, buf, bytes);
            }
        });
    }
#else
    static void setup_crash_handler() {}
    static void defer_crash_stacktraces() {}
#endif

enum class Status { OK, Failed, Crashed, Skipped, None };

struct Engine {
    virtual ~Engine() {}
    virtual bool spawn(std::function<Status(void)>) = 0;
    virtual Status wait_one() = 0;
};

struct SerialEngine : Engine {
    Status last = Status::None;

    bool spawn(std::function<Status(void)> fn) override {
        last = fn();
        return true;
    }

    Status wait_one() override {
        Status s = last;
        last = Status::None;
        return s;
    }
};

struct ThreadEngine : Engine {
    std::list<std::future<Status>> live;

    bool spawn(std::function<Status(void)> fn) override {
        live.push_back(std::async(std::launch::async, fn));
        return true;
    }

    Status wait_one() override {
        if (live.empty()) {
            return Status::None;
        }

        for (;;) {
            for (auto it = live.begin(); it != live.end(); it++) {
                if (it->wait_for(std::chrono::seconds::zero()) == std::future_status::ready) {
                    Status s = it->get();
                    live.erase(it);
                    return s;
                }
            }
        }
    }
};

#if defined(_MSC_VER)
    using ForkEngine = ThreadEngine;
#else
    #include <sys/wait.h>
    #include <unistd.h>

    struct ForkEngine : Engine {
        bool spawn(std::function<Status(void)> fn) override {
            switch (fork()) {
                case  0: _exit((int)fn());
                case -1: return false;
                default: return true;
            }
        }

        Status wait_one() override {
            do {
                int status;
                if (wait(&status) > 0) {
                    return WIFEXITED(status) ? (Status)WEXITSTATUS(status)
                                             : Status::Crashed;
                }
            } while (errno == EINTR);
            return Status::None;
        }
    };
#endif

struct Src {
    virtual ~Src() {}
    virtual std::string name() = 0;
    virtual SkISize     size() = 0;
    virtual void draw(SkCanvas*) = 0;
};

struct Stream {
    virtual ~Stream() {}
    virtual std::unique_ptr<Src> next() = 0;
};

struct Options {
    std::map<std::string, std::string> kv;

    explicit Options(std::string str) {
        std::string k,v, *curr = &k;
        for (auto c : str) {
            switch(c) {
                case ',': kv[k] = v;
                          curr = &(k = "");
                          break;
                case '=': curr = &(v = "");
                          break;
                default: *curr += c;
            }
        }
        kv[k] = v;
    }

    std::string lookup(std::string k, std::string fallback) {
        for (auto it = kv.find(k); it != kv.end(); ) {
            return it->second;
        }
        return fallback;
    }
};

struct GMStream : Stream {
    const skiagm::GMRegistry* registry = skiagm::GMRegistry::Head();

    struct GMSrc : Src {
        skiagm::GM* (*factory)(void*);
        std::unique_ptr<skiagm::GM> gm;

        std::string name() override {
            gm.reset(factory(nullptr));
            return gm->getName();
        }

        SkISize size() override {
            return gm->getISize();
        }

        void draw(SkCanvas* canvas) override {
            canvas->clear(0xffffffff);
            canvas->concat(gm->getInitialTransform());
            gm->draw(canvas);
        }
    };

    std::unique_ptr<Src> next() override {
        if (!registry) {
            return nullptr;
        }
        GMSrc src;
        src.factory = registry->factory();
        registry = registry->next();
        return std::unique_ptr<Src>{new GMSrc{std::move(src)}};
    }
};

struct SKPStream : Stream {
    std::string dir;
    std::vector<std::string> skps;

    explicit SKPStream(Options options) : dir(options.lookup("dir", "skps")) {
        SkOSFile::Iter it{dir.c_str(), ".skp"};
        for (SkString path; it.next(&path); ) {
            skps.push_back(path.c_str());
        }
    }

    struct SKPSrc : Src {
        std::string dir, path;
        sk_sp<SkPicture> pic;

        std::string name() override {
            return path;
        }

        SkISize size() override {
            auto skp = SkData::MakeFromFileName((dir+"/"+path).c_str());
            pic = SkPicture::MakeFromData(skp.get());
            return pic->cullRect().roundOut().size();
        }

        void draw(SkCanvas* canvas) override {
            canvas->clear(0xffffffff);
            pic->playback(canvas);
        }
    };

    std::unique_ptr<Src> next() override {
        if (skps.empty()) {
            return nullptr;
        }
        SKPSrc src;
        src.dir  = dir;
        src.path = skps.back();
        skps.pop_back();
        return std::unique_ptr<Src>{new SKPSrc{std::move(src)}};
    }
};

struct {
    const char* name;
    std::unique_ptr<Stream> (*factory)(Options);
} streams[] = {
    {"gm",  [](Options options) { return std::unique_ptr<Stream>{new GMStream}; }},
    {"skp", [](Options options) { return std::unique_ptr<Stream>{new SKPStream{options}}; }},
};

int main(int argc, char** argv) {
    SkGraphics::Init();
    setup_crash_handler();

    int         jobs        {1};
    std::regex  match       {".*"};
    std::regex  search      {".*"};
    std::string write_dir   {""};

    std::unique_ptr<Stream> stream;

    auto help = [&] {
        std::string stream_types;
        for (auto st : streams) {
            if (!stream_types.empty()) {
                stream_types += ", ";
            }
            stream_types += st.name;
        }

        printf("%s [-j N] [-m regex] [-s regex] [-w dir] [-h] stream[:k=v,k=v,...]  \n"
                "     -j: Run at most N processes at any time.                      \n"
                "         If <0, use -N threads instead.                            \n"
                "         If 0, use one thread in one process.                      \n"
                "         If 1 (default) or -1, auto-detect N.                      \n"
                "     -m: Run only names matching regex exactly.                    \n"
                "     -s: Run only names matching regex anywhere.                   \n"
                "     -w: If set, write .pngs into dir.                             \n"
                "     -h: Print this message and exit.                              \n"
                " stream: content to draw:  %s                                      \n"
                "         Some streams have options, e.g. skp:dir=skps              \n",
                argv[0], stream_types.c_str());
        return 1;
    };

    for (int i = 1; i < argc; i++) {
        if (0 == strcmp("-j", argv[i])) { jobs      = atoi(argv[++i]); }
        if (0 == strcmp("-m", argv[i])) { match     =      argv[++i] ; }
        if (0 == strcmp("-s", argv[i])) { search    =      argv[++i] ; }
        if (0 == strcmp("-w", argv[i])) { write_dir =      argv[++i] ; }
        if (0 == strcmp("-h", argv[i])) { return help(); }

        for (auto st : streams) {
            size_t len = strlen(st.name);
            if (0 == strncmp(st.name, argv[i], len)) {
                switch (argv[i][len]) {
                    case  ':': len++;
                    case '\0': stream = st.factory(Options{argv[i]+len});
                }
            }
        }
    }
    if (!stream) { return help(); }

    std::unique_ptr<Engine> engine;
    if (jobs == 0) { engine.reset(new SerialEngine);                            }
    if (jobs  > 0) { engine.reset(new   ForkEngine); defer_crash_stacktraces(); }
    if (jobs  < 0) { engine.reset(new ThreadEngine); jobs = -jobs;              }

    if (jobs == 1) { jobs = std::thread::hardware_concurrency(); }

    if (!write_dir.empty()) {
        sk_mkdir(write_dir.c_str());
    }

    int ok = 0, failed = 0, crashed = 0, skipped = 0;

    auto update_stats = [&](Status s) {
        switch (s) {
            case Status::OK:      ok++;      break;
            case Status::Failed:  failed++;  break;
            case Status::Crashed: crashed++; break;
            case Status::Skipped: skipped++; break;
            case Status::None:              return;
        }
        const char* leader = "\r";
        auto print = [&](int count, const char* label) {
            if (count) {
                printf("%s%d %s", leader, count, label);
                leader = ", ";
            }
        };
        print(ok,      "ok");
        print(failed,  "failed");
        print(crashed, "crashed");
        print(skipped, "skipped");
        fflush(stdout);
    };

    auto spawn = [&](std::function<Status(void)> fn) {
        if (--jobs < 0) {
            update_stats(engine->wait_one());
        }
        while (!engine->spawn(fn)) {
            update_stats(engine->wait_one());
        }
    };

    for (std::unique_ptr<Src> owned = stream->next(); owned; owned = stream->next()) {
        Src* raw = owned.release();  // Can't move std::unique_ptr into a lambda in C++11. :(
        spawn([=] {
            std::unique_ptr<Src> src{raw};

            auto name = src->name();
            tls_name = name.c_str();
            if (!std::regex_match (name, match) ||
                !std::regex_search(name, search)) {
                return Status::Skipped;
            }

            auto size = src->size();
            auto surface = SkSurface::MakeRasterN32Premul(size.width(), size.height());
            src->draw(surface->getCanvas());

            if (!write_dir.empty()) {
                auto image = surface->makeImageSnapshot();
                sk_sp<SkData> png{image->encode()};

                std::string path = write_dir + "/" + name + ".png";
                SkFILEWStream{path.c_str()}.write(png->data(), png->size());
            }
            return Status::OK;
        });
    }

    for (Status s = Status::OK; s != Status::None; ) {
        s = engine->wait_one();
        update_stats(s);
    }
    printf("\n");
    return (failed || crashed) ? 1 : 0;
}
<|endoftext|>


<commit_before>#ifndef ATOMIC_HH
#define ATOMIC_HH

#include <pthread.h>
#include <queue>
#include "locks.hh"

#define MAX_THREADS 100

template<typename T>
class ThreadLocal {
public:
    ThreadLocal(void (*destructor)(void *) = NULL) {
        int rc = pthread_key_create(&key, destructor);
        if (rc != 0) {
            throw std::runtime_error("Failed to create a thread-specific key");
        }
    }

    ~ThreadLocal() {
        pthread_key_delete(key);
    }

    void set(const T &newValue) {
        pthread_setspecific(key, newValue);
    }

    T get() const {
        return reinterpret_cast<T>(pthread_getspecific(key));
    }

    void operator =(const T &newValue) {
        set(newValue);
    }

    operator T() const {
        return get();
    }

private:
    pthread_key_t key;
};

template <typename T>
class ThreadLocalPtr : public ThreadLocal<T*> {
public:
    ThreadLocalPtr(void (*destructor)(void *) = NULL) : ThreadLocal<T*>(destructor) {}

    ~ThreadLocalPtr() {}

    T *operator ->() {
        return ThreadLocal<T*>::get();
    }

    T operator *() {
        return *ThreadLocal<T*>::get();
    }

    void operator =(T *newValue) {
        set(newValue);
    }
};

template <typename T>
class Atomic {
public:
    Atomic() {}

    Atomic(const T &initial) {
        set(initial);
    }

    ~Atomic() {}

    T get() const {
        return value;
    }

    void set(const T &newValue) {
        value = newValue;
        __sync_synchronize();
    }

    operator T() const {
        return get();
    }

    void operator =(const T &newValue) {
        set(newValue);
    }

    bool cas(const T &oldValue, const T &newValue) {
        return __sync_bool_compare_and_swap(&value, oldValue, newValue);
    }

    T operator ++() { // prefix
        return __sync_add_and_fetch(&value, 1);
    }

    T operator ++(int ignored) { // postfix
        (void)ignored;
        return __sync_fetch_and_add(&value, 1);
    }

    T operator +=(T increment) {
        // Returns the new value
        return __sync_add_and_fetch(&value, increment);
    }

    T operator -=(T decrement) {
        return __sync_add_and_fetch(&value, -decrement);
    }

    T incr(const T &increment = 1) {
        // Returns the old value
        return __sync_fetch_and_add(&value, increment);
    }

    T swap(const T &newValue) {
        T rv;
        while (true) {
            rv = get();
            if (cas(rv, newValue)) {
                break;
            }
        }
        return rv;
    }

    T swapIfNot(const T &badValue, const T &newValue) {
        T oldValue;
        while (true) {
            oldValue = get();
            if (oldValue != badValue) {
                if (cas(oldValue, newValue)) {
                    break;
                }
            } else {
                break;
            }
        }
        return oldValue;
    }
private:
    volatile T value;
};

template <typename T>
class AtomicPtr : public Atomic<T*> {
public:
    AtomicPtr(T *initial = NULL) : Atomic<T*>(initial) {}

    ~AtomicPtr() {}

    T *operator ->() {
        return Atomic<T*>::get();
    }

    T operator *() {
        return *Atomic<T*>::get();
    }
};

template <typename T>
class AtomicQueue {
public:
    AtomicQueue() : counter((size_t)0), numItems((size_t)0) {}

    ~AtomicQueue() {
        size_t i;
        for (i = 0; i < counter; ++i) {
            delete queues[i];
        }
    }

    void push(T value) {
        std::queue<T> *q = swapQueue(); // steal our queue
        q->push(value);
        ++numItems;
        q = swapQueue(q);
    }

    void pushQueue(std::queue<T> &inQueue) {
        std::queue<T> *q = swapQueue(); // steal our queue
        numItems.incr(inQueue.size());
        while (!inQueue.empty()) {
            q->push(inQueue.front());
            inQueue.pop();
        }
        q = swapQueue(q);
    }

    void getAll(std::queue<T> &outQueue) {
        std::queue<T> *q(swapQueue()); // Grab my own queue
        std::queue<T> *newQueue(NULL);
        int i(0), count(0);

        // Will start empty unless this thread is adding stuff
        while (!q->empty()) {
            outQueue.push(q->front());
            q->pop();
            ++count;
        }

        int c = counter;
        for (i = 0; i < c; ++i) {
            // Swap with another thread
            newQueue = queues[i].swapIfNot(NULL, q);
            // Empty the queue
            if (newQueue != NULL) {
                q = newQueue;
                while (!q->empty()) {
                    outQueue.push(q->front());
                    q->pop();
                    ++count;
                }
            }
        }

        q = swapQueue(q);
        numItems -= count;
    }

    size_t getNumQueues() const {
        return counter;
    }

    bool empty() const {
        return size() == 0;
    }

    size_t size() const {
        return numItems;
    }
private:
    AtomicPtr<std::queue<T> > *initialize() {
        std::queue<T> *q = new std::queue<T>;
        int i = counter++;
        queues[i] = q;
        threadQueue = &queues[i];
        return &queues[i];
    }

    std::queue<T> *swapQueue(std::queue<T> *newQueue = NULL) {
        AtomicPtr<std::queue<T> > *qPtr(threadQueue);
        if (qPtr == NULL) {
            qPtr = initialize();
        }
        return qPtr->swap(newQueue);
    }

    ThreadLocalPtr<AtomicPtr<std::queue<T> > > threadQueue;
    AtomicPtr<std::queue<T> > queues[MAX_THREADS];
    Atomic<size_t> counter;
    Atomic<size_t> numItems;
    DISALLOW_COPY_AND_ASSIGN(AtomicQueue);
};

#endif // ATOMIC_HH
<commit_msg>Compile fixes for Linux.<commit_after>#ifndef ATOMIC_HH
#define ATOMIC_HH

#include <pthread.h>
#include <queue>
#include "locks.hh"

#define MAX_THREADS 100

template<typename T>
class ThreadLocal {
public:
    ThreadLocal(void (*destructor)(void *) = NULL) {
        int rc = pthread_key_create(&key, destructor);
        if (rc != 0) {
            throw std::runtime_error("Failed to create a thread-specific key");
        }
    }

    ~ThreadLocal() {
        pthread_key_delete(key);
    }

    void set(const T &newValue) {
        pthread_setspecific(key, newValue);
    }

    T get() const {
        return reinterpret_cast<T>(pthread_getspecific(key));
    }

    void operator =(const T &newValue) {
        set(newValue);
    }

    operator T() const {
        return get();
    }

private:
    pthread_key_t key;
};

template <typename T>
class ThreadLocalPtr : public ThreadLocal<T*> {
public:
    ThreadLocalPtr(void (*destructor)(void *) = NULL) : ThreadLocal<T*>(destructor) {}

    ~ThreadLocalPtr() {}

    T *operator ->() {
        return ThreadLocal<T*>::get();
    }

    T operator *() {
        return *ThreadLocal<T*>::get();
    }

    void operator =(T *newValue) {
        set(newValue);
    }
};

template <typename T>
class Atomic {
public:
    Atomic() {}

    Atomic(const T &initial) {
        set(initial);
    }

    ~Atomic() {}

    T get() const {
        return value;
    }

    void set(const T &newValue) {
        value = newValue;
        __sync_synchronize();
    }

    operator T() const {
        return get();
    }

    void operator =(const T &newValue) {
        set(newValue);
    }

    bool cas(const T &oldValue, const T &newValue) {
        return __sync_bool_compare_and_swap(&value, oldValue, newValue);
    }

    T operator ++() { // prefix
        return __sync_add_and_fetch(&value, 1);
    }

    T operator ++(int ignored) { // postfix
        (void)ignored;
        return __sync_fetch_and_add(&value, 1);
    }

    T operator +=(T increment) {
        // Returns the new value
        return __sync_add_and_fetch(&value, increment);
    }

    T operator -=(T decrement) {
        return __sync_add_and_fetch(&value, -decrement);
    }

    T incr(const T &increment = 1) {
        // Returns the old value
        return __sync_fetch_and_add(&value, increment);
    }

    T swap(const T &newValue) {
        T rv;
        while (true) {
            rv = get();
            if (cas(rv, newValue)) {
                break;
            }
        }
        return rv;
    }

    T swapIfNot(const T &badValue, const T &newValue) {
        T oldValue;
        while (true) {
            oldValue = get();
            if (oldValue != badValue) {
                if (cas(oldValue, newValue)) {
                    break;
                }
            } else {
                break;
            }
        }
        return oldValue;
    }
private:
    volatile T value;
};

template <typename T>
class AtomicPtr : public Atomic<T*> {
public:
    AtomicPtr(T *initial = NULL) : Atomic<T*>(initial) {}

    ~AtomicPtr() {}

    T *operator ->() {
        return Atomic<T*>::get();
    }

    T operator *() {
        return *Atomic<T*>::get();
    }
};

template <typename T>
class AtomicQueue {
public:
    AtomicQueue() : counter((size_t)0), numItems((size_t)0) {}

    ~AtomicQueue() {
        size_t i;
        for (i = 0; i < counter; ++i) {
            delete queues[i];
        }
    }

    void push(T value) {
        std::queue<T> *q = swapQueue(); // steal our queue
        q->push(value);
        ++numItems;
        q = swapQueue(q);
    }

    void pushQueue(std::queue<T> &inQueue) {
        std::queue<T> *q = swapQueue(); // steal our queue
        numItems.incr(inQueue.size());
        while (!inQueue.empty()) {
            q->push(inQueue.front());
            inQueue.pop();
        }
        q = swapQueue(q);
    }

    void getAll(std::queue<T> &outQueue) {
        std::queue<T> *q(swapQueue()); // Grab my own queue
        std::queue<T> *newQueue(NULL);
        int count(0);

        // Will start empty unless this thread is adding stuff
        while (!q->empty()) {
            outQueue.push(q->front());
            q->pop();
            ++count;
        }

        size_t c(counter);
        for (size_t i = 0; i < c; ++i) {
            // Swap with another thread
            newQueue = queues[i].swapIfNot(NULL, q);
            // Empty the queue
            if (newQueue != NULL) {
                q = newQueue;
                while (!q->empty()) {
                    outQueue.push(q->front());
                    q->pop();
                    ++count;
                }
            }
        }

        q = swapQueue(q);
        numItems -= count;
    }

    size_t getNumQueues() const {
        return counter;
    }

    bool empty() const {
        return size() == 0;
    }

    size_t size() const {
        return numItems;
    }
private:
    AtomicPtr<std::queue<T> > *initialize() {
        std::queue<T> *q = new std::queue<T>;
        size_t i(counter++);
        queues[i] = q;
        threadQueue = &queues[i];
        return &queues[i];
    }

    std::queue<T> *swapQueue(std::queue<T> *newQueue = NULL) {
        AtomicPtr<std::queue<T> > *qPtr(threadQueue);
        if (qPtr == NULL) {
            qPtr = initialize();
        }
        return qPtr->swap(newQueue);
    }

    ThreadLocalPtr<AtomicPtr<std::queue<T> > > threadQueue;
    AtomicPtr<std::queue<T> > queues[MAX_THREADS];
    Atomic<size_t> counter;
    Atomic<size_t> numItems;
    DISALLOW_COPY_AND_ASSIGN(AtomicQueue);
};

#endif // ATOMIC_HH
<|endoftext|>


<commit_before>#include<iostream>
using namespace std;

int main()
{
	int a=2,b=2;
	a++;
	cout<<a<<endl;
	b=++a;
	cout<<b<<endl;
	return 0;
}
<commit_msg>Update test09.cpp<commit_after>#include<iostream>
using namespace std;

int main()
{
	int a=2,b=2;
	a++;//a=3
	cout<<a<<endl;//3
	b=++a;//4
	cout<<b<<endl;//4
	return 0;
}
<|endoftext|>


<commit_before>/*===========================================================================*\
 *                                                                           *
 *                               OpenMesh                                    *
 *      Copyright (C) 2001-2012 by Computer Graphics Group, RWTH Aachen      *
 *                           www.openmesh.org                                *
 *                                                                           *
 *---------------------------------------------------------------------------*
 *  This file is part of OpenMesh.                                           *
 *                                                                           *
 *  OpenMesh is free software: you can redistribute it and/or modify         *
 *  it under the terms of the GNU Lesser General Public License as           *
 *  published by the Free Software Foundation, either version 3 of           *
 *  the License, or (at your option) any later version with the              *
 *  following exceptions:                                                    *
 *                                                                           *
 *  If other files instantiate templates or use macros                       *
 *  or inline functions from this file, or you compile this file and         *
 *  link it with other files to produce an executable, this file does        *
 *  not by itself cause the resulting executable to be covered by the        *
 *  GNU Lesser General Public License. This exception does not however       *
 *  invalidate any other reasons why the executable file might be            *
 *  covered by the GNU Lesser General Public License.                        *
 *                                                                           *
 *  OpenMesh 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 Lesser General Public License for more details.                      *
 *                                                                           *
 *  You should have received a copy of the GNU LesserGeneral Public          *
 *  License along with OpenMesh.  If not,                                    *
 *  see <http://www.gnu.org/licenses/>.                                      *
 *                                                                           *
\*===========================================================================*/

/*===========================================================================*\
 *                                                                           *
 *   $Revision$                                                         *
 *   $Date$                   *
 *                                                                           *
\*===========================================================================*/

//=============================================================================
//
//  CLASS TriMeshT
//
//=============================================================================

#ifndef OPENMESH_TRIMESH_HH
#define OPENMESH_TRIMESH_HH

//== INCLUDES =================================================================

#include <OpenMesh/Core/System/config.h>
#include <OpenMesh/Core/Mesh/PolyMeshT.hh>
#include <vector>

//== NAMESPACES ===============================================================

namespace OpenMesh {

//== CLASS DEFINITION =========================================================

/** \class TriMeshT TriMeshT.hh <OpenMesh/Mesh/TriMeshT.hh>

    Base type for a triangle mesh.

    Base type for a triangle mesh, parameterized by a mesh kernel. The
    mesh inherits all methods from the kernel class and the
    more general polygonal mesh PolyMeshT. Therefore it provides
    the same types for items, handles, iterators and so on.

    \param Kernel: template argument for the mesh kernel
    \note You should use the predefined mesh-kernel combinations in
    \ref mesh_types_group
    \see \ref mesh_type
    \see OpenMesh::PolyMeshT
*/

template <class Kernel>
class TriMeshT : public PolyMeshT<Kernel>
{

public:

  // self
  typedef TriMeshT<Kernel>                      This;
  typedef PolyMeshT<Kernel>                     PolyMesh;

  //@{
  /// Determine whether this is a PolyMeshT or TriMeshT ( This function does not check the per face vertex count! It only checks if the datatype is PolyMeshT or TriMeshT )
  enum { IsPolyMesh = 0 };
  enum { IsTriMesh  = 1 };
  static bool is_polymesh() { return false; }
  static bool is_trimesh()  { return  true; }
  //@}

  //--- items ---

  typedef typename PolyMesh::Scalar             Scalar;
  typedef typename PolyMesh::Point              Point;
  typedef typename PolyMesh::Normal             Normal;
  typedef typename PolyMesh::Color              Color;
  typedef typename PolyMesh::TexCoord1D         TexCoord1D;
  typedef typename PolyMesh::TexCoord2D         TexCoord2D;
  typedef typename PolyMesh::TexCoord3D         TexCoord3D;
  typedef typename PolyMesh::Vertex             Vertex;
  typedef typename PolyMesh::Halfedge           Halfedge;
  typedef typename PolyMesh::Edge               Edge;
  typedef typename PolyMesh::Face               Face;

  //--- handles ---

  typedef typename PolyMesh::VertexHandle       VertexHandle;
  typedef typename PolyMesh::HalfedgeHandle     HalfedgeHandle;
  typedef typename PolyMesh::EdgeHandle         EdgeHandle;
  typedef typename PolyMesh::FaceHandle         FaceHandle;

  //--- iterators ---

  typedef typename PolyMesh::VertexIter         VertexIter;
  typedef typename PolyMesh::ConstVertexIter    ConstVertexIter;
  typedef typename PolyMesh::EdgeIter           EdgeIter;
  typedef typename PolyMesh::ConstEdgeIter      ConstEdgeIter;
  typedef typename PolyMesh::FaceIter           FaceIter;
  typedef typename PolyMesh::ConstFaceIter      ConstFaceIter;

  //--- circulators ---

  typedef typename PolyMesh::VertexVertexIter         VertexVertexIter;
  typedef typename PolyMesh::VertexOHalfedgeIter      VertexOHalfedgeIter;
  typedef typename PolyMesh::VertexIHalfedgeIter      VertexIHalfedgeIter;
  typedef typename PolyMesh::VertexEdgeIter           VertexEdgeIter;
  typedef typename PolyMesh::VertexFaceIter           VertexFaceIter;
  typedef typename PolyMesh::FaceVertexIter           FaceVertexIter;
  typedef typename PolyMesh::FaceHalfedgeIter         FaceHalfedgeIter;
  typedef typename PolyMesh::FaceEdgeIter             FaceEdgeIter;
  typedef typename PolyMesh::FaceFaceIter             FaceFaceIter;
  typedef typename PolyMesh::ConstVertexVertexIter    ConstVertexVertexIter;
  typedef typename PolyMesh::ConstVertexOHalfedgeIter ConstVertexOHalfedgeIter;
  typedef typename PolyMesh::ConstVertexIHalfedgeIter ConstVertexIHalfedgeIter;
  typedef typename PolyMesh::ConstVertexEdgeIter      ConstVertexEdgeIter;
  typedef typename PolyMesh::ConstVertexFaceIter      ConstVertexFaceIter;
  typedef typename PolyMesh::ConstFaceVertexIter      ConstFaceVertexIter;
  typedef typename PolyMesh::ConstFaceHalfedgeIter    ConstFaceHalfedgeIter;
  typedef typename PolyMesh::ConstFaceEdgeIter        ConstFaceEdgeIter;
  typedef typename PolyMesh::ConstFaceFaceIter        ConstFaceFaceIter;

  // --- constructor/destructor

  /// Default constructor
  TriMeshT() : PolyMesh() {}
  /// Destructor
  virtual ~TriMeshT() {}

  //--- halfedge collapse / vertex split ---

  /** \brief Vertex Split: inverse operation to collapse().
   *
   * Insert the new vertex at position v0. The vertex will be added
   * as the inverse of the vertex split. The faces above the split
   * will be correctly attached to the two new edges
   *
   * Before:
   * v_l     v0     v_r
   *  x      x      x
   *   \           /
   *    \         /
   *     \       /
   *      \     /
   *       \   /
   *        \ /
   *         x
   *         v1
   *
   * After:
   * v_l    v0      v_r
   *  x------x------x
   *   \     |     /
   *    \    |    /
   *     \   |   /
   *      \  |  /
   *       \ | /
   *        \|/
   *         x
   *         v1
   *
   * @param _v0_point Point position for the new point
   * @param _v1       Vertex that will be split
   * @param _vl       Left vertex handle
   * @param _vr       Right vertex handle
   * @return Newly inserted halfedge
   */
  inline HalfedgeHandle vertex_split(Point _v0_point,  VertexHandle _v1,
                                     VertexHandle _vl, VertexHandle _vr)
  { return PolyMesh::vertex_split(add_vertex(_v0_point), _v1, _vl, _vr); }

  /** \brief Vertex Split: inverse operation to collapse().
   *
   * Insert the new vertex at position v0. The vertex will be added
   * as the inverse of the vertex split. The faces above the split
   * will be correctly attached to the two new edges
   *
   * Before:
   * v_l     v0     v_r
   *  x      x      x
   *   \           /
   *    \         /
   *     \       /
   *      \     /
   *       \   /
   *        \ /
   *         x
   *         v1
   *
   * After:
   * v_l    v0      v_r
   *  x------x------x
   *   \     |     /
   *    \    |    /
   *     \   |   /
   *      \  |  /
   *       \ | /
   *        \|/
   *         x
   *         v1
   *
   * @param _v0 Vertex handle for the newly inserted point (Input has to be unconnected!)
   * @param _v1 Vertex that will be split
   * @param _vl Left vertex handle
   * @param _vr Right vertex handle
   * @return Newly inserted halfedge
   */
  inline HalfedgeHandle vertex_split(VertexHandle _v0, VertexHandle _v1,
                                     VertexHandle _vl, VertexHandle _vr)
  { return PolyMesh::vertex_split(_v0, _v1, _vl, _vr); }

  /** \brief Edge split (= 2-to-4 split)
   *
   * The properties of the new edges will be undefined!
   *
   *
   * @param _eh Edge handle that should be splitted
   * @param _p  New point position that will be inserted at the edge
   * @return    Vertex handle of the newly added vertex
   */
  inline VertexHandle split(EdgeHandle _eh, const Point& _p)
  {
    //Do not call PolyMeshT function below as this does the wrong operation
    const VertexHandle vh = this->add_vertex(_p); Kernel::split(_eh, vh); return vh;
  }

  /** \brief Edge split (= 2-to-4 split)
   *
   * The properties of the new edges will be adjusted to the properties of the original edge
   *
   * @param _eh Edge handle that should be splitted
   * @param _p  New point position that will be inserted at the edge
   * @return    Vertex handle of the newly added vertex
   */
  inline VertexHandle split_copy(EdgeHandle _eh, const Point& _p)
  {
    //Do not call PolyMeshT function below as this does the wrong operation
    const VertexHandle vh = this->add_vertex(_p); Kernel::split_copy(_eh, vh); return vh;
  }

  /** \brief Edge split (= 2-to-4 split)
   *
   * The properties of the new edges will be undefined!
   *
   * @param _eh Edge handle that should be splitted
   * @param _vh Vertex handle that will be inserted at the edge
   */
  inline void split(EdgeHandle _eh, VertexHandle _vh)
  {
    //Do not call PolyMeshT function below as this does the wrong operation
    Kernel::split(_eh, _vh);
  }

  /** \brief Edge split (= 2-to-4 split)
   *
   * The properties of the new edges will be adjusted to the properties of the original edge
   *
   * @param _eh Edge handle that should be splitted
   * @param _vh Vertex handle that will be inserted at the edge
   */
  inline void split_copy(EdgeHandle _eh, VertexHandle _vh)
  {
    //Do not call PolyMeshT function below as this does the wrong operation
    Kernel::split_copy(_eh, _vh);
  }

  /** \brief Face split (= 1-to-3 split, calls corresponding PolyMeshT function).
   *
   * The properties of the new faces will be undefined!
   *
   * @param _fh Face handle that should be splitted
   * @param _p  New point position that will be inserted in the face
   */
  inline void split(FaceHandle _fh, const Point& _p)
  { PolyMesh::split(_fh, _p); }

  /** \brief Face split (= 1-to-3 split, calls corresponding PolyMeshT function).
   *
   * The properties of the new faces will be adjusted to the properties of the original face
   *
   * @param _fh Face handle that should be splitted
   * @param _p  New point position that will be inserted in the face
   */
  inline void split_copy(FaceHandle _fh, const Point& _p)
  { PolyMesh::split_copy(_fh, _p); }

  /** \brief Face split (= 1-to-3 split, calls corresponding PolyMeshT function).
   *
   * The properties of the new faces will be undefined!
   *
   * @param _fh Face handle that should be splitted
   * @param _vh Vertex handle that will be inserted at the face
   */
  inline void split(FaceHandle _fh, VertexHandle _vh)
  { PolyMesh::split(_fh, _vh); }

  /** \brief Face split (= 1-to-3 split, calls corresponding PolyMeshT function).
   *
   * The properties of the new faces will be adjusted to the properties of the original face
   *
   * @param _fh Face handle that should be splitted
   * @param _vh Vertex handle that will be inserted at the face
   */
  inline void split_copy(FaceHandle _fh, VertexHandle _vh)
  { PolyMesh::split_copy(_fh, _vh); }

  /** \name Normal vector computation
  */
  //@{

  /** Calculate normal vector for face _fh (specialized for TriMesh). */
  Normal calc_face_normal(FaceHandle _fh) const;

  //@}
};

//=============================================================================
} // namespace OpenMesh
//=============================================================================
#if defined(OM_INCLUDE_TEMPLATES) && !defined(OPENMESH_TRIMESH_C)
#define OPENMESH_TRIMESH_TEMPLATES
#include "TriMeshT.cc"
#endif
//=============================================================================
#endif // OPENMESH_TRIMESH_HH defined
//=============================================================================
<commit_msg>Return vertex handles of newly added vertices in split and split copy when passing points instead of handles<commit_after>/*===========================================================================*\
 *                                                                           *
 *                               OpenMesh                                    *
 *      Copyright (C) 2001-2012 by Computer Graphics Group, RWTH Aachen      *
 *                           www.openmesh.org                                *
 *                                                                           *
 *---------------------------------------------------------------------------*
 *  This file is part of OpenMesh.                                           *
 *                                                                           *
 *  OpenMesh is free software: you can redistribute it and/or modify         *
 *  it under the terms of the GNU Lesser General Public License as           *
 *  published by the Free Software Foundation, either version 3 of           *
 *  the License, or (at your option) any later version with the              *
 *  following exceptions:                                                    *
 *                                                                           *
 *  If other files instantiate templates or use macros                       *
 *  or inline functions from this file, or you compile this file and         *
 *  link it with other files to produce an executable, this file does        *
 *  not by itself cause the resulting executable to be covered by the        *
 *  GNU Lesser General Public License. This exception does not however       *
 *  invalidate any other reasons why the executable file might be            *
 *  covered by the GNU Lesser General Public License.                        *
 *                                                                           *
 *  OpenMesh 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 Lesser General Public License for more details.                      *
 *                                                                           *
 *  You should have received a copy of the GNU LesserGeneral Public          *
 *  License along with OpenMesh.  If not,                                    *
 *  see <http://www.gnu.org/licenses/>.                                      *
 *                                                                           *
\*===========================================================================*/

/*===========================================================================*\
 *                                                                           *
 *   $Revision$                                                         *
 *   $Date$                   *
 *                                                                           *
\*===========================================================================*/

//=============================================================================
//
//  CLASS TriMeshT
//
//=============================================================================

#ifndef OPENMESH_TRIMESH_HH
#define OPENMESH_TRIMESH_HH

//== INCLUDES =================================================================

#include <OpenMesh/Core/System/config.h>
#include <OpenMesh/Core/Mesh/PolyMeshT.hh>
#include <vector>

//== NAMESPACES ===============================================================

namespace OpenMesh {

//== CLASS DEFINITION =========================================================

/** \class TriMeshT TriMeshT.hh <OpenMesh/Mesh/TriMeshT.hh>

    Base type for a triangle mesh.

    Base type for a triangle mesh, parameterized by a mesh kernel. The
    mesh inherits all methods from the kernel class and the
    more general polygonal mesh PolyMeshT. Therefore it provides
    the same types for items, handles, iterators and so on.

    \param Kernel: template argument for the mesh kernel
    \note You should use the predefined mesh-kernel combinations in
    \ref mesh_types_group
    \see \ref mesh_type
    \see OpenMesh::PolyMeshT
*/

template <class Kernel>
class TriMeshT : public PolyMeshT<Kernel>
{

public:

  // self
  typedef TriMeshT<Kernel>                      This;
  typedef PolyMeshT<Kernel>                     PolyMesh;

  //@{
  /// Determine whether this is a PolyMeshT or TriMeshT ( This function does not check the per face vertex count! It only checks if the datatype is PolyMeshT or TriMeshT )
  enum { IsPolyMesh = 0 };
  enum { IsTriMesh  = 1 };
  static bool is_polymesh() { return false; }
  static bool is_trimesh()  { return  true; }
  //@}

  //--- items ---

  typedef typename PolyMesh::Scalar             Scalar;
  typedef typename PolyMesh::Point              Point;
  typedef typename PolyMesh::Normal             Normal;
  typedef typename PolyMesh::Color              Color;
  typedef typename PolyMesh::TexCoord1D         TexCoord1D;
  typedef typename PolyMesh::TexCoord2D         TexCoord2D;
  typedef typename PolyMesh::TexCoord3D         TexCoord3D;
  typedef typename PolyMesh::Vertex             Vertex;
  typedef typename PolyMesh::Halfedge           Halfedge;
  typedef typename PolyMesh::Edge               Edge;
  typedef typename PolyMesh::Face               Face;

  //--- handles ---

  typedef typename PolyMesh::VertexHandle       VertexHandle;
  typedef typename PolyMesh::HalfedgeHandle     HalfedgeHandle;
  typedef typename PolyMesh::EdgeHandle         EdgeHandle;
  typedef typename PolyMesh::FaceHandle         FaceHandle;

  //--- iterators ---

  typedef typename PolyMesh::VertexIter         VertexIter;
  typedef typename PolyMesh::ConstVertexIter    ConstVertexIter;
  typedef typename PolyMesh::EdgeIter           EdgeIter;
  typedef typename PolyMesh::ConstEdgeIter      ConstEdgeIter;
  typedef typename PolyMesh::FaceIter           FaceIter;
  typedef typename PolyMesh::ConstFaceIter      ConstFaceIter;

  //--- circulators ---

  typedef typename PolyMesh::VertexVertexIter         VertexVertexIter;
  typedef typename PolyMesh::VertexOHalfedgeIter      VertexOHalfedgeIter;
  typedef typename PolyMesh::VertexIHalfedgeIter      VertexIHalfedgeIter;
  typedef typename PolyMesh::VertexEdgeIter           VertexEdgeIter;
  typedef typename PolyMesh::VertexFaceIter           VertexFaceIter;
  typedef typename PolyMesh::FaceVertexIter           FaceVertexIter;
  typedef typename PolyMesh::FaceHalfedgeIter         FaceHalfedgeIter;
  typedef typename PolyMesh::FaceEdgeIter             FaceEdgeIter;
  typedef typename PolyMesh::FaceFaceIter             FaceFaceIter;
  typedef typename PolyMesh::ConstVertexVertexIter    ConstVertexVertexIter;
  typedef typename PolyMesh::ConstVertexOHalfedgeIter ConstVertexOHalfedgeIter;
  typedef typename PolyMesh::ConstVertexIHalfedgeIter ConstVertexIHalfedgeIter;
  typedef typename PolyMesh::ConstVertexEdgeIter      ConstVertexEdgeIter;
  typedef typename PolyMesh::ConstVertexFaceIter      ConstVertexFaceIter;
  typedef typename PolyMesh::ConstFaceVertexIter      ConstFaceVertexIter;
  typedef typename PolyMesh::ConstFaceHalfedgeIter    ConstFaceHalfedgeIter;
  typedef typename PolyMesh::ConstFaceEdgeIter        ConstFaceEdgeIter;
  typedef typename PolyMesh::ConstFaceFaceIter        ConstFaceFaceIter;

  // --- constructor/destructor

  /// Default constructor
  TriMeshT() : PolyMesh() {}
  /// Destructor
  virtual ~TriMeshT() {}

  //--- halfedge collapse / vertex split ---

  /** \brief Vertex Split: inverse operation to collapse().
   *
   * Insert the new vertex at position v0. The vertex will be added
   * as the inverse of the vertex split. The faces above the split
   * will be correctly attached to the two new edges
   *
   * Before:
   * v_l     v0     v_r
   *  x      x      x
   *   \           /
   *    \         /
   *     \       /
   *      \     /
   *       \   /
   *        \ /
   *         x
   *         v1
   *
   * After:
   * v_l    v0      v_r
   *  x------x------x
   *   \     |     /
   *    \    |    /
   *     \   |   /
   *      \  |  /
   *       \ | /
   *        \|/
   *         x
   *         v1
   *
   * @param _v0_point Point position for the new point
   * @param _v1       Vertex that will be split
   * @param _vl       Left vertex handle
   * @param _vr       Right vertex handle
   * @return Newly inserted halfedge
   */
  inline HalfedgeHandle vertex_split(Point _v0_point,  VertexHandle _v1,
                                     VertexHandle _vl, VertexHandle _vr)
  { return PolyMesh::vertex_split(add_vertex(_v0_point), _v1, _vl, _vr); }

  /** \brief Vertex Split: inverse operation to collapse().
   *
   * Insert the new vertex at position v0. The vertex will be added
   * as the inverse of the vertex split. The faces above the split
   * will be correctly attached to the two new edges
   *
   * Before:
   * v_l     v0     v_r
   *  x      x      x
   *   \           /
   *    \         /
   *     \       /
   *      \     /
   *       \   /
   *        \ /
   *         x
   *         v1
   *
   * After:
   * v_l    v0      v_r
   *  x------x------x
   *   \     |     /
   *    \    |    /
   *     \   |   /
   *      \  |  /
   *       \ | /
   *        \|/
   *         x
   *         v1
   *
   * @param _v0 Vertex handle for the newly inserted point (Input has to be unconnected!)
   * @param _v1 Vertex that will be split
   * @param _vl Left vertex handle
   * @param _vr Right vertex handle
   * @return Newly inserted halfedge
   */
  inline HalfedgeHandle vertex_split(VertexHandle _v0, VertexHandle _v1,
                                     VertexHandle _vl, VertexHandle _vr)
  { return PolyMesh::vertex_split(_v0, _v1, _vl, _vr); }

  /** \brief Edge split (= 2-to-4 split)
   *
   * The properties of the new edges will be undefined!
   *
   *
   * @param _eh Edge handle that should be splitted
   * @param _p  New point position that will be inserted at the edge
   * @return    Vertex handle of the newly added vertex
   */
  inline VertexHandle split(EdgeHandle _eh, const Point& _p)
  {
    //Do not call PolyMeshT function below as this does the wrong operation
    const VertexHandle vh = this->add_vertex(_p); Kernel::split(_eh, vh); return vh;
  }

  /** \brief Edge split (= 2-to-4 split)
   *
   * The properties of the new edges will be adjusted to the properties of the original edge
   *
   * @param _eh Edge handle that should be splitted
   * @param _p  New point position that will be inserted at the edge
   * @return    Vertex handle of the newly added vertex
   */
  inline VertexHandle split_copy(EdgeHandle _eh, const Point& _p)
  {
    //Do not call PolyMeshT function below as this does the wrong operation
    const VertexHandle vh = this->add_vertex(_p); Kernel::split_copy(_eh, vh); return vh;
  }

  /** \brief Edge split (= 2-to-4 split)
   *
   * The properties of the new edges will be undefined!
   *
   * @param _eh Edge handle that should be splitted
   * @param _vh Vertex handle that will be inserted at the edge
   */
  inline void split(EdgeHandle _eh, VertexHandle _vh)
  {
    //Do not call PolyMeshT function below as this does the wrong operation
    Kernel::split(_eh, _vh);
  }

  /** \brief Edge split (= 2-to-4 split)
   *
   * The properties of the new edges will be adjusted to the properties of the original edge
   *
   * @param _eh Edge handle that should be splitted
   * @param _vh Vertex handle that will be inserted at the edge
   */
  inline void split_copy(EdgeHandle _eh, VertexHandle _vh)
  {
    //Do not call PolyMeshT function below as this does the wrong operation
    Kernel::split_copy(_eh, _vh);
  }

  /** \brief Face split (= 1-to-3 split, calls corresponding PolyMeshT function).
   *
   * The properties of the new faces will be undefined!
   *
   * @param _fh Face handle that should be splitted
   * @param _p  New point position that will be inserted in the face
   *
   * @return Vertex handle of the new vertex
   */
  inline VertexHandle split(FaceHandle _fh, const Point& _p)
  { const VertexHandle vh = this->add_vertex(_p); PolyMesh::split(_fh, vh); return vh; }

  /** \brief Face split (= 1-to-3 split, calls corresponding PolyMeshT function).
   *
   * The properties of the new faces will be adjusted to the properties of the original face
   *
   * @param _fh Face handle that should be splitted
   * @param _p  New point position that will be inserted in the face
   *
   * @return Vertex handle of the new vertex
   */
  inline VertexHandle split_copy(FaceHandle _fh, const Point& _p)
  { const VertexHandle vh = this->add_vertex(_p);  PolyMesh::split_copy(_fh, _p); return vh; }

  /** \brief Face split (= 1-to-3 split, calls corresponding PolyMeshT function).
   *
   * The properties of the new faces will be undefined!
   *
   * @param _fh Face handle that should be splitted
   * @param _vh Vertex handle that will be inserted at the face
   */
  inline void split(FaceHandle _fh, VertexHandle _vh)
  { PolyMesh::split(_fh, _vh); }

  /** \brief Face split (= 1-to-3 split, calls corresponding PolyMeshT function).
   *
   * The properties of the new faces will be adjusted to the properties of the original face
   *
   * @param _fh Face handle that should be splitted
   * @param _vh Vertex handle that will be inserted at the face
   */
  inline void split_copy(FaceHandle _fh, VertexHandle _vh)
  { PolyMesh::split_copy(_fh, _vh); }

  /** \name Normal vector computation
  */
  //@{

  /** Calculate normal vector for face _fh (specialized for TriMesh). */
  Normal calc_face_normal(FaceHandle _fh) const;

  //@}
};

//=============================================================================
} // namespace OpenMesh
//=============================================================================
#if defined(OM_INCLUDE_TEMPLATES) && !defined(OPENMESH_TRIMESH_C)
#define OPENMESH_TRIMESH_TEMPLATES
#include "TriMeshT.cc"
#endif
//=============================================================================
#endif // OPENMESH_TRIMESH_HH defined
//=============================================================================
<|endoftext|>