<commit_before>/*
 * Copyright (c) 2009, Willow Garage, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Willow Garage, Inc. nor the names of its
 *       contributors may be used to endorse or promote products derived from
 *       this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "frame_manager.h"
#include "common.h"
#include "display.h"
#include "properties/property.h"

#include <tf/transform_listener.h>
#include <ros/ros.h>

namespace rviz
{

FrameManagerPtr FrameManager::instance()
{
  static FrameManagerWPtr instw;

  FrameManagerPtr inst = instw.lock();
  if (!inst)
  {
    inst.reset(new FrameManager);
    instw = inst;
  }

  return inst;
}

FrameManager::FrameManager()
{
  tf_ = new tf::TransformListener(ros::NodeHandle(), ros::Duration(10 * 60), false);
}

FrameManager::~FrameManager()
{
  delete tf_;
}

void FrameManager::update()
{
  boost::mutex::scoped_lock lock(cache_mutex_);
  cache_.clear();
}

void FrameManager::setFixedFrame(const std::string& frame)
{
  boost::mutex::scoped_lock lock(cache_mutex_);
  fixed_frame_ = frame;
  cache_.clear();
}

bool FrameManager::getTransform(const std::string& frame, ros::Time time, Ogre::Vector3& position, Ogre::Quaternion& orientation, bool relative_orientation)
{
  boost::mutex::scoped_lock lock(cache_mutex_);

  position = Ogre::Vector3(9999999, 9999999, 9999999);
  orientation = Ogre::Quaternion::IDENTITY;

  if (fixed_frame_.empty())
  {
    return false;
  }

  M_Cache::iterator it = cache_.find(CacheKey(frame, time, relative_orientation));
  if (it != cache_.end())
  {
    position = it->second.position;
    orientation = it->second.orientation;
    return true;
  }

  geometry_msgs::Pose pose;
  pose.orientation.w = 1.0f;

  if (!transform(frame, time, pose, position, orientation, relative_orientation))
  {
    return false;
  }

  cache_.insert(std::make_pair(CacheKey(frame, time, relative_orientation), CacheEntry(position, orientation)));

  return true;
}

bool FrameManager::transform(const std::string& frame, ros::Time time, const geometry_msgs::Pose& pose_msg, Ogre::Vector3& position, Ogre::Quaternion& orientation, bool relative_orientation)
{
  position = Ogre::Vector3::ZERO;
  orientation = Ogre::Quaternion::IDENTITY;

  btQuaternion btorient(pose_msg.orientation.x, pose_msg.orientation.y, pose_msg.orientation.z, pose_msg.orientation.w);
  if (btorient.x() == 0.0 && btorient.y() == 0.0 && btorient.z() == 0.0 && btorient.w() == 0.0)
  {
    btorient.setW(1.0);
  }

  tf::Stamped<tf::Pose> pose(btTransform(btorient,
                                   btVector3(pose_msg.position.x, pose_msg.position.y, pose_msg.position.z)),
                                   time, frame);
  try
  {
    tf_->transformPose( fixed_frame_, pose, pose );
  }
  catch(tf::TransformException& e)
  {
    ROS_DEBUG("Error transforming from frame '%s' to frame '%s': %s", frame.c_str(), fixed_frame_.c_str(), e.what());
    return false;
  }

  position = Ogre::Vector3(pose.getOrigin().x(), pose.getOrigin().y(), pose.getOrigin().z());
  robotToOgre(position);

  btQuaternion quat;
  pose.getBasis().getRotation( quat );
  orientation = Ogre::Quaternion::IDENTITY;

  if (relative_orientation)
  {
    ogreToRobot(orientation);
  }

  orientation = Ogre::Quaternion( quat.w(), quat.x(), quat.y(), quat.z() ) * orientation;
  robotToOgre(orientation);

  return true;
}

bool FrameManager::frameHasProblems(const std::string& frame, ros::Time time, std::string& error)
{
  if (!tf_->frameExists(frame))
  {
    error = "Frame [" + frame + "] does not exist";
    if (frame == fixed_frame_)
    {
      error = "Fixed " + error;
    }
    return true;
  }

  return false;
}

bool FrameManager::transformHasProblems(const std::string& frame, ros::Time time, std::string& error)
{
  std::string tf_error;
  bool transform_succeeded = tf_->canTransform(fixed_frame_, frame, time, &tf_error);
  if (transform_succeeded)
  {
    return false;
  }

  bool ok = true;
  ok = ok && !frameHasProblems(fixed_frame_, time, error);
  ok = ok && !frameHasProblems(frame, time, error);

  if (ok)
  {
    std::stringstream ss;
    ss << "No transform to fixed frame [" << fixed_frame_ << "].  TF error: [" << tf_error << "]";
    error = ss.str();
    ok = false;
  }

  {
    std::stringstream ss;
    ss << "For frame [" << frame << "]: " << error;
    error = ss.str();
  }

  return !ok;
}

std::string getTransformStatusName(const std::string& caller_id)
{
  std::stringstream ss;
  ss << "Transform [sender=" << caller_id << "]";
  return ss.str();
}

std::string FrameManager::discoverFailureReason(const roslib::Header& header, const std::string& caller_id, tf::FilterFailureReason reason)
{
  if (reason == tf::filter_failure_reasons::OutTheBack)
  {
    std::stringstream ss;
    ss << "Message removed because it is too old (frame=[" << header.frame_id << "], stamp=[" << header.stamp << "])";
    return ss.str();
  }
  else
  {
    std::string error;
    if (transformHasProblems(header.frame_id, header.stamp, error))
    {
      return error;
    }
  }

  return "Unknown reason for transform failure";
}

void FrameManager::messageArrived(const roslib::Header& header, const std::string& caller_id, Display* display)
{
  display->setStatus(status_levels::Ok, getTransformStatusName(caller_id), "Transform OK");
}

void FrameManager::messageFailed(const roslib::Header& header, const std::string& caller_id, tf::FilterFailureReason reason, Display* display)
{
  std::string status_name = getTransformStatusName(caller_id);
  std::string status_text = discoverFailureReason(header, caller_id, reason);
  display->setStatus(status_levels::Error, status_name, status_text);
}

}
<commit_msg>reduce TF cache time to 60 seconds (from 10 minute), due to memory usage<commit_after>/*
 * Copyright (c) 2009, Willow Garage, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Willow Garage, Inc. nor the names of its
 *       contributors may be used to endorse or promote products derived from
 *       this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "frame_manager.h"
#include "common.h"
#include "display.h"
#include "properties/property.h"

#include <tf/transform_listener.h>
#include <ros/ros.h>

namespace rviz
{

FrameManagerPtr FrameManager::instance()
{
  static FrameManagerWPtr instw;

  FrameManagerPtr inst = instw.lock();
  if (!inst)
  {
    inst.reset(new FrameManager);
    instw = inst;
  }

  return inst;
}

FrameManager::FrameManager()
{
  tf_ = new tf::TransformListener(ros::NodeHandle(), ros::Duration(60), false);
}

FrameManager::~FrameManager()
{
  delete tf_;
}

void FrameManager::update()
{
  boost::mutex::scoped_lock lock(cache_mutex_);
  cache_.clear();
}

void FrameManager::setFixedFrame(const std::string& frame)
{
  boost::mutex::scoped_lock lock(cache_mutex_);
  fixed_frame_ = frame;
  cache_.clear();
}

bool FrameManager::getTransform(const std::string& frame, ros::Time time, Ogre::Vector3& position, Ogre::Quaternion& orientation, bool relative_orientation)
{
  boost::mutex::scoped_lock lock(cache_mutex_);

  position = Ogre::Vector3(9999999, 9999999, 9999999);
  orientation = Ogre::Quaternion::IDENTITY;

  if (fixed_frame_.empty())
  {
    return false;
  }

  M_Cache::iterator it = cache_.find(CacheKey(frame, time, relative_orientation));
  if (it != cache_.end())
  {
    position = it->second.position;
    orientation = it->second.orientation;
    return true;
  }

  geometry_msgs::Pose pose;
  pose.orientation.w = 1.0f;

  if (!transform(frame, time, pose, position, orientation, relative_orientation))
  {
    return false;
  }

  cache_.insert(std::make_pair(CacheKey(frame, time, relative_orientation), CacheEntry(position, orientation)));

  return true;
}

bool FrameManager::transform(const std::string& frame, ros::Time time, const geometry_msgs::Pose& pose_msg, Ogre::Vector3& position, Ogre::Quaternion& orientation, bool relative_orientation)
{
  position = Ogre::Vector3::ZERO;
  orientation = Ogre::Quaternion::IDENTITY;

  btQuaternion btorient(pose_msg.orientation.x, pose_msg.orientation.y, pose_msg.orientation.z, pose_msg.orientation.w);
  if (btorient.x() == 0.0 && btorient.y() == 0.0 && btorient.z() == 0.0 && btorient.w() == 0.0)
  {
    btorient.setW(1.0);
  }

  tf::Stamped<tf::Pose> pose(btTransform(btorient,
                                   btVector3(pose_msg.position.x, pose_msg.position.y, pose_msg.position.z)),
                                   time, frame);
  try
  {
    tf_->transformPose( fixed_frame_, pose, pose );
  }
  catch(tf::TransformException& e)
  {
    ROS_DEBUG("Error transforming from frame '%s' to frame '%s': %s", frame.c_str(), fixed_frame_.c_str(), e.what());
    return false;
  }

  position = Ogre::Vector3(pose.getOrigin().x(), pose.getOrigin().y(), pose.getOrigin().z());
  robotToOgre(position);

  btQuaternion quat;
  pose.getBasis().getRotation( quat );
  orientation = Ogre::Quaternion::IDENTITY;

  if (relative_orientation)
  {
    ogreToRobot(orientation);
  }

  orientation = Ogre::Quaternion( quat.w(), quat.x(), quat.y(), quat.z() ) * orientation;
  robotToOgre(orientation);

  return true;
}

bool FrameManager::frameHasProblems(const std::string& frame, ros::Time time, std::string& error)
{
  if (!tf_->frameExists(frame))
  {
    error = "Frame [" + frame + "] does not exist";
    if (frame == fixed_frame_)
    {
      error = "Fixed " + error;
    }
    return true;
  }

  return false;
}

bool FrameManager::transformHasProblems(const std::string& frame, ros::Time time, std::string& error)
{
  std::string tf_error;
  bool transform_succeeded = tf_->canTransform(fixed_frame_, frame, time, &tf_error);
  if (transform_succeeded)
  {
    return false;
  }

  bool ok = true;
  ok = ok && !frameHasProblems(fixed_frame_, time, error);
  ok = ok && !frameHasProblems(frame, time, error);

  if (ok)
  {
    std::stringstream ss;
    ss << "No transform to fixed frame [" << fixed_frame_ << "].  TF error: [" << tf_error << "]";
    error = ss.str();
    ok = false;
  }

  {
    std::stringstream ss;
    ss << "For frame [" << frame << "]: " << error;
    error = ss.str();
  }

  return !ok;
}

std::string getTransformStatusName(const std::string& caller_id)
{
  std::stringstream ss;
  ss << "Transform [sender=" << caller_id << "]";
  return ss.str();
}

std::string FrameManager::discoverFailureReason(const roslib::Header& header, const std::string& caller_id, tf::FilterFailureReason reason)
{
  if (reason == tf::filter_failure_reasons::OutTheBack)
  {
    std::stringstream ss;
    ss << "Message removed because it is too old (frame=[" << header.frame_id << "], stamp=[" << header.stamp << "])";
    return ss.str();
  }
  else
  {
    std::string error;
    if (transformHasProblems(header.frame_id, header.stamp, error))
    {
      return error;
    }
  }

  return "Unknown reason for transform failure";
}

void FrameManager::messageArrived(const roslib::Header& header, const std::string& caller_id, Display* display)
{
  display->setStatus(status_levels::Ok, getTransformStatusName(caller_id), "Transform OK");
}

void FrameManager::messageFailed(const roslib::Header& header, const std::string& caller_id, tf::FilterFailureReason reason, Display* display)
{
  std::string status_name = getTransformStatusName(caller_id);
  std::string status_text = discoverFailureReason(header, caller_id, reason);
  display->setStatus(status_levels::Error, status_name, status_text);
}

}
<|endoftext|>


<commit_before>// Copyright 2013-2015 Stanford University
//
// 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.

#include "src/search/search_state.h"

using namespace std;
using namespace x64asm;
using namespace stoke;

void SearchState::configure(Init init, const Cfg& target, size_t size) {
  switch(init) {
  case Init::TARGET:
    configure_target(target, size);
    break;
  case Init::ZERO:
    configure_zero(target, size);
    break;
  case Init::EMPTY:
    configure_empty(target, size);
    break;
  case Init::PREVIOUS:
    // no-op
    break;
  case Init::EXTENSION:
    configure_extension(target, size);
    break;
  default:
    assert(false);
  }
}

void SearchState::configure_empty(const Cfg& target, size_t size) {
  current = Cfg({{}}, target.def_ins(), target.live_outs());
  current.get_code().push_back(target.get_code()[0]);
  for (size_t i = 1, ie = size - 1; i < ie; ++i) {
    current.get_code().push_back({NOP});
  }
  current.get_code().push_back({RET});

  best_yet = current;
  best_correct = target;
}

Code SearchState::find_sound_code(const RegSet& def_ins, const RegSet& live_outs) {
  auto diff = live_outs;
  vector<Instruction> code;

  // initialize all general purpose registers
  for (auto rit = diff.gp_begin(); rit != diff.gp_end(); ++rit) {
    auto reg = *rit;
    auto type = reg.type();
    if (type == Type::R_64 || type == Type::RAX) {
      code.push_back(Instruction(XOR_R64_R64, {reg, reg}));
    } else if (type == Type::R_32 || type == Type::EAX) {
      code.push_back(Instruction(XOR_R32_R32, {reg, reg}));
    } else if (type == Type::R_16 || type == Type::AX || type == Type::DX) {
      code.push_back(Instruction(XOR_R16_R16, {reg, reg}));
    } else if (type == Type::RL || type == Type::AL || type == Type::CL) {
      code.push_back(Instruction(XOR_RL_RL, {reg, reg}));
    } else if (type == Type::RH) {
      code.push_back(Instruction(XOR_RH_RH, {reg, reg}));
    } else if (type == Type::RB) {
      code.push_back(Instruction(XOR_RB_RB, {reg, reg}));
    }
  }

  // initialize sse registers
  for (auto rit = diff.sse_begin(); rit != diff.sse_end(); ++rit) {
    auto reg = *rit;
    auto type = reg.type();
    if (type == Type::XMM || type == Type::XMM_0) {
      code.push_back(Instruction(PXOR_XMM_XMM, {reg, reg}));
    } else if (type == Type::YMM) {
      code.push_back(Instruction(VPXOR_YMM_YMM_YMM, {reg, reg, reg}));
    }
  }

  // initialize mm registers
  for (auto rit = diff.mm_begin(); rit != diff.mm_end(); ++rit) {
    auto reg = *rit;
    code.push_back(Instruction(PXOR_MM_MM, {reg, reg}));
  }

  // flags
  bool regular = false;
  for (auto rit = diff.flags_begin(); rit != diff.flags_end(); ++rit) {
    auto reg = *rit;
    if ((reg == Constants::eflags_of() ||
         reg == Constants::eflags_zf() ||
         reg == Constants::eflags_sf() ||
         reg == Constants::eflags_af() ||
         reg == Constants::eflags_cf() ||
         reg == Constants::eflags_pf()) && !regular) {
      regular = true;
      code.push_back(Instruction(XOR_R32_R32, {Constants::rax(), Constants::rax()}));
      code.push_back(Instruction(ADD_R32_IMM32, {Constants::rax(), Imm32(0)}));
    }
  }

  // remove statements if possible
  bool changed = true;
  while (changed) {
    changed = false;
    int i = 0;
    for (auto it = code.begin(); it != code.end(); ++it, ++i) {
      vector<Instruction> copy = code;
      copy.erase(copy.begin()+i);
      if (Cfg(Code(copy.begin(), copy.end()), def_ins, live_outs).is_sound()) {
        code = copy;
        changed = true;
        break;
      }
    }
  }

  return Code(code.begin(), code.end());
}

void SearchState::configure_zero(const Cfg& target, size_t size) {
  // If nothing is live out in the target, nothing to do
  if (target.def_ins().contains(target.live_outs())) {
    configure_empty(target, size);
    return;
  }

  current = Cfg({{}}, target.def_ins(), target.live_outs());
  current.get_code().push_back(target.get_code()[0]);
  auto code = find_sound_code(target.def_ins(), target.live_outs());
  for (const auto& instr : code) {
    current.get_code().push_back(instr);
  }
  for (size_t i = code.size(), ie = size - 1; i < ie; ++i) {
    current.get_code().push_back({NOP});
  }
  current.get_code().push_back({RET});

  best_yet = current;
  best_correct = target;
}

void SearchState::configure_target(const Cfg& target, size_t size) {
  current = target;
  best_yet = target;
  best_correct = target;
}

void SearchState::configure_extension(const Cfg& target, size_t size) {
  // Add user-defined logic here ...

  // Invariant 1: Search state should agree with target on boundary conditions.
  assert(current.def_ins() == target.def_ins());
  assert(current.live_outs() == target.live_outs());

  assert(best_yet.def_ins() == target.def_ins());
  assert(best_yet.live_outs() == target.live_outs());

  assert(best_correct.def_ins() == target.def_ins());
  assert(best_correct.live_outs() == target.live_outs());

  // Invariant 2: Search state must agree on first instruction. This instruction
  // must be the label definition that appears in the target.
  assert(current.get_code()[0] == target.get_code()[0]);
  assert(best_yet.get_code()[0] == target.get_code()[0]);
  assert(best_correct.get_code()[0] == target.get_code()[0]);

  // See Search::configure for enforcement of additional invariants.
  // 3. The "best_correct" code must actually be correct
  // 4. The cost of best_yet code must be less than the cost of current.
}
<commit_msg>Have to call recompute at the end of search_state::configure_xxx methods.<commit_after>// Copyright 2013-2015 Stanford University
//
// 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.

#include "src/search/search_state.h"

using namespace std;
using namespace x64asm;
using namespace stoke;

void SearchState::configure(Init init, const Cfg& target, size_t size) {
  switch(init) {
  case Init::TARGET:
    configure_target(target, size);
    break;
  case Init::ZERO:
    configure_zero(target, size);
    break;
  case Init::EMPTY:
    configure_empty(target, size);
    break;
  case Init::PREVIOUS:
    // no-op
    break;
  case Init::EXTENSION:
    configure_extension(target, size);
    break;
  default:
    assert(false);
  }
}

void SearchState::configure_empty(const Cfg& target, size_t size) {
  current = Cfg({{}}, target.def_ins(), target.live_outs());
  current.get_code().push_back(target.get_code()[0]);
  for (size_t i = 1, ie = size - 1; i < ie; ++i) {
    current.get_code().push_back({NOP});
  }
  current.get_code().push_back({RET});
	current.recompute();

  best_yet = current;
  best_correct = target;
}

Code SearchState::find_sound_code(const RegSet& def_ins, const RegSet& live_outs) {
  auto diff = live_outs;
  vector<Instruction> code;

  // initialize all general purpose registers
  for (auto rit = diff.gp_begin(); rit != diff.gp_end(); ++rit) {
    auto reg = *rit;
    auto type = reg.type();
    if (type == Type::R_64 || type == Type::RAX) {
      code.push_back(Instruction(XOR_R64_R64, {reg, reg}));
    } else if (type == Type::R_32 || type == Type::EAX) {
      code.push_back(Instruction(XOR_R32_R32, {reg, reg}));
    } else if (type == Type::R_16 || type == Type::AX || type == Type::DX) {
      code.push_back(Instruction(XOR_R16_R16, {reg, reg}));
    } else if (type == Type::RL || type == Type::AL || type == Type::CL) {
      code.push_back(Instruction(XOR_RL_RL, {reg, reg}));
    } else if (type == Type::RH) {
      code.push_back(Instruction(XOR_RH_RH, {reg, reg}));
    } else if (type == Type::RB) {
      code.push_back(Instruction(XOR_RB_RB, {reg, reg}));
    }
  }

  // initialize sse registers
  for (auto rit = diff.sse_begin(); rit != diff.sse_end(); ++rit) {
    auto reg = *rit;
    auto type = reg.type();
    if (type == Type::XMM || type == Type::XMM_0) {
      code.push_back(Instruction(PXOR_XMM_XMM, {reg, reg}));
    } else if (type == Type::YMM) {
      code.push_back(Instruction(VPXOR_YMM_YMM_YMM, {reg, reg, reg}));
    }
  }

  // initialize mm registers
  for (auto rit = diff.mm_begin(); rit != diff.mm_end(); ++rit) {
    auto reg = *rit;
    code.push_back(Instruction(PXOR_MM_MM, {reg, reg}));
  }

  // flags
  bool regular = false;
  for (auto rit = diff.flags_begin(); rit != diff.flags_end(); ++rit) {
    auto reg = *rit;
    if ((reg == Constants::eflags_of() ||
         reg == Constants::eflags_zf() ||
         reg == Constants::eflags_sf() ||
         reg == Constants::eflags_af() ||
         reg == Constants::eflags_cf() ||
         reg == Constants::eflags_pf()) && !regular) {
      regular = true;
      code.push_back(Instruction(XOR_R32_R32, {Constants::rax(), Constants::rax()}));
      code.push_back(Instruction(ADD_R32_IMM32, {Constants::rax(), Imm32(0)}));
    }
  }

  // remove statements if possible
  bool changed = true;
  while (changed) {
    changed = false;
    int i = 0;
    for (auto it = code.begin(); it != code.end(); ++it, ++i) {
      vector<Instruction> copy = code;
      copy.erase(copy.begin()+i);
      if (Cfg(Code(copy.begin(), copy.end()), def_ins, live_outs).is_sound()) {
        code = copy;
        changed = true;
        break;
      }
    }
  }

  return Code(code.begin(), code.end());
}

void SearchState::configure_zero(const Cfg& target, size_t size) {
  // If nothing is live out in the target, nothing to do
  if (target.def_ins().contains(target.live_outs())) {
    configure_empty(target, size);
    return;
  }

  current = Cfg({{}}, target.def_ins(), target.live_outs());
  current.get_code().push_back(target.get_code()[0]);
  auto code = find_sound_code(target.def_ins(), target.live_outs());
  for (const auto& instr : code) {
    current.get_code().push_back(instr);
  }
  for (size_t i = code.size(), ie = size - 1; i < ie; ++i) {
    current.get_code().push_back({NOP});
  }
  current.get_code().push_back({RET});
	current.recompute();

  best_yet = current;
  best_correct = target;
}

void SearchState::configure_target(const Cfg& target, size_t size) {
  current = target;
  best_yet = target;
  best_correct = target;
}

void SearchState::configure_extension(const Cfg& target, size_t size) {
  // Add user-defined logic here ...

  // Invariant 1: Search state should agree with target on boundary conditions.
  assert(current.def_ins() == target.def_ins());
  assert(current.live_outs() == target.live_outs());

  assert(best_yet.def_ins() == target.def_ins());
  assert(best_yet.live_outs() == target.live_outs());

  assert(best_correct.def_ins() == target.def_ins());
  assert(best_correct.live_outs() == target.live_outs());

  // Invariant 2: Search state must agree on first instruction. This instruction
  // must be the label definition that appears in the target.
  assert(current.get_code()[0] == target.get_code()[0]);
  assert(best_yet.get_code()[0] == target.get_code()[0]);
  assert(best_correct.get_code()[0] == target.get_code()[0]);

  // See Search::configure for enforcement of additional invariants.
  // 3. The "best_correct" code must actually be correct
  // 4. The cost of best_yet code must be less than the cost of current.
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// 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.
//
////////////////////////////////////////////////////////////////////////////

#include "admin_realm.hpp"

#include "event_loop_dispatcher.hpp"
#include "object_store.hpp"
#include "results.hpp"
#include "object_schema.hpp"

#include "sync/sync_config.hpp"
#include "sync/sync_manager.hpp"
#include "sync/sync_user.hpp"
#include "sync/sync_session.hpp"

#include <realm/util/file.hpp>
#include <realm/util/scope_exit.hpp>
#include <realm/util/uri.hpp>

#include <stdexcept>
#include <vector>

using namespace realm;
using namespace realm::_impl;

namespace {
sync::ObjectID id_for_row(Group& group, RowExpr row)
{
    return sync::object_id_for_row(group, *row.get_table(), row.get_index());
}
} // anonymous namespace

AdminRealmListener::AdminRealmListener(std::string local_root_dir, SyncConfig sync_config_template)
: m_local_root_dir(std::move(local_root_dir))
, m_sync_config_template(std::move(sync_config_template))
{
    m_config.cache = false;
    m_config.path = util::File::resolve("realms.realm", m_local_root_dir);
    // We explicitly set the schema to avoid a race condition where the Admin Realm may
    // have been created but its schema may not have been uploaded to the server yet.
    m_config.schema_mode = SchemaMode::Additive;
    m_config.schema = Schema{
        {"RealmFile", {
            Property{"path", PropertyType::String, Property::IsPrimary{true}},
        }},
    };
    m_config.sync_config = std::make_shared<SyncConfig>(m_sync_config_template);
    m_config.sync_config->reference_realm_url += "/__admin";
}

void AdminRealmListener::start()
{
    if (m_download_session) {
        // If we're already downloading the Realm, don't need to do anything
        return;
    }

    if (auto realm = m_results.get_realm()) {
        // If we've finished downloading the Realm, just re-report all the files listed in it
        auto& group = realm->read_group();
        auto& table = *ObjectStore::table_for_object_type(group, "RealmFile");
        size_t path_col_ndx = table.get_column_index("path");

        for (size_t i = 0, size = table.size(); i < size; ++i)
            register_realm(id_for_row(group, table[i]), table.get_string(path_col_ndx, i));
        return;
    }

    std::weak_ptr<AdminRealmListener> weak_self = shared_from_this();

    m_config.sync_config->error_handler = EventLoopDispatcher<void(std::shared_ptr<SyncSession>, SyncError)>([weak_self, this](std::shared_ptr<SyncSession>, SyncError e) {
        auto self = weak_self.lock();
        if (!self)
            return;
        error(std::make_exception_ptr(std::system_error(e.error_code)));
        m_download_session.reset();
    });

    EventLoopDispatcher<void(std::error_code)> download_callback([weak_self, this](std::error_code ec) {
        auto self = weak_self.lock();
        if (!self)
            return;

        auto cleanup = util::make_scope_exit([&]() noexcept { m_download_session.reset(); });
        if (ec) {
            if (ec == util::error::operation_aborted)
                return;
            error(std::make_exception_ptr(std::system_error(ec)));
            return;
        }
        download_complete();

        auto realm = Realm::get_shared_realm(m_config);
        m_results = Results(realm, *ObjectStore::table_for_object_type(realm->read_group(), "RealmFile")).sort({{"path", true}});

        struct Handler {
            bool initial_sent = false;
            std::weak_ptr<AdminRealmListener> weak_self;
            Handler(std::weak_ptr<AdminRealmListener> weak_self) : weak_self(std::move(weak_self)) { }

            void before(CollectionChangeSet const& c)
            {
                if (c.deletions.empty())
                    return;
                auto self = weak_self.lock();
                if (!self)
                    return;

                auto& group = self->m_results.get_realm()->read_group();
                size_t path_col_ndx = self->m_results.get(0).get_column_index("path");
                for (auto i : c.deletions.as_indexes()) {
                    auto row = self->m_results.get(i);
                    self->unregister_realm(id_for_row(group, row), row.get_string(path_col_ndx));
                }
            }

            void after(CollectionChangeSet const& c)
            {
                if (c.insertions.empty() && initial_sent)
                    return;

                auto self = weak_self.lock();
                if (!self)
                    return;
                if (self->m_results.size() == 0)
                    return;

                auto& group = self->m_results.get_realm()->read_group();
                size_t path_col_ndx = self->m_results.get(0).get_column_index("path");

                if (!initial_sent) {
                    for (size_t i = 0, size = self->m_results.size(); i < size; ++i) {
                        auto row = self->m_results.get(i);
                        self->register_realm(id_for_row(group, row), row.get_string(path_col_ndx));
                    }
                    initial_sent = true;
                }
                else {
                    for (auto i : c.insertions.as_indexes()) {
                        auto row = self->m_results.get(i);
                        self->register_realm(id_for_row(group, row), row.get_string(path_col_ndx));
                    }
                }
            }

            void error(std::exception_ptr e)
            {
                if (auto self = weak_self.lock())
                    self->error(e);
            }
        };
        m_notification_token = m_results.add_notification_callback(Handler(std::move(weak_self)));
    });
    m_download_session = SyncManager::shared().get_session(m_config.path, *m_config.sync_config);
    bool result = m_download_session->wait_for_download_completion(std::move(download_callback));
    REALM_ASSERT_RELEASE(result);
}

Realm::Config AdminRealmListener::get_config(StringData virtual_path, StringData id) const {
    Realm::Config config;

    std::string file_path = m_local_root_dir + "/realms" + virtual_path.data();
    if (id) {
        file_path += std::string("/") + id.data();
    }
    file_path += + ".realm";
    for (size_t pos = m_local_root_dir.size(); pos != file_path.npos; pos = file_path.find('/', pos + 1)) {
        file_path[pos] = '\0';
        util::try_make_dir(file_path);
        file_path[pos] = '/';
    }

    config.path = std::move(file_path);
    config.sync_config = std::make_unique<SyncConfig>(m_sync_config_template);
    config.sync_config->reference_realm_url += virtual_path.data();
    config.schema_mode = SchemaMode::Additive;
    config.cache = false;
    config.automatic_change_notifications = false;
    return config;
}
<commit_msg>Supply schema version to the GN (#478)<commit_after>////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// 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.
//
////////////////////////////////////////////////////////////////////////////

#include "admin_realm.hpp"

#include "event_loop_dispatcher.hpp"
#include "object_store.hpp"
#include "results.hpp"
#include "object_schema.hpp"

#include "sync/sync_config.hpp"
#include "sync/sync_manager.hpp"
#include "sync/sync_user.hpp"
#include "sync/sync_session.hpp"

#include <realm/util/file.hpp>
#include <realm/util/scope_exit.hpp>
#include <realm/util/uri.hpp>

#include <stdexcept>
#include <vector>

using namespace realm;
using namespace realm::_impl;

namespace {
sync::ObjectID id_for_row(Group& group, RowExpr row)
{
    return sync::object_id_for_row(group, *row.get_table(), row.get_index());
}
} // anonymous namespace

AdminRealmListener::AdminRealmListener(std::string local_root_dir, SyncConfig sync_config_template)
: m_local_root_dir(std::move(local_root_dir))
, m_sync_config_template(std::move(sync_config_template))
{
    m_config.cache = false;
    m_config.path = util::File::resolve("realms.realm", m_local_root_dir);
    // We explicitly set the schema to avoid a race condition where the Admin Realm may
    // have been created but its schema may not have been uploaded to the server yet.
    m_config.schema_mode = SchemaMode::Additive;
    m_config.schema = Schema{
        {"RealmFile", {
            Property{"path", PropertyType::String, Property::IsPrimary{true}},
        }},
    };
    m_config.schema_version = 0;
    m_config.sync_config = std::make_shared<SyncConfig>(m_sync_config_template);
    m_config.sync_config->reference_realm_url += "/__admin";
}

void AdminRealmListener::start()
{
    if (m_download_session) {
        // If we're already downloading the Realm, don't need to do anything
        return;
    }

    if (auto realm = m_results.get_realm()) {
        // If we've finished downloading the Realm, just re-report all the files listed in it
        auto& group = realm->read_group();
        auto& table = *ObjectStore::table_for_object_type(group, "RealmFile");
        size_t path_col_ndx = table.get_column_index("path");

        for (size_t i = 0, size = table.size(); i < size; ++i)
            register_realm(id_for_row(group, table[i]), table.get_string(path_col_ndx, i));
        return;
    }

    std::weak_ptr<AdminRealmListener> weak_self = shared_from_this();

    m_config.sync_config->error_handler = EventLoopDispatcher<void(std::shared_ptr<SyncSession>, SyncError)>([weak_self, this](std::shared_ptr<SyncSession>, SyncError e) {
        auto self = weak_self.lock();
        if (!self)
            return;
        error(std::make_exception_ptr(std::system_error(e.error_code)));
        m_download_session.reset();
    });

    EventLoopDispatcher<void(std::error_code)> download_callback([weak_self, this](std::error_code ec) {
        auto self = weak_self.lock();
        if (!self)
            return;

        auto cleanup = util::make_scope_exit([&]() noexcept { m_download_session.reset(); });
        if (ec) {
            if (ec == util::error::operation_aborted)
                return;
            error(std::make_exception_ptr(std::system_error(ec)));
            return;
        }
        download_complete();

        auto realm = Realm::get_shared_realm(m_config);
        m_results = Results(realm, *ObjectStore::table_for_object_type(realm->read_group(), "RealmFile")).sort({{"path", true}});

        struct Handler {
            bool initial_sent = false;
            std::weak_ptr<AdminRealmListener> weak_self;
            Handler(std::weak_ptr<AdminRealmListener> weak_self) : weak_self(std::move(weak_self)) { }

            void before(CollectionChangeSet const& c)
            {
                if (c.deletions.empty())
                    return;
                auto self = weak_self.lock();
                if (!self)
                    return;

                auto& group = self->m_results.get_realm()->read_group();
                size_t path_col_ndx = self->m_results.get(0).get_column_index("path");
                for (auto i : c.deletions.as_indexes()) {
                    auto row = self->m_results.get(i);
                    self->unregister_realm(id_for_row(group, row), row.get_string(path_col_ndx));
                }
            }

            void after(CollectionChangeSet const& c)
            {
                if (c.insertions.empty() && initial_sent)
                    return;

                auto self = weak_self.lock();
                if (!self)
                    return;
                if (self->m_results.size() == 0)
                    return;

                auto& group = self->m_results.get_realm()->read_group();
                size_t path_col_ndx = self->m_results.get(0).get_column_index("path");

                if (!initial_sent) {
                    for (size_t i = 0, size = self->m_results.size(); i < size; ++i) {
                        auto row = self->m_results.get(i);
                        self->register_realm(id_for_row(group, row), row.get_string(path_col_ndx));
                    }
                    initial_sent = true;
                }
                else {
                    for (auto i : c.insertions.as_indexes()) {
                        auto row = self->m_results.get(i);
                        self->register_realm(id_for_row(group, row), row.get_string(path_col_ndx));
                    }
                }
            }

            void error(std::exception_ptr e)
            {
                if (auto self = weak_self.lock())
                    self->error(e);
            }
        };
        m_notification_token = m_results.add_notification_callback(Handler(std::move(weak_self)));
    });
    m_download_session = SyncManager::shared().get_session(m_config.path, *m_config.sync_config);
    bool result = m_download_session->wait_for_download_completion(std::move(download_callback));
    REALM_ASSERT_RELEASE(result);
}

Realm::Config AdminRealmListener::get_config(StringData virtual_path, StringData id) const {
    Realm::Config config;

    std::string file_path = m_local_root_dir + "/realms" + virtual_path.data();
    if (id) {
        file_path += std::string("/") + id.data();
    }
    file_path += + ".realm";
    for (size_t pos = m_local_root_dir.size(); pos != file_path.npos; pos = file_path.find('/', pos + 1)) {
        file_path[pos] = '\0';
        util::try_make_dir(file_path);
        file_path[pos] = '/';
    }

    config.path = std::move(file_path);
    config.sync_config = std::make_unique<SyncConfig>(m_sync_config_template);
    config.sync_config->reference_realm_url += virtual_path.data();
    config.schema_mode = SchemaMode::Additive;
    config.cache = false;
    config.automatic_change_notifications = false;
    return config;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 deipi.com LLC and contributors. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include "server_raft.h"

#ifdef XAPIAND_CLUSTERING

#include "server.h"

#include <assert.h>

typedef void (RaftServer::* dispatch_func)(const std::string &);

RaftServer::RaftServer(const std::shared_ptr<XapiandServer>& server_, ev::loop_ref *loop_, const std::shared_ptr<Raft> &raft_)
	: BaseServer(server_, loop_, raft_->sock),
	raft(raft_)
{
	// accept event actually started in BaseServer::BaseServer
	L_EV(this, "Start raft's server accept event (sock=%d)", raft->sock);

	L_OBJ(this, "CREATED RAFT SERVER!");
}

RaftServer::~RaftServer()
{
	L_OBJ(this, "DELETED RAFT SERVER!");
}

void
RaftServer::raft_server(Raft::Message type, const std::string &message)
{
	static const dispatch_func dispatch[] = {
		&RaftServer::heartbeat_leader,
		&RaftServer::request_vote,
		&RaftServer::response_vote,
		&RaftServer::leader,
		&RaftServer::request_data,
		&RaftServer::response_data,
		&RaftServer::reset,
	};
	if (static_cast<size_t>(type) >= sizeof(dispatch) / sizeof(dispatch[0])) {
		std::string errmsg("Unexpected message type ");
		errmsg += std::to_string(toUType(type));
		throw Xapian::InvalidArgumentError(errmsg);
	}
	(this->*(dispatch[static_cast<int>(type)]))(message);
}

void RaftServer::request_vote(const std::string& message)
{
	const char *p = message.data();
	const char *p_end = p + message.size();

	Node remote_node;
	if (remote_node.unserialise(&p, p_end) == -1) {
		throw MSG_NetworkError("Badly formed message: No proper node!");
	}
	if (local_node.region.load() != remote_node.region.load()) {
		return;
	}

	raft->register_activity();

	std::string str_remote_term;
	if (unserialise_string(str_remote_term, &p, p_end) == -1) {
		L_RAFT(this, "Badly formed message: No proper term!");
		return;
	}

	uint64_t remote_term = std::stoull(str_remote_term);

	L_RAFT(this, "remote_term: %llu  local_term: %llu", remote_term, raft->term);

	if (remote_term > raft->term) {
		if (raft->state == Raft::State::LEADER && remote_node != local_node) {
			L_ERR(this, "ERROR: Node %s (with highest term) does not receive this node as a leader. Therefore, this node will reset!", remote_node.name.c_str());
			raft->reset();
		}

		raft->votedFor = lower_string(remote_node.name);
		raft->term = remote_term;

		L_RAFT(this, "It Vote for %s", raft->votedFor.c_str());
		raft->send_message(Raft::Message::RESPONSE_VOTE, remote_node.serialise() +
			serialise_string("1") + serialise_string(str_remote_term));
	} else {
		if (raft->state == Raft::State::LEADER && remote_node != local_node) {
			L_ERR(this, "ERROR: Remote node %s does not recognize this node (with highest term) as a leader. Therefore, remote node will reset!", remote_node.name.c_str());
			raft->send_message(Raft::Message::RESET, remote_node.serialise());
			return;
		}

		if (remote_term < raft->term) {
			L_RAFT(this, "Vote for %s", raft->votedFor.c_str());
			raft->send_message(Raft::Message::RESPONSE_VOTE, remote_node.serialise() +
				serialise_string("0") + serialise_string(std::to_string(raft->term)));
		} else if (raft->votedFor.empty()) {
			raft->votedFor = lower_string(remote_node.name);
			L_RAFT(this, "Vote for %s", raft->votedFor.c_str());
			raft->send_message(Raft::Message::RESPONSE_VOTE, remote_node.serialise() +
				serialise_string("1") + serialise_string(std::to_string(raft->term)));
		} else {
			L_RAFT(this, "Vote for %s", raft->votedFor.c_str());
			raft->send_message(Raft::Message::RESPONSE_VOTE, remote_node.serialise() +
				serialise_string("0") + serialise_string(std::to_string(raft->term)));
		}
	}
}

void RaftServer::response_vote(const std::string& message)
{
	const char *p = message.data();
	const char *p_end = p + message.size();

	Node remote_node;
	if (remote_node.unserialise(&p, p_end) == -1) {
		throw MSG_NetworkError("Badly formed message: No proper node!");
	}
	if (local_node.region.load() != remote_node.region.load()) {
		return;
	}

	raft->register_activity();

	if (remote_node == local_node && raft->state == Raft::State::CANDIDATE) {
		std::string vote;

		if (unserialise_string(vote, &p, p_end) == -1) {
			L_RAFT(this, "Badly formed message: No proper vote!");
			return;
		}

		if (vote == "1") {
			++raft->votes;
			L_RAFT(this, "Number of servers: %d;  Votos received: %d", raft->number_servers.load(), raft->votes);
			if (raft->votes > raft->number_servers / 2) {
				L_RAFT(this, "It becomes the leader for region: %d", local_node.region.load());
				raft->state = Raft::State::LEADER;
				raft->start_heartbeat();
			}
			return;
		}

		std::string str_remote_term;
		if (unserialise_string(str_remote_term, &p, p_end) == -1) {
			L_RAFT(this, "Badly formed message: No proper term!");
			return;
		}
		uint64_t remote_term = std::stoull(str_remote_term);

		if (raft->term < remote_term) {
			raft->term = remote_term;
			raft->state = Raft::State::FOLLOWER;
			return;
		}
	}
}

void RaftServer::heartbeat_leader(const std::string& message)
{
	const char *p = message.data();
	const char *p_end = p + message.size();

	Node remote_node;
	if (remote_node.unserialise(&p, p_end) == -1) {
		throw MSG_NetworkError("Badly formed message: No proper node!");
	}
	if (local_node.region.load() != remote_node.region.load()) {
		return;
	}

	raft->register_activity();

	if (raft->leader != lower_string(remote_node.name)) {
		L_RAFT(this, "Request the raft server's configuration!");
		raft->send_message(Raft::Message::REQUEST_DATA, local_node.serialise());
	}
	L_RAFT_PROTO(this, "Listening %s's heartbeat in timestamp: %f!", remote_node.name.c_str(), raft->last_activity);
}

void RaftServer::leader(const std::string& message)
{
	const char *p = message.data();
	const char *p_end = p + message.size();

	Node remote_node;
	if (remote_node.unserialise(&p, p_end) == -1) {
		throw MSG_NetworkError("Badly formed message: No proper node!");
	}
	if (local_node.region.load() != remote_node.region.load()) {
		return;
	}

	raft->register_activity();

	if (raft->state == Raft::State::LEADER) {
		assert(remote_node == local_node);
		return;
	}

	std::string str_servers;
	if (unserialise_string(str_servers, &p, p_end) == -1) {
		L_RAFT(this, "Badly formed message: No proper number of servers!");
		return;
	}
	raft->number_servers.store(std::stoull(str_servers));

	std::string str_remote_term;
	if (unserialise_string(str_remote_term, &p, p_end) == -1) {
		L_RAFT(this, "Badly formed message: No proper term!");
		return;
	}
	raft->term = std::stoull(str_remote_term);

	raft->leader = lower_string(remote_node.name);
	raft->state = Raft::State::FOLLOWER;
}

void RaftServer::request_data(const std::string& message)
{
	const char *p = message.data();
	const char *p_end = p + message.size();

	Node remote_node;
	if (remote_node.unserialise(&p, p_end) == -1) {
		throw MSG_NetworkError("Badly formed message: No proper node!");
	}
	if (local_node.region.load() != remote_node.region.load()) {
		return;
	}

	if (raft->state == Raft::State::LEADER) {
		L_DEBUG(this, "Sending Data!");
		raft->send_message(Raft::Message::RESPONSE_DATA, local_node.serialise() +
			serialise_string(std::to_string(raft->number_servers)) +
			serialise_string(std::to_string(raft->term)));
	}
}

void RaftServer::response_data(const std::string& message)
{
	const char *p = message.data();
	const char *p_end = p + message.size();

	Node remote_node;
	if (remote_node.unserialise(&p, p_end) == -1) {
		throw MSG_NetworkError("Badly formed message: No proper node!");
	}
	if (local_node.region.load() != remote_node.region.load()) {
		return;
	}

	raft->register_activity();

	if (raft->state == Raft::State::LEADER) {
		L_CRIT(this, "I'm leader, other responded as leader!");
		raft->reset();
		return;
	}

	L_DEBUG(this, "Receiving Data!");

	std::string str_servers;
	if (unserialise_string(str_servers, &p, p_end) == -1) {
		L_RAFT(this, "Badly formed message: No proper number of servers!");
		return;
	}
	raft->number_servers.store(std::stoull(str_servers));

	std::string str_remote_term;
	if (unserialise_string(str_remote_term, &p, p_end) == -1) {
		L_RAFT(this, "Badly formed message: No proper term!");
		return;
	}
	raft->term = std::stoull(str_remote_term);

	raft->leader = lower_string(remote_node.name);

	L_INFO(this, "Raft: New leader is %s", raft->leader.c_str());
}

void RaftServer::reset(const std::string& message)
{
	const char *p = message.data();
	const char *p_end = p + message.size();

	Node remote_node;
	if (remote_node.unserialise(&p, p_end) == -1) {
		throw MSG_NetworkError("Badly formed message: No proper node!");
	}
	if (local_node.region.load() != remote_node.region.load()) {
		return;
	}

	if (local_node == remote_node) {
		raft->reset();
	}
}

void
RaftServer::io_accept_cb(ev::io &watcher, int revents)
{
	L_EV_BEGIN(this, "RaftServer::io_accept_cb:BEGIN");
	if (EV_ERROR & revents) {
		L_EV(this, "ERROR: got invalid raft event (sock=%d): %s", raft->sock, strerror(errno));
		L_EV_END(this, "RaftServer::io_accept_cb:END");
		return;
	}

	assert(raft->sock == watcher.fd || raft->sock == -1);

	if (revents & EV_READ) {
		try {
			std::string message;
			Raft::Message type = static_cast<Raft::Message>(raft->get_message(message, static_cast<char>(Raft::Message::MAX)));
			if (type != Raft::Message::HEARTBEAT_LEADER) {
				L_RAFT(this, ">> get_message(%s)", Raft::MessageNames[static_cast<int>(type)]);
			}
			L_RAFT_PROTO(this, "message: '%s'", repr(message).c_str());

			raft_server(type, message);
		} catch (...) {
			L_EV_END(this, "RaftServer::io_accept_cb:END %lld", now);
			throw;
		}
	}

	L_EV_END(this, "RaftServer::io_accept_cb:END %lld", now);
}

#endif
<commit_msg>Raft: Log leader changes<commit_after>/*
 * Copyright (C) 2015 deipi.com LLC and contributors. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include "server_raft.h"

#ifdef XAPIAND_CLUSTERING

#include "server.h"

#include <assert.h>

typedef void (RaftServer::* dispatch_func)(const std::string &);

RaftServer::RaftServer(const std::shared_ptr<XapiandServer>& server_, ev::loop_ref *loop_, const std::shared_ptr<Raft> &raft_)
	: BaseServer(server_, loop_, raft_->sock),
	raft(raft_)
{
	// accept event actually started in BaseServer::BaseServer
	L_EV(this, "Start raft's server accept event (sock=%d)", raft->sock);

	L_OBJ(this, "CREATED RAFT SERVER!");
}

RaftServer::~RaftServer()
{
	L_OBJ(this, "DELETED RAFT SERVER!");
}

void
RaftServer::raft_server(Raft::Message type, const std::string &message)
{
	static const dispatch_func dispatch[] = {
		&RaftServer::heartbeat_leader,
		&RaftServer::request_vote,
		&RaftServer::response_vote,
		&RaftServer::leader,
		&RaftServer::request_data,
		&RaftServer::response_data,
		&RaftServer::reset,
	};
	if (static_cast<size_t>(type) >= sizeof(dispatch) / sizeof(dispatch[0])) {
		std::string errmsg("Unexpected message type ");
		errmsg += std::to_string(toUType(type));
		throw Xapian::InvalidArgumentError(errmsg);
	}
	(this->*(dispatch[static_cast<int>(type)]))(message);
}

void RaftServer::request_vote(const std::string& message)
{
	const char *p = message.data();
	const char *p_end = p + message.size();

	Node remote_node;
	if (remote_node.unserialise(&p, p_end) == -1) {
		throw MSG_NetworkError("Badly formed message: No proper node!");
	}
	if (local_node.region.load() != remote_node.region.load()) {
		return;
	}

	raft->register_activity();

	std::string str_remote_term;
	if (unserialise_string(str_remote_term, &p, p_end) == -1) {
		L_RAFT(this, "Badly formed message: No proper term!");
		return;
	}

	uint64_t remote_term = std::stoull(str_remote_term);

	L_RAFT(this, "remote_term: %llu  local_term: %llu", remote_term, raft->term);

	if (remote_term > raft->term) {
		if (raft->state == Raft::State::LEADER && remote_node != local_node) {
			L_ERR(this, "ERROR: Node %s (with highest term) does not receive this node as a leader. Therefore, this node will reset!", remote_node.name.c_str());
			raft->reset();
		}

		raft->votedFor = lower_string(remote_node.name);
		raft->term = remote_term;

		L_RAFT(this, "It Vote for %s", raft->votedFor.c_str());
		raft->send_message(Raft::Message::RESPONSE_VOTE, remote_node.serialise() +
			serialise_string("1") + serialise_string(str_remote_term));
	} else {
		if (raft->state == Raft::State::LEADER && remote_node != local_node) {
			L_ERR(this, "ERROR: Remote node %s does not recognize this node (with highest term) as a leader. Therefore, remote node will reset!", remote_node.name.c_str());
			raft->send_message(Raft::Message::RESET, remote_node.serialise());
			return;
		}

		if (remote_term < raft->term) {
			L_RAFT(this, "Vote for %s", raft->votedFor.c_str());
			raft->send_message(Raft::Message::RESPONSE_VOTE, remote_node.serialise() +
				serialise_string("0") + serialise_string(std::to_string(raft->term)));
		} else if (raft->votedFor.empty()) {
			raft->votedFor = lower_string(remote_node.name);
			L_RAFT(this, "Vote for %s", raft->votedFor.c_str());
			raft->send_message(Raft::Message::RESPONSE_VOTE, remote_node.serialise() +
				serialise_string("1") + serialise_string(std::to_string(raft->term)));
		} else {
			L_RAFT(this, "Vote for %s", raft->votedFor.c_str());
			raft->send_message(Raft::Message::RESPONSE_VOTE, remote_node.serialise() +
				serialise_string("0") + serialise_string(std::to_string(raft->term)));
		}
	}
}

void RaftServer::response_vote(const std::string& message)
{
	const char *p = message.data();
	const char *p_end = p + message.size();

	Node remote_node;
	if (remote_node.unserialise(&p, p_end) == -1) {
		throw MSG_NetworkError("Badly formed message: No proper node!");
	}
	if (local_node.region.load() != remote_node.region.load()) {
		return;
	}

	raft->register_activity();

	if (remote_node == local_node && raft->state == Raft::State::CANDIDATE) {
		std::string vote;

		if (unserialise_string(vote, &p, p_end) == -1) {
			L_RAFT(this, "Badly formed message: No proper vote!");
			return;
		}

		if (vote == "1") {
			++raft->votes;
			L_RAFT(this, "Number of servers: %d;  Votos received: %d", raft->number_servers.load(), raft->votes);
			if (raft->votes > raft->number_servers / 2) {
				L_RAFT(this, "It becomes the leader for region: %d", local_node.region.load());

				raft->state = Raft::State::LEADER;
				raft->leader = lower_string(local_node.name);

				L_INFO(this, "Raft: New leader is %s (1)", raft->leader.c_str());

				raft->start_heartbeat();
			}
			return;
		}

		std::string str_remote_term;
		if (unserialise_string(str_remote_term, &p, p_end) == -1) {
			L_RAFT(this, "Badly formed message: No proper term!");
			return;
		}
		uint64_t remote_term = std::stoull(str_remote_term);

		if (raft->term < remote_term) {
			raft->term = remote_term;
			raft->state = Raft::State::FOLLOWER;
			return;
		}
	}
}

void RaftServer::heartbeat_leader(const std::string& message)
{
	const char *p = message.data();
	const char *p_end = p + message.size();

	Node remote_node;
	if (remote_node.unserialise(&p, p_end) == -1) {
		throw MSG_NetworkError("Badly formed message: No proper node!");
	}
	if (local_node.region.load() != remote_node.region.load()) {
		return;
	}

	raft->register_activity();

	if (raft->leader != lower_string(remote_node.name)) {
		L_RAFT(this, "Request the raft server's configuration!");
		raft->send_message(Raft::Message::REQUEST_DATA, local_node.serialise());
	}
	L_RAFT_PROTO(this, "Listening %s's heartbeat in timestamp: %f!", remote_node.name.c_str(), raft->last_activity);
}

void RaftServer::leader(const std::string& message)
{
	const char *p = message.data();
	const char *p_end = p + message.size();

	Node remote_node;
	if (remote_node.unserialise(&p, p_end) == -1) {
		throw MSG_NetworkError("Badly formed message: No proper node!");
	}
	if (local_node.region.load() != remote_node.region.load()) {
		return;
	}

	raft->register_activity();

	if (raft->state == Raft::State::LEADER) {
		assert(remote_node == local_node);
		return;
	}

	std::string str_servers;
	if (unserialise_string(str_servers, &p, p_end) == -1) {
		L_RAFT(this, "Badly formed message: No proper number of servers!");
		return;
	}
	raft->number_servers.store(std::stoull(str_servers));

	std::string str_remote_term;
	if (unserialise_string(str_remote_term, &p, p_end) == -1) {
		L_RAFT(this, "Badly formed message: No proper term!");
		return;
	}
	raft->term = std::stoull(str_remote_term);

	raft->leader = lower_string(remote_node.name);
	raft->state = Raft::State::FOLLOWER;

	L_INFO(this, "Raft: New leader is %s (2)", raft->leader.c_str());
}

void RaftServer::request_data(const std::string& message)
{
	const char *p = message.data();
	const char *p_end = p + message.size();

	Node remote_node;
	if (remote_node.unserialise(&p, p_end) == -1) {
		throw MSG_NetworkError("Badly formed message: No proper node!");
	}
	if (local_node.region.load() != remote_node.region.load()) {
		return;
	}

	if (raft->state == Raft::State::LEADER) {
		L_DEBUG(this, "Sending Data!");
		raft->send_message(Raft::Message::RESPONSE_DATA, local_node.serialise() +
			serialise_string(std::to_string(raft->number_servers)) +
			serialise_string(std::to_string(raft->term)));
	}
}

void RaftServer::response_data(const std::string& message)
{
	const char *p = message.data();
	const char *p_end = p + message.size();

	Node remote_node;
	if (remote_node.unserialise(&p, p_end) == -1) {
		throw MSG_NetworkError("Badly formed message: No proper node!");
	}
	if (local_node.region.load() != remote_node.region.load()) {
		return;
	}

	raft->register_activity();

	if (raft->state == Raft::State::LEADER) {
		L_CRIT(this, "I'm leader, other responded as leader!");
		raft->reset();
		return;
	}

	L_DEBUG(this, "Receiving Data!");

	std::string str_servers;
	if (unserialise_string(str_servers, &p, p_end) == -1) {
		L_RAFT(this, "Badly formed message: No proper number of servers!");
		return;
	}
	raft->number_servers.store(std::stoull(str_servers));

	std::string str_remote_term;
	if (unserialise_string(str_remote_term, &p, p_end) == -1) {
		L_RAFT(this, "Badly formed message: No proper term!");
		return;
	}
	raft->term = std::stoull(str_remote_term);

	raft->leader = lower_string(remote_node.name);

	L_INFO(this, "Raft: New leader is %s (3)", raft->leader.c_str());
}

void RaftServer::reset(const std::string& message)
{
	const char *p = message.data();
	const char *p_end = p + message.size();

	Node remote_node;
	if (remote_node.unserialise(&p, p_end) == -1) {
		throw MSG_NetworkError("Badly formed message: No proper node!");
	}
	if (local_node.region.load() != remote_node.region.load()) {
		return;
	}

	if (local_node == remote_node) {
		raft->reset();
	}
}

void
RaftServer::io_accept_cb(ev::io &watcher, int revents)
{
	L_EV_BEGIN(this, "RaftServer::io_accept_cb:BEGIN");
	if (EV_ERROR & revents) {
		L_EV(this, "ERROR: got invalid raft event (sock=%d): %s", raft->sock, strerror(errno));
		L_EV_END(this, "RaftServer::io_accept_cb:END");
		return;
	}

	assert(raft->sock == watcher.fd || raft->sock == -1);

	if (revents & EV_READ) {
		try {
			std::string message;
			Raft::Message type = static_cast<Raft::Message>(raft->get_message(message, static_cast<char>(Raft::Message::MAX)));
			if (type != Raft::Message::HEARTBEAT_LEADER) {
				L_RAFT(this, ">> get_message(%s)", Raft::MessageNames[static_cast<int>(type)]);
			}
			L_RAFT_PROTO(this, "message: '%s'", repr(message).c_str());

			raft_server(type, message);
		} catch (...) {
			L_EV_END(this, "RaftServer::io_accept_cb:END %lld", now);
			throw;
		}
	}

	L_EV_END(this, "RaftServer::io_accept_cb:END %lld", now);
}

#endif
<|endoftext|>


<commit_before>#pragma once

#include <memory>

#include "token.hpp"

namespace mstch {

class render_context;

class render_state {
 public:
  virtual std::string render(render_context& context, const token& token) = 0;
};

}
<commit_msg>virtual destructor<commit_after>#pragma once

#include <memory>

#include "token.hpp"

namespace mstch {

class render_context;

class render_state {
 public:
  virtual ~render_state() {}
  virtual std::string render(render_context& context, const token& token) = 0;
};

}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <support/lockedpool.h>
#include <support/cleanse.h>

#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif

#ifdef WIN32
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x0501
#define WIN32_LEAN_AND_MEAN 1
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#else
#include <sys/mman.h> // for mmap
#include <sys/resource.h> // for getrlimit
#include <limits.h> // for PAGESIZE
#include <unistd.h> // for sysconf
#endif

#include <algorithm>

LockedPoolManager* LockedPoolManager::_instance = nullptr;
std::once_flag LockedPoolManager::init_flag;

/*******************************************************************************/
// Utilities
//
/** Align up to power of 2 */
static inline size_t align_up(size_t x, size_t align)
{
    return (x + align - 1) & ~(align - 1);
}

/*******************************************************************************/
// Implementation: Arena

Arena::Arena(void *base_in, size_t size_in, size_t alignment_in):
    base(static_cast<char*>(base_in)), end(static_cast<char*>(base_in) + size_in), alignment(alignment_in)
{
    // Start with one free chunk that covers the entire arena
    auto it = size_to_free_chunk.emplace(size_in, base);
    chunks_free.emplace(base, it);
    chunks_free_end.emplace(base + size_in, it);
}

Arena::~Arena()
{
}

void* Arena::alloc(size_t size)
{
    // Round to next multiple of alignment
    size = align_up(size, alignment);

    // Don't handle zero-sized chunks
    if (size == 0)
        return nullptr;

    // Pick a large enough free-chunk. Returns an iterator pointing to the first element that is not less than key.
    // This allocation strategy is best-fit. According to "Dynamic Storage Allocation: A Survey and Critical Review",
    // Wilson et. al. 1995, http://www.scs.stanford.edu/14wi-cs140/sched/readings/wilson.pdf, best-fit and first-fit
    // policies seem to work well in practice.
    auto sizePtrIt = size_to_free_chunk.lower_bound(size);
    if (sizePtrIt == size_to_free_chunk.end())
        return nullptr;

    // Create the used-chunk, taking its space from the end of the free-chunk
    const size_t sizeRemaining = sizePtrIt->first - size;
    auto alloced = chunks_used.emplace(sizePtrIt->second + sizeRemaining, size).first;
    chunks_free_end.erase(sizePtrIt->second + sizePtrIt->first);
    if (sizePtrIt->first == size) {
        // whole chunk is used up
        chunks_free.erase(sizePtrIt->second);
    } else {
        // still some memory left in the chunk
        auto itRemaining = size_to_free_chunk.emplace(sizeRemaining, sizePtrIt->second);
        chunks_free[sizePtrIt->second] = itRemaining;
        chunks_free_end.emplace(sizePtrIt->second + sizeRemaining, itRemaining);
    }
    size_to_free_chunk.erase(sizePtrIt);

    return reinterpret_cast<void*>(alloced->first);
}

void Arena::free(void *ptr)
{
    // Freeing the nullptr pointer is OK.
    if (ptr == nullptr) {
        return;
    }

    // Remove chunk from used map
    auto i = chunks_used.find(static_cast<char*>(ptr));
    if (i == chunks_used.end()) {
        throw std::runtime_error("Arena: invalid or double free");
    }
    std::pair<char*, size_t> freed = *i;
    chunks_used.erase(i);

    // Coalesc freed with previous chunk
    auto prev = chunks_free_end.find(freed.first);
    if (prev != chunks_free_end.end()) {
        freed.first -= prev->second->first;
        freed.second += prev->second->first;
        size_to_free_chunk.erase(prev->second);
        chunks_free_end.erase(prev);
    }

    // Coalesc freed with chunk after freed
    auto next = chunks_free.find(freed.first + freed.second);
    if (next != chunks_free.end()) {
        freed.second += next->second->first;
        size_to_free_chunk.erase(next->second);
        chunks_free.erase(next);
    }

    // Add/set space with coalesced free chunk
    auto it = size_to_free_chunk.emplace(freed.second, freed.first);
    chunks_free[freed.first] = it;
    chunks_free_end[freed.first + freed.second] = it;
}

Arena::Stats Arena::stats() const
{
    Arena::Stats r{ 0, 0, 0, chunks_used.size(), chunks_free.size() };
    for (const auto& chunk: chunks_used)
        r.used += chunk.second;
    for (const auto& chunk: chunks_free)
        r.free += chunk.second->first;
    r.total = r.used + r.free;
    return r;
}

#ifdef ARENA_DEBUG
void printchunk(char* base, size_t sz, bool used) {
    std::cout <<
        "0x" << std::hex << std::setw(16) << std::setfill('0') << base <<
        " 0x" << std::hex << std::setw(16) << std::setfill('0') << sz <<
        " 0x" << used << std::endl;
}
void Arena::walk() const
{
    for (const auto& chunk: chunks_used)
        printchunk(chunk.first, chunk.second, true);
    std::cout << std::endl;
    for (const auto& chunk: chunks_free)
        printchunk(chunk.first, chunk.second, false);
    std::cout << std::endl;
}
#endif

/*******************************************************************************/
// Implementation: Win32LockedPageAllocator

#ifdef WIN32
/** LockedPageAllocator specialized for Windows.
 */
class Win32LockedPageAllocator: public LockedPageAllocator
{
public:
    Win32LockedPageAllocator();
    void* AllocateLocked(size_t len, bool *lockingSuccess) override;
    void FreeLocked(void* addr, size_t len) override;
    size_t GetLimit() override;
private:
    size_t page_size;
};

Win32LockedPageAllocator::Win32LockedPageAllocator()
{
    // Determine system page size in bytes
    SYSTEM_INFO sSysInfo;
    GetSystemInfo(&sSysInfo);
    page_size = sSysInfo.dwPageSize;
}
void *Win32LockedPageAllocator::AllocateLocked(size_t len, bool *lockingSuccess)
{
    len = align_up(len, page_size);
    void *addr = VirtualAlloc(nullptr, len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (addr) {
        // VirtualLock is used to attempt to keep keying material out of swap. Note
        // that it does not provide this as a guarantee, but, in practice, memory
        // that has been VirtualLock'd almost never gets written to the pagefile
        // except in rare circumstances where memory is extremely low.
        *lockingSuccess = VirtualLock(const_cast<void*>(addr), len) != 0;
    }
    return addr;
}
void Win32LockedPageAllocator::FreeLocked(void* addr, size_t len)
{
    len = align_up(len, page_size);
    memory_cleanse(addr, len);
    VirtualUnlock(const_cast<void*>(addr), len);
}

size_t Win32LockedPageAllocator::GetLimit()
{
    // TODO is there a limit on windows, how to get it?
    return std::numeric_limits<size_t>::max();
}
#endif

/*******************************************************************************/
// Implementation: PosixLockedPageAllocator

#ifndef WIN32
/** LockedPageAllocator specialized for OSes that don't try to be
 * special snowflakes.
 */
class PosixLockedPageAllocator: public LockedPageAllocator
{
public:
    PosixLockedPageAllocator();
    void* AllocateLocked(size_t len, bool *lockingSuccess) override;
    void FreeLocked(void* addr, size_t len) override;
    size_t GetLimit() override;
private:
    size_t page_size;
};

PosixLockedPageAllocator::PosixLockedPageAllocator()
{
    // Determine system page size in bytes
#if defined(PAGESIZE) // defined in limits.h
    page_size = PAGESIZE;
#else                   // assume some POSIX OS
    page_size = sysconf(_SC_PAGESIZE);
#endif
}

// Some systems (at least OS X) do not define MAP_ANONYMOUS yet and define
// MAP_ANON which is deprecated
#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS MAP_ANON
#endif

void *PosixLockedPageAllocator::AllocateLocked(size_t len, bool *lockingSuccess)
{
    void *addr;
    len = align_up(len, page_size);
    addr = mmap(nullptr, len, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
    if (addr) {
        *lockingSuccess = mlock(addr, len) == 0;
    }
    return addr;
}
void PosixLockedPageAllocator::FreeLocked(void* addr, size_t len)
{
    len = align_up(len, page_size);
    memory_cleanse(addr, len);
    munlock(addr, len);
    munmap(addr, len);
}
size_t PosixLockedPageAllocator::GetLimit()
{
#ifdef RLIMIT_MEMLOCK
    struct rlimit rlim;
    if (getrlimit(RLIMIT_MEMLOCK, &rlim) == 0) {
        if (rlim.rlim_cur != RLIM_INFINITY) {
            return rlim.rlim_cur;
        }
    }
#endif
    return std::numeric_limits<size_t>::max();
}
#endif

/*******************************************************************************/
// Implementation: LockedPool

LockedPool::LockedPool(std::unique_ptr<LockedPageAllocator> allocator_in, LockingFailed_Callback lf_cb_in):
    allocator(std::move(allocator_in)), lf_cb(lf_cb_in), cumulative_bytes_locked(0)
{
}

LockedPool::~LockedPool()
{
}
void* LockedPool::alloc(size_t size)
{
    std::lock_guard<std::mutex> lock(mutex);

    // Don't handle impossible sizes
    if (size == 0 || size > ARENA_SIZE)
        return nullptr;

    // Try allocating from each current arena
    for (auto &arena: arenas) {
        void *addr = arena.alloc(size);
        if (addr) {
            return addr;
        }
    }
    // If that fails, create a new one
    if (new_arena(ARENA_SIZE, ARENA_ALIGN)) {
        return arenas.back().alloc(size);
    }
    return nullptr;
}

void LockedPool::free(void *ptr)
{
    std::lock_guard<std::mutex> lock(mutex);
    // TODO we can do better than this linear search by keeping a map of arena
    // extents to arena, and looking up the address.
    for (auto &arena: arenas) {
        if (arena.addressInArena(ptr)) {
            arena.free(ptr);
            return;
        }
    }
    throw std::runtime_error("LockedPool: invalid address not pointing to any arena");
}

LockedPool::Stats LockedPool::stats() const
{
    std::lock_guard<std::mutex> lock(mutex);
    LockedPool::Stats r{0, 0, 0, cumulative_bytes_locked, 0, 0};
    for (const auto &arena: arenas) {
        Arena::Stats i = arena.stats();
        r.used += i.used;
        r.free += i.free;
        r.total += i.total;
        r.chunks_used += i.chunks_used;
        r.chunks_free += i.chunks_free;
    }
    return r;
}

bool LockedPool::new_arena(size_t size, size_t align)
{
    bool locked;
    // If this is the first arena, handle this specially: Cap the upper size
    // by the process limit. This makes sure that the first arena will at least
    // be locked. An exception to this is if the process limit is 0:
    // in this case no memory can be locked at all so we'll skip past this logic.
    if (arenas.empty()) {
        size_t limit = allocator->GetLimit();
        if (limit > 0) {
            size = std::min(size, limit);
        }
    }
    void *addr = allocator->AllocateLocked(size, &locked);
    if (!addr) {
        return false;
    }
    if (locked) {
        cumulative_bytes_locked += size;
    } else if (lf_cb) { // Call the locking-failed callback if locking failed
        if (!lf_cb()) { // If the callback returns false, free the memory and fail, otherwise consider the user warned and proceed.
            allocator->FreeLocked(addr, size);
            return false;
        }
    }
    arenas.emplace_back(allocator.get(), addr, size, align);
    return true;
}

LockedPool::LockedPageArena::LockedPageArena(LockedPageAllocator *allocator_in, void *base_in, size_t size_in, size_t align_in):
    Arena(base_in, size_in, align_in), base(base_in), size(size_in), allocator(allocator_in)
{
}
LockedPool::LockedPageArena::~LockedPageArena()
{
    allocator->FreeLocked(base, size);
}

/*******************************************************************************/
// Implementation: LockedPoolManager
//
LockedPoolManager::LockedPoolManager(std::unique_ptr<LockedPageAllocator> allocator_in):
    LockedPool(std::move(allocator_in), &LockedPoolManager::LockingFailed)
{
}

bool LockedPoolManager::LockingFailed()
{
    // TODO: log something but how? without including util.h
    return true;
}

void LockedPoolManager::CreateInstance()
{
    // Using a local static instance guarantees that the object is initialized
    // when it's first needed and also deinitialized after all objects that use
    // it are done with it.  I can think of one unlikely scenario where we may
    // have a static deinitialization order/problem, but the check in
    // LockedPoolManagerBase's destructor helps us detect if that ever happens.
#ifdef WIN32
    std::unique_ptr<LockedPageAllocator> allocator(new Win32LockedPageAllocator());
#else
    std::unique_ptr<LockedPageAllocator> allocator(new PosixLockedPageAllocator());
#endif
    static LockedPoolManager instance(std::move(allocator));
    LockedPoolManager::_instance = &instance;
}
<commit_msg>fix nits: variable naming, typos<commit_after>// Copyright (c) 2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <support/lockedpool.h>
#include <support/cleanse.h>

#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif

#ifdef WIN32
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x0501
#define WIN32_LEAN_AND_MEAN 1
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#else
#include <sys/mman.h> // for mmap
#include <sys/resource.h> // for getrlimit
#include <limits.h> // for PAGESIZE
#include <unistd.h> // for sysconf
#endif

#include <algorithm>

LockedPoolManager* LockedPoolManager::_instance = nullptr;
std::once_flag LockedPoolManager::init_flag;

/*******************************************************************************/
// Utilities
//
/** Align up to power of 2 */
static inline size_t align_up(size_t x, size_t align)
{
    return (x + align - 1) & ~(align - 1);
}

/*******************************************************************************/
// Implementation: Arena

Arena::Arena(void *base_in, size_t size_in, size_t alignment_in):
    base(static_cast<char*>(base_in)), end(static_cast<char*>(base_in) + size_in), alignment(alignment_in)
{
    // Start with one free chunk that covers the entire arena
    auto it = size_to_free_chunk.emplace(size_in, base);
    chunks_free.emplace(base, it);
    chunks_free_end.emplace(base + size_in, it);
}

Arena::~Arena()
{
}

void* Arena::alloc(size_t size)
{
    // Round to next multiple of alignment
    size = align_up(size, alignment);

    // Don't handle zero-sized chunks
    if (size == 0)
        return nullptr;

    // Pick a large enough free-chunk. Returns an iterator pointing to the first element that is not less than key.
    // This allocation strategy is best-fit. According to "Dynamic Storage Allocation: A Survey and Critical Review",
    // Wilson et. al. 1995, http://www.scs.stanford.edu/14wi-cs140/sched/readings/wilson.pdf, best-fit and first-fit
    // policies seem to work well in practice.
    auto size_ptr_it = size_to_free_chunk.lower_bound(size);
    if (size_ptr_it == size_to_free_chunk.end())
        return nullptr;

    // Create the used-chunk, taking its space from the end of the free-chunk
    const size_t size_remaining = size_ptr_it->first - size;
    auto alloced = chunks_used.emplace(size_ptr_it->second + size_remaining, size).first;
    chunks_free_end.erase(size_ptr_it->second + size_ptr_it->first);
    if (size_ptr_it->first == size) {
        // whole chunk is used up
        chunks_free.erase(size_ptr_it->second);
    } else {
        // still some memory left in the chunk
        auto it_remaining = size_to_free_chunk.emplace(size_remaining, size_ptr_it->second);
        chunks_free[size_ptr_it->second] = it_remaining;
        chunks_free_end.emplace(size_ptr_it->second + size_remaining, it_remaining);
    }
    size_to_free_chunk.erase(size_ptr_it);

    return reinterpret_cast<void*>(alloced->first);
}

void Arena::free(void *ptr)
{
    // Freeing the nullptr pointer is OK.
    if (ptr == nullptr) {
        return;
    }

    // Remove chunk from used map
    auto i = chunks_used.find(static_cast<char*>(ptr));
    if (i == chunks_used.end()) {
        throw std::runtime_error("Arena: invalid or double free");
    }
    std::pair<char*, size_t> freed = *i;
    chunks_used.erase(i);

    // coalesce freed with previous chunk
    auto prev = chunks_free_end.find(freed.first);
    if (prev != chunks_free_end.end()) {
        freed.first -= prev->second->first;
        freed.second += prev->second->first;
        size_to_free_chunk.erase(prev->second);
        chunks_free_end.erase(prev);
    }

    // coalesce freed with chunk after freed
    auto next = chunks_free.find(freed.first + freed.second);
    if (next != chunks_free.end()) {
        freed.second += next->second->first;
        size_to_free_chunk.erase(next->second);
        chunks_free.erase(next);
    }

    // Add/set space with coalesced free chunk
    auto it = size_to_free_chunk.emplace(freed.second, freed.first);
    chunks_free[freed.first] = it;
    chunks_free_end[freed.first + freed.second] = it;
}

Arena::Stats Arena::stats() const
{
    Arena::Stats r{ 0, 0, 0, chunks_used.size(), chunks_free.size() };
    for (const auto& chunk: chunks_used)
        r.used += chunk.second;
    for (const auto& chunk: chunks_free)
        r.free += chunk.second->first;
    r.total = r.used + r.free;
    return r;
}

#ifdef ARENA_DEBUG
void printchunk(char* base, size_t sz, bool used) {
    std::cout <<
        "0x" << std::hex << std::setw(16) << std::setfill('0') << base <<
        " 0x" << std::hex << std::setw(16) << std::setfill('0') << sz <<
        " 0x" << used << std::endl;
}
void Arena::walk() const
{
    for (const auto& chunk: chunks_used)
        printchunk(chunk.first, chunk.second, true);
    std::cout << std::endl;
    for (const auto& chunk: chunks_free)
        printchunk(chunk.first, chunk.second, false);
    std::cout << std::endl;
}
#endif

/*******************************************************************************/
// Implementation: Win32LockedPageAllocator

#ifdef WIN32
/** LockedPageAllocator specialized for Windows.
 */
class Win32LockedPageAllocator: public LockedPageAllocator
{
public:
    Win32LockedPageAllocator();
    void* AllocateLocked(size_t len, bool *lockingSuccess) override;
    void FreeLocked(void* addr, size_t len) override;
    size_t GetLimit() override;
private:
    size_t page_size;
};

Win32LockedPageAllocator::Win32LockedPageAllocator()
{
    // Determine system page size in bytes
    SYSTEM_INFO sSysInfo;
    GetSystemInfo(&sSysInfo);
    page_size = sSysInfo.dwPageSize;
}
void *Win32LockedPageAllocator::AllocateLocked(size_t len, bool *lockingSuccess)
{
    len = align_up(len, page_size);
    void *addr = VirtualAlloc(nullptr, len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (addr) {
        // VirtualLock is used to attempt to keep keying material out of swap. Note
        // that it does not provide this as a guarantee, but, in practice, memory
        // that has been VirtualLock'd almost never gets written to the pagefile
        // except in rare circumstances where memory is extremely low.
        *lockingSuccess = VirtualLock(const_cast<void*>(addr), len) != 0;
    }
    return addr;
}
void Win32LockedPageAllocator::FreeLocked(void* addr, size_t len)
{
    len = align_up(len, page_size);
    memory_cleanse(addr, len);
    VirtualUnlock(const_cast<void*>(addr), len);
}

size_t Win32LockedPageAllocator::GetLimit()
{
    // TODO is there a limit on windows, how to get it?
    return std::numeric_limits<size_t>::max();
}
#endif

/*******************************************************************************/
// Implementation: PosixLockedPageAllocator

#ifndef WIN32
/** LockedPageAllocator specialized for OSes that don't try to be
 * special snowflakes.
 */
class PosixLockedPageAllocator: public LockedPageAllocator
{
public:
    PosixLockedPageAllocator();
    void* AllocateLocked(size_t len, bool *lockingSuccess) override;
    void FreeLocked(void* addr, size_t len) override;
    size_t GetLimit() override;
private:
    size_t page_size;
};

PosixLockedPageAllocator::PosixLockedPageAllocator()
{
    // Determine system page size in bytes
#if defined(PAGESIZE) // defined in limits.h
    page_size = PAGESIZE;
#else                   // assume some POSIX OS
    page_size = sysconf(_SC_PAGESIZE);
#endif
}

// Some systems (at least OS X) do not define MAP_ANONYMOUS yet and define
// MAP_ANON which is deprecated
#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS MAP_ANON
#endif

void *PosixLockedPageAllocator::AllocateLocked(size_t len, bool *lockingSuccess)
{
    void *addr;
    len = align_up(len, page_size);
    addr = mmap(nullptr, len, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
    if (addr) {
        *lockingSuccess = mlock(addr, len) == 0;
    }
    return addr;
}
void PosixLockedPageAllocator::FreeLocked(void* addr, size_t len)
{
    len = align_up(len, page_size);
    memory_cleanse(addr, len);
    munlock(addr, len);
    munmap(addr, len);
}
size_t PosixLockedPageAllocator::GetLimit()
{
#ifdef RLIMIT_MEMLOCK
    struct rlimit rlim;
    if (getrlimit(RLIMIT_MEMLOCK, &rlim) == 0) {
        if (rlim.rlim_cur != RLIM_INFINITY) {
            return rlim.rlim_cur;
        }
    }
#endif
    return std::numeric_limits<size_t>::max();
}
#endif

/*******************************************************************************/
// Implementation: LockedPool

LockedPool::LockedPool(std::unique_ptr<LockedPageAllocator> allocator_in, LockingFailed_Callback lf_cb_in):
    allocator(std::move(allocator_in)), lf_cb(lf_cb_in), cumulative_bytes_locked(0)
{
}

LockedPool::~LockedPool()
{
}
void* LockedPool::alloc(size_t size)
{
    std::lock_guard<std::mutex> lock(mutex);

    // Don't handle impossible sizes
    if (size == 0 || size > ARENA_SIZE)
        return nullptr;

    // Try allocating from each current arena
    for (auto &arena: arenas) {
        void *addr = arena.alloc(size);
        if (addr) {
            return addr;
        }
    }
    // If that fails, create a new one
    if (new_arena(ARENA_SIZE, ARENA_ALIGN)) {
        return arenas.back().alloc(size);
    }
    return nullptr;
}

void LockedPool::free(void *ptr)
{
    std::lock_guard<std::mutex> lock(mutex);
    // TODO we can do better than this linear search by keeping a map of arena
    // extents to arena, and looking up the address.
    for (auto &arena: arenas) {
        if (arena.addressInArena(ptr)) {
            arena.free(ptr);
            return;
        }
    }
    throw std::runtime_error("LockedPool: invalid address not pointing to any arena");
}

LockedPool::Stats LockedPool::stats() const
{
    std::lock_guard<std::mutex> lock(mutex);
    LockedPool::Stats r{0, 0, 0, cumulative_bytes_locked, 0, 0};
    for (const auto &arena: arenas) {
        Arena::Stats i = arena.stats();
        r.used += i.used;
        r.free += i.free;
        r.total += i.total;
        r.chunks_used += i.chunks_used;
        r.chunks_free += i.chunks_free;
    }
    return r;
}

bool LockedPool::new_arena(size_t size, size_t align)
{
    bool locked;
    // If this is the first arena, handle this specially: Cap the upper size
    // by the process limit. This makes sure that the first arena will at least
    // be locked. An exception to this is if the process limit is 0:
    // in this case no memory can be locked at all so we'll skip past this logic.
    if (arenas.empty()) {
        size_t limit = allocator->GetLimit();
        if (limit > 0) {
            size = std::min(size, limit);
        }
    }
    void *addr = allocator->AllocateLocked(size, &locked);
    if (!addr) {
        return false;
    }
    if (locked) {
        cumulative_bytes_locked += size;
    } else if (lf_cb) { // Call the locking-failed callback if locking failed
        if (!lf_cb()) { // If the callback returns false, free the memory and fail, otherwise consider the user warned and proceed.
            allocator->FreeLocked(addr, size);
            return false;
        }
    }
    arenas.emplace_back(allocator.get(), addr, size, align);
    return true;
}

LockedPool::LockedPageArena::LockedPageArena(LockedPageAllocator *allocator_in, void *base_in, size_t size_in, size_t align_in):
    Arena(base_in, size_in, align_in), base(base_in), size(size_in), allocator(allocator_in)
{
}
LockedPool::LockedPageArena::~LockedPageArena()
{
    allocator->FreeLocked(base, size);
}

/*******************************************************************************/
// Implementation: LockedPoolManager
//
LockedPoolManager::LockedPoolManager(std::unique_ptr<LockedPageAllocator> allocator_in):
    LockedPool(std::move(allocator_in), &LockedPoolManager::LockingFailed)
{
}

bool LockedPoolManager::LockingFailed()
{
    // TODO: log something but how? without including util.h
    return true;
}

void LockedPoolManager::CreateInstance()
{
    // Using a local static instance guarantees that the object is initialized
    // when it's first needed and also deinitialized after all objects that use
    // it are done with it.  I can think of one unlikely scenario where we may
    // have a static deinitialization order/problem, but the check in
    // LockedPoolManagerBase's destructor helps us detect if that ever happens.
#ifdef WIN32
    std::unique_ptr<LockedPageAllocator> allocator(new Win32LockedPageAllocator());
#else
    std::unique_ptr<LockedPageAllocator> allocator(new PosixLockedPageAllocator());
#endif
    static LockedPoolManager instance(std::move(allocator));
    LockedPoolManager::_instance = &instance;
}
<|endoftext|>