<commit_before>#include <vigir_generic_params/parameter_manager.h>

namespace vigir_generic_params
{
ParameterManager::Ptr ParameterManager::singelton_ = ParameterManager::Ptr();

ParameterManager::ParameterManager()
  : active_parameter_set_(new ParameterSet())
{
}

ParameterManager::~ParameterManager()
{
  nh_.shutdown();
}

void ParameterManager::initialize(ros::NodeHandle& nh)
{
  Instance()->nh_ = nh;

  // load parameter sets if given
  if (Instance()->nh_.hasParam("params_path"))
  {
    std::string path;
    Instance()->nh_.getParam("params_path", path);
    vigir_generic_params::ParameterManager::loadParameterSets(path);

    std::vector<std::string> names;
    vigir_generic_params::ParameterManager::getParameterSetNames(names);

    if (names.empty())
    {
      ROS_ERROR("[ParameterManager] Couldn't load any parameter files!");
      exit(1);
    }

    if (!Instance()->nh_.hasParam("default_params") || !vigir_generic_params::ParameterManager::setActive(Instance()->nh_.param("default_params", std::string())))
      vigir_generic_params::ParameterManager::setActive(names.front());
  }

  // subscribe topics
  Instance()->update_parameter_set_sub_ = Instance()->nh_.subscribe("params/update_parameter_set", 1, &ParameterManager::updateParameterSet, Instance().get());

  // start own services
  Instance()->set_parameter_set_srv_ = Instance()->nh_.advertiseService("params/set_parameter_set", &ParameterManager::setParameterSetService, Instance().get());
  Instance()->get_parameter_set_srv_ = Instance()->nh_.advertiseService("params/get_parameter_set", &ParameterManager::getParameterSetService, Instance().get());
  Instance()->get_all_parameter_sets_srv_ = Instance()->nh_.advertiseService("params/get_all_parameter_sets", &ParameterManager::getAllParameterSetsService, Instance().get());
  Instance()->get_parameter_set_names_srv_ = Instance()->nh_.advertiseService("params/get_parameter_set_names", &ParameterManager::getParameterSetNamesService, Instance().get());

  // init action servers
  Instance()->set_parameter_set_as_.reset(new SetParameterSetActionServer(Instance()->nh_, "params/set_parameter_set", boost::bind(&ParameterManager::setParameterSetAction, Instance().get(), _1), false));
  Instance()->get_parameter_set_as_.reset(new GetParameterSetActionServer(Instance()->nh_, "params/get_parameter_set", boost::bind(&ParameterManager::getParameterSetAction, Instance().get(), _1), false));
  Instance()->get_all_parameter_sets_as_.reset(new GetAllParameterSetsActionServer(Instance()->nh_, "params/get_all_parameter_sets", boost::bind(&ParameterManager::getAllParameterSetsAction, Instance().get(), _1), false));
  Instance()->get_parameter_set_names_as_.reset(new GetParameterSetNamesActionServer(Instance()->nh_, "params/get_parameter_set_names", boost::bind(&ParameterManager::getParameterSetNamesAction, Instance().get(), _1), false));

  // start action servers
  Instance()->set_parameter_set_as_->start();
  Instance()->get_parameter_set_as_->start();
  Instance()->get_all_parameter_sets_as_->start();
  Instance()->get_parameter_set_names_as_->start();
}

ParameterManager::Ptr ParameterManager::Instance()
{
 if (!singelton_)
    singelton_.reset(new ParameterManager());
 return singelton_;
}

void ParameterManager::clear()
{
  Instance()->param_sets_.clear();
  Instance()->active_parameter_set_.reset(new ParameterSet());
}

bool ParameterManager::empty()
{
  return Instance()->param_sets_.empty();
}

size_t ParameterManager::size()
{
  return Instance()->param_sets_.size();
}

bool ParameterManager::loadFromFile(const boost::filesystem::path& path, ParameterSet& params)
{
  // load yaml file into rosparam server
  std::string name_space = ros::this_node::getNamespace() + "/" + path.filename().c_str();
  name_space.resize(name_space.size()-5);
  std::replace(name_space.begin(), name_space.end(), '.', '_');

  std::string cmd = "rosparam load " + std::string(path.c_str()) + " " + std::string(name_space.c_str());
  if (system(cmd.c_str()))
    return false;

  // get parameter as XmlRpcValue
  ros::NodeHandle nh(name_space);
  XmlRpc::XmlRpcValue val;
  nh.getParam("/", val);

  // cleanup
  cmd = "rosparam delete " + std::string(name_space.c_str());
  if (system(cmd.c_str()))
    return false;

  // parse XmlRpcValue
  params.clear();
  return params.fromXmlRpcValue(val);
}

void ParameterManager::loadParameterSets(const std::string& path)
{
  if (!boost::filesystem::exists(path))
  {
    ROS_ERROR("[loadParameterSets] Path not found: '%s'", path.c_str());
    return;
  }

  if (!boost::filesystem::is_directory(path))
  {
    ROS_ERROR("[loadParameterSets] '%s' is not a directory!", path.c_str());
    return;
  }

  // cycle through the directory
  for(boost::filesystem::directory_iterator itr(path); itr != boost::filesystem::directory_iterator(); itr++)
  {
    if (boost::filesystem::is_regular_file(*itr) and itr->path().extension() == ".yaml")
    {
      ParameterSet params;
      if (Instance()->loadFromFile(itr->path(), params))
      {
        if (!hasParameterSet(params.getName()))
        {
          updateParameterSet(params);
          ROS_INFO("[loadParameterSets] Loaded '%s' from '%s' with %u parameters", params.getName().c_str(), itr->path().filename().c_str(), params.size());
        }
        else
          ROS_WARN("[loadParameterSets] Set named '%s' already exist. Parameters from '%s' with %u parameters can't be added.", params.getName().c_str(), itr->path().filename().c_str(), params.size());
      }
      else
        ROS_ERROR("[loadParameterSets] Couldn't load parameters from '%s'", itr->path().filename().c_str());
    }
  }

  if (Instance()->param_sets_.empty())
    ROS_ERROR("Couldn't load any parameters!");
}

void ParameterManager::updateParameterSet(const ParameterSet& params)
{
  Instance()->param_sets_[params.getName()] = params;
  ROS_INFO("Updated parameter set '%s'.", params.getName().c_str());
}

void ParameterManager::updateParameterSet(const ParameterSetMsg& params)
{
  Instance()->param_sets_[params.name.data].fromMsg(params);
  ROS_INFO("Updated parameter set '%s'.", params.name.data.c_str());
}

bool ParameterManager::getParameterSet(const std::string& name, ParameterSet& params)
{
  std::map<std::string, ParameterSet>::const_iterator itr = Instance()->param_sets_.find(name);
  if (itr == Instance()->param_sets_.end())
    return false;

  params = itr->second;
  return true;
}

bool ParameterManager::getParameterSet(const std::string& name, ParameterSetMsg& params)
{
  std::map<std::string, ParameterSet>::const_iterator itr = Instance()->param_sets_.find(name);
  if (itr == Instance()->param_sets_.end())
    return false;

  itr->second.toMsg(params);
  return true;
}

void ParameterManager::getAllParameterSets(std::vector<ParameterSet>& param_sets)
{
  param_sets.clear();
  for (std::map<std::string, ParameterSet>::const_iterator itr = Instance()->param_sets_.begin(); itr != Instance()->param_sets_.end(); itr++)
    param_sets.push_back(itr->second);
}

void ParameterManager::getAllParameterSets(std::vector<ParameterSetMsg>& param_sets)
{
  param_sets.clear();
  ParameterSetMsg params;
  for (std::map<std::string, ParameterSet>::const_iterator itr = Instance()->param_sets_.begin(); itr != Instance()->param_sets_.end(); itr++)
  {
    itr->second.toMsg(params);
    param_sets.push_back(params);
  }
}

void ParameterManager::removeParameterSet(const std::string& name)
{
  Instance()->param_sets_.erase(name);

  if (Instance()->active_parameter_set_->getName() == name)
    Instance()->active_parameter_set_.reset(new ParameterSet());
}

bool ParameterManager::hasParameterSet(const std::string& name)
{
  return Instance()->param_sets_.find(name) != Instance()->param_sets_.end();
}

void ParameterManager::getParameterSetNames(std::vector<std::string>& names)
{
  names.clear();

  for (std::map<std::string, ParameterSet>::const_iterator itr = Instance()->param_sets_.begin(); itr != Instance()->param_sets_.end(); itr++)
    names.push_back(itr->first);
}

void ParameterManager::getParameterSetNames(std::vector<std_msgs::String>& names)
{
  names.clear();

  std_msgs::String name;
  for (std::map<std::string, ParameterSet>::const_iterator itr = Instance()->param_sets_.begin(); itr != Instance()->param_sets_.end(); itr++)
  {
    name.data = itr->first;
    names.push_back(name);
  }
}

bool ParameterManager::setActive(const std::string& name)
{
  if (getParameterSet(name, *Instance()->active_parameter_set_))
  {
    ROS_INFO("[ParameterManager] Set '%s' as active parameter set.", name.c_str());
  }
  else
  {
    ROS_ERROR("[ParameterManager] Can't set '%s' as active parameter set!", name.c_str());
    return false;
  }
  return true;
}

const ParameterSet& ParameterManager::getActive()
{
  return *(Instance()->active_parameter_set_);
}

// --- Subscriber calls ---

void ParameterManager::updateParameterSet(const ParameterSetMsgConstPtr& params)
{
  updateParameterSet(*params);
}

// --- Service calls ---

bool ParameterManager::setParameterSetService(SetParameterSetService::Request& req, SetParameterSetService::Response& resp)
{
  updateParameterSet(req.params);
  return true;
}

bool ParameterManager::getParameterSetService(GetParameterSetService::Request& req, GetParameterSetService::Response& resp)
{
  getParameterSet(req.name.data, resp.params);
  return true;
}

bool ParameterManager::getAllParameterSetsService(GetAllParameterSetsService::Request& req, GetAllParameterSetsService::Response& resp)
{
  getAllParameterSets(resp.param_sets);
  return true;
}

bool ParameterManager::getParameterSetNamesService(GetParameterSetNamesService::Request& req, GetParameterSetNamesService::Response& resp)
{
  getParameterSetNames(resp.names);
  return true;
}

// --- Action Server calls ---

void ParameterManager::setParameterSetAction(const SetParameterSetGoalConstPtr& goal)
{
  // check if new goal was preempted in the meantime
  if (set_parameter_set_as_->isPreemptRequested())
  {
    set_parameter_set_as_->setPreempted();
    return;
  }

  SetParameterSetResult result;
  updateParameterSet(goal->params);

  set_parameter_set_as_->setSucceeded(result);
}

void ParameterManager::getParameterSetAction(const GetParameterSetGoalConstPtr& goal)
{
  // check if new goal was preempted in the meantime
  if (get_parameter_set_as_->isPreemptRequested())
  {
    get_parameter_set_as_->setPreempted();
    return;
  }

  GetParameterSetResult result;
  if (getParameterSet(goal->name.data, result.params))
    get_parameter_set_as_->setSucceeded(result);
  else
    get_parameter_set_as_->setAborted(result, "[ParameterManager] getParameterSetAction: Couldn't get params named '" + goal->name.data + "'");
}

void ParameterManager::getAllParameterSetsAction(const GetAllParameterSetsGoalConstPtr& goal)
{
  // check if new goal was preempted in the meantime
  if (get_all_parameter_sets_as_->isPreemptRequested())
  {
    get_all_parameter_sets_as_->setPreempted();
    return;
  }

  GetAllParameterSetsResult result;
  getAllParameterSets(result.param_sets);

  get_all_parameter_sets_as_->setSucceeded(result);
}

void ParameterManager::getParameterSetNamesAction(const GetParameterSetNamesGoalConstPtr& goal)
{
  // check if new goal was preempted in the meantime
  if (get_parameter_set_names_as_->isPreemptRequested())
  {
    get_parameter_set_names_as_->setPreempted();
    return;
  }

  GetParameterSetNamesResult result;
  getParameterSetNames(result.names);

  get_parameter_set_names_as_->setSucceeded(result);
}
} // namespace
<commit_msg>fixed error when reinitializing services<commit_after>#include <vigir_generic_params/parameter_manager.h>

namespace vigir_generic_params
{
ParameterManager::Ptr ParameterManager::singelton_ = ParameterManager::Ptr();

ParameterManager::ParameterManager()
  : active_parameter_set_(new ParameterSet())
{
}

ParameterManager::~ParameterManager()
{
  nh_.shutdown();
}

void ParameterManager::initialize(ros::NodeHandle& nh)
{
  Instance()->nh_ = nh;

  // clear old services
  Instance()->set_parameter_set_srv_ = ros::ServiceServer();
  Instance()->get_parameter_set_srv_ = ros::ServiceServer();
  Instance()->get_all_parameter_sets_srv_ = ros::ServiceServer();
  Instance()->get_parameter_set_names_srv_ = ros::ServiceServer();

  // load parameter sets if given
  if (Instance()->nh_.hasParam("params_path"))
  {
    std::string path;
    Instance()->nh_.getParam("params_path", path);
    ParameterManager::loadParameterSets(path);

    std::vector<std::string> names;
    ParameterManager::getParameterSetNames(names);

    if (names.empty())
    {
      ROS_ERROR("[ParameterManager] Couldn't load any parameter files!");
      exit(1);
    }

    if (!Instance()->nh_.hasParam("default_params") || !ParameterManager::setActive(Instance()->nh_.param("default_params", std::string())))
      ParameterManager::setActive(names.front());
  }

  // subscribe topics
  Instance()->update_parameter_set_sub_ = Instance()->nh_.subscribe("params/update_parameter_set", 1, &ParameterManager::updateParameterSet, Instance().get());

  // start own services
  Instance()->set_parameter_set_srv_ = Instance()->nh_.advertiseService("params/set_parameter_set", &ParameterManager::setParameterSetService, Instance().get());
  Instance()->get_parameter_set_srv_ = Instance()->nh_.advertiseService("params/get_parameter_set", &ParameterManager::getParameterSetService, Instance().get());
  Instance()->get_all_parameter_sets_srv_ = Instance()->nh_.advertiseService("params/get_all_parameter_sets", &ParameterManager::getAllParameterSetsService, Instance().get());
  Instance()->get_parameter_set_names_srv_ = Instance()->nh_.advertiseService("params/get_parameter_set_names", &ParameterManager::getParameterSetNamesService, Instance().get());

  // init action servers
  Instance()->set_parameter_set_as_.reset(new SetParameterSetActionServer(Instance()->nh_, "params/set_parameter_set", boost::bind(&ParameterManager::setParameterSetAction, Instance().get(), _1), false));
  Instance()->get_parameter_set_as_.reset(new GetParameterSetActionServer(Instance()->nh_, "params/get_parameter_set", boost::bind(&ParameterManager::getParameterSetAction, Instance().get(), _1), false));
  Instance()->get_all_parameter_sets_as_.reset(new GetAllParameterSetsActionServer(Instance()->nh_, "params/get_all_parameter_sets", boost::bind(&ParameterManager::getAllParameterSetsAction, Instance().get(), _1), false));
  Instance()->get_parameter_set_names_as_.reset(new GetParameterSetNamesActionServer(Instance()->nh_, "params/get_parameter_set_names", boost::bind(&ParameterManager::getParameterSetNamesAction, Instance().get(), _1), false));

  // start action servers
  Instance()->set_parameter_set_as_->start();
  Instance()->get_parameter_set_as_->start();
  Instance()->get_all_parameter_sets_as_->start();
  Instance()->get_parameter_set_names_as_->start();
}

ParameterManager::Ptr ParameterManager::Instance()
{
 if (!singelton_)
    singelton_.reset(new ParameterManager());
 return singelton_;
}

void ParameterManager::clear()
{
  Instance()->param_sets_.clear();
  Instance()->active_parameter_set_.reset(new ParameterSet());
}

bool ParameterManager::empty()
{
  return Instance()->param_sets_.empty();
}

size_t ParameterManager::size()
{
  return Instance()->param_sets_.size();
}

bool ParameterManager::loadFromFile(const boost::filesystem::path& path, ParameterSet& params)
{
  // load yaml file into rosparam server
  std::string name_space = ros::this_node::getNamespace() + "/" + path.filename().c_str();
  name_space.resize(name_space.size()-5);
  std::replace(name_space.begin(), name_space.end(), '.', '_');

  std::string cmd = "rosparam load " + std::string(path.c_str()) + " " + std::string(name_space.c_str());
  if (system(cmd.c_str()))
    return false;

  // get parameter as XmlRpcValue
  ros::NodeHandle nh(name_space);
  XmlRpc::XmlRpcValue val;
  nh.getParam("/", val);

  // cleanup
  cmd = "rosparam delete " + std::string(name_space.c_str());
  if (system(cmd.c_str()))
    return false;

  // parse XmlRpcValue
  params.clear();
  return params.fromXmlRpcValue(val);
}

void ParameterManager::loadParameterSets(const std::string& path)
{
  if (!boost::filesystem::exists(path))
  {
    ROS_ERROR("[loadParameterSets] Path not found: '%s'", path.c_str());
    return;
  }

  if (!boost::filesystem::is_directory(path))
  {
    ROS_ERROR("[loadParameterSets] '%s' is not a directory!", path.c_str());
    return;
  }

  // cycle through the directory
  for(boost::filesystem::directory_iterator itr(path); itr != boost::filesystem::directory_iterator(); itr++)
  {
    if (boost::filesystem::is_regular_file(*itr) and itr->path().extension() == ".yaml")
    {
      ParameterSet params;
      if (Instance()->loadFromFile(itr->path(), params))
      {
        if (!hasParameterSet(params.getName()))
        {
          updateParameterSet(params);
          ROS_INFO("[loadParameterSets] Loaded '%s' from '%s' with %u parameters", params.getName().c_str(), itr->path().filename().c_str(), params.size());
        }
        else
          ROS_WARN("[loadParameterSets] Set named '%s' already exist. Parameters from '%s' with %u parameters can't be added.", params.getName().c_str(), itr->path().filename().c_str(), params.size());
      }
      else
        ROS_ERROR("[loadParameterSets] Couldn't load parameters from '%s'", itr->path().filename().c_str());
    }
  }

  if (Instance()->param_sets_.empty())
    ROS_ERROR("Couldn't load any parameters!");
}

void ParameterManager::updateParameterSet(const ParameterSet& params)
{
  Instance()->param_sets_[params.getName()] = params;
  ROS_INFO("Updated parameter set '%s'.", params.getName().c_str());
}

void ParameterManager::updateParameterSet(const ParameterSetMsg& params)
{
  Instance()->param_sets_[params.name.data].fromMsg(params);
  ROS_INFO("Updated parameter set '%s'.", params.name.data.c_str());
}

bool ParameterManager::getParameterSet(const std::string& name, ParameterSet& params)
{
  std::map<std::string, ParameterSet>::const_iterator itr = Instance()->param_sets_.find(name);
  if (itr == Instance()->param_sets_.end())
    return false;

  params = itr->second;
  return true;
}

bool ParameterManager::getParameterSet(const std::string& name, ParameterSetMsg& params)
{
  std::map<std::string, ParameterSet>::const_iterator itr = Instance()->param_sets_.find(name);
  if (itr == Instance()->param_sets_.end())
    return false;

  itr->second.toMsg(params);
  return true;
}

void ParameterManager::getAllParameterSets(std::vector<ParameterSet>& param_sets)
{
  param_sets.clear();
  for (std::map<std::string, ParameterSet>::const_iterator itr = Instance()->param_sets_.begin(); itr != Instance()->param_sets_.end(); itr++)
    param_sets.push_back(itr->second);
}

void ParameterManager::getAllParameterSets(std::vector<ParameterSetMsg>& param_sets)
{
  param_sets.clear();
  ParameterSetMsg params;
  for (std::map<std::string, ParameterSet>::const_iterator itr = Instance()->param_sets_.begin(); itr != Instance()->param_sets_.end(); itr++)
  {
    itr->second.toMsg(params);
    param_sets.push_back(params);
  }
}

void ParameterManager::removeParameterSet(const std::string& name)
{
  Instance()->param_sets_.erase(name);

  if (Instance()->active_parameter_set_->getName() == name)
    Instance()->active_parameter_set_.reset(new ParameterSet());
}

bool ParameterManager::hasParameterSet(const std::string& name)
{
  return Instance()->param_sets_.find(name) != Instance()->param_sets_.end();
}

void ParameterManager::getParameterSetNames(std::vector<std::string>& names)
{
  names.clear();

  for (std::map<std::string, ParameterSet>::const_iterator itr = Instance()->param_sets_.begin(); itr != Instance()->param_sets_.end(); itr++)
    names.push_back(itr->first);
}

void ParameterManager::getParameterSetNames(std::vector<std_msgs::String>& names)
{
  names.clear();

  std_msgs::String name;
  for (std::map<std::string, ParameterSet>::const_iterator itr = Instance()->param_sets_.begin(); itr != Instance()->param_sets_.end(); itr++)
  {
    name.data = itr->first;
    names.push_back(name);
  }
}

bool ParameterManager::setActive(const std::string& name)
{
  if (getParameterSet(name, *Instance()->active_parameter_set_))
  {
    ROS_INFO("[ParameterManager] Set '%s' as active parameter set.", name.c_str());
  }
  else
  {
    ROS_ERROR("[ParameterManager] Can't set '%s' as active parameter set!", name.c_str());
    return false;
  }
  return true;
}

const ParameterSet& ParameterManager::getActive()
{
  return *(Instance()->active_parameter_set_);
}

// --- Subscriber calls ---

void ParameterManager::updateParameterSet(const ParameterSetMsgConstPtr& params)
{
  updateParameterSet(*params);
}

// --- Service calls ---

bool ParameterManager::setParameterSetService(SetParameterSetService::Request& req, SetParameterSetService::Response& resp)
{
  updateParameterSet(req.params);
  return true;
}

bool ParameterManager::getParameterSetService(GetParameterSetService::Request& req, GetParameterSetService::Response& resp)
{
  getParameterSet(req.name.data, resp.params);
  return true;
}

bool ParameterManager::getAllParameterSetsService(GetAllParameterSetsService::Request& req, GetAllParameterSetsService::Response& resp)
{
  getAllParameterSets(resp.param_sets);
  return true;
}

bool ParameterManager::getParameterSetNamesService(GetParameterSetNamesService::Request& req, GetParameterSetNamesService::Response& resp)
{
  getParameterSetNames(resp.names);
  return true;
}

// --- Action Server calls ---

void ParameterManager::setParameterSetAction(const SetParameterSetGoalConstPtr& goal)
{
  // check if new goal was preempted in the meantime
  if (set_parameter_set_as_->isPreemptRequested())
  {
    set_parameter_set_as_->setPreempted();
    return;
  }

  SetParameterSetResult result;
  updateParameterSet(goal->params);

  set_parameter_set_as_->setSucceeded(result);
}

void ParameterManager::getParameterSetAction(const GetParameterSetGoalConstPtr& goal)
{
  // check if new goal was preempted in the meantime
  if (get_parameter_set_as_->isPreemptRequested())
  {
    get_parameter_set_as_->setPreempted();
    return;
  }

  GetParameterSetResult result;
  if (getParameterSet(goal->name.data, result.params))
    get_parameter_set_as_->setSucceeded(result);
  else
    get_parameter_set_as_->setAborted(result, "[ParameterManager] getParameterSetAction: Couldn't get params named '" + goal->name.data + "'");
}

void ParameterManager::getAllParameterSetsAction(const GetAllParameterSetsGoalConstPtr& goal)
{
  // check if new goal was preempted in the meantime
  if (get_all_parameter_sets_as_->isPreemptRequested())
  {
    get_all_parameter_sets_as_->setPreempted();
    return;
  }

  GetAllParameterSetsResult result;
  getAllParameterSets(result.param_sets);

  get_all_parameter_sets_as_->setSucceeded(result);
}

void ParameterManager::getParameterSetNamesAction(const GetParameterSetNamesGoalConstPtr& goal)
{
  // check if new goal was preempted in the meantime
  if (get_parameter_set_names_as_->isPreemptRequested())
  {
    get_parameter_set_names_as_->setPreempted();
    return;
  }

  GetParameterSetNamesResult result;
  getParameterSetNames(result.names);

  get_parameter_set_names_as_->setSucceeded(result);
}
} // namespace
<|endoftext|>


<commit_before>#ifndef ARGPARSE_H
#define ARGPARSE_H
#include <iosfwd>
#include <string>
#include <vector>
#include <iostream>
#include <sstream>
#include <memory>
#include <map>

#include "argparse_formatter.hpp"
#include "argparse_default_converter.hpp"
#include "argparse_error.hpp"
#include "argparse_value.hpp"

namespace argparse {

    class Argument;
    class ArgumentGroup;

    enum class Action {
        STORE,
        STORE_TRUE,
        STORE_FALSE,
        HELP,
        VERSION
    };

    enum class ShowIn {
        USAGE_AND_HELP,
        HELP_ONLY
    };

    class ArgumentParser {
        public:
            //Initializes an argument parser
            ArgumentParser(std::string prog_name, std::string description_str=std::string(), std::ostream& os=std::cout);

            //Overrides the program name
            ArgumentParser& prog(std::string prog, bool basename_only=true);

            //Sets the program version
            ArgumentParser& version(std::string version);

            //Specifies the epilog text at the bottom of the help description
            ArgumentParser& epilog(std::string prog);

            //Adds an argument or option with a single name
            template<typename T, typename Converter=DefaultConverter<T>>
            Argument& add_argument(ArgValue<T>& dest, std::string option);

            //Adds an option with a long and short option name
            template<typename T, typename Converter=DefaultConverter<T>>
            Argument& add_argument(ArgValue<T>& dest, std::string long_opt, std::string short_opt);

            //Adds a group to collect related arguments
            ArgumentGroup& add_argument_group(std::string description_str);

            //Like parse_arg_throw(), but catches exceptions and exits the program
            void parse_args(int argc, const char** argv, int error_exit_code=1, int help_exit_code=0, int version_exit_code=0);

            //Parses the specified command-line arguments and sets the appropriat argument values
            // Returns a vector of Arguments which were specified.
            //If an error occurs throws ArgParseError
            //If an help is requested occurs throws ArgParseHelp
            void parse_args_throw(int argc, const char** argv);
            void parse_args_throw(std::vector<std::string> args);

            //Prints the basic usage
            void print_usage();

            //Prints the usage and full help description for each option
            void print_help();

            //Prints the version information
            void print_version();
        public:
            //Returns the program name
            std::string prog() const;

            std::string version() const;

            //Returns the program description (after usage, but before option descriptions)
            std::string description() const;

            //Returns the epilog (end of help)
            std::string epilog() const;

            //Returns all the argument groups in this parser
            std::vector<ArgumentGroup> argument_groups() const;

        private:
            void add_help_option_if_unspecified();
        private:
            std::string prog_;
            std::string description_;
            std::string epilog_;
            std::string version_;
            std::vector<ArgumentGroup> argument_groups_;

            Formatter* formatter_;
            std::ostream& os_;
            ArgValue<bool> show_help_dummy_; //Dummy variable used as destination for automatically generated help option
    };

    class ArgumentGroup {
        public:

            //Adds an argument or option with a single name
            template<typename T, typename Converter=DefaultConverter<T>>
            Argument& add_argument(ArgValue<T>& dest, std::string option);

            //Adds an option with a long and short option name
            template<typename T, typename Converter=DefaultConverter<T>>
            Argument& add_argument(ArgValue<T>& dest, std::string long_opt, std::string short_opt);

            //Adds an epilog to the group
            ArgumentGroup& epilog(std::string str);

        public:
            //Returns the name of the group
            std::string name() const;

            //Returns the epilog
            std::string epilog() const;

            //Returns the arguments within the group
            const std::vector<std::shared_ptr<Argument>>& arguments() const;
        public:
            ArgumentGroup(const ArgumentGroup&) = default;
            ArgumentGroup(ArgumentGroup&&) = default;
            ArgumentGroup& operator=(const ArgumentGroup&) = delete;
            ArgumentGroup& operator=(const ArgumentGroup&&) = delete;
        private:
            friend class ArgumentParser;
            ArgumentGroup(std::string name_str=std::string());
        private:
            std::string name_;
            std::string epilog_;
            std::vector<std::shared_ptr<Argument>> arguments_;
    };

    class Argument {
        public:
            Argument(std::string long_opt, std::string short_opt);
        public: //Mutators
            //Sets the hlep text
            Argument& help(std::string help_str);

            //Sets the defuault value
            Argument& default_value(const std::string& default_val);

            //Sets the action
            Argument& action(Action action);

            //Sets whether this argument is required
            Argument& required(bool is_required);

            //Sets the associated metavar (if not specified, inferred from argument name, or choices)
            Argument& metavar(std::string metavar_sr);

            //Sets the expected number of arguments
            Argument& nargs(char nargs_type);

            //Sets the valid choices for this option's value
            Argument& choices(std::vector<std::string> choice_values);

            //Sets the group name this argument is associated with
            Argument& group_name(std::string grp);

            //Sets where this option appears in the help
            Argument& show_in(ShowIn show);

            //Sets the target value to the specified default
            virtual void set_dest_to_default() = 0;

            //Sets the target value to the specified value
            virtual void set_dest_to_value(std::string value) = 0;

            //Set the target value to true
            virtual void set_dest_to_true() = 0;

            //Set the target value to false
            virtual void set_dest_to_false() = 0;
        public: //Accessors

            //Returns a discriptive name build from the long/short option
            std::string name() const;

            //Returns the long option name (or positional name) for this argument.
            //Note that this may be a single-letter option if only a short option name was specified
            std::string long_option() const;

            //Returns the short option name for this argument, note that this returns
            //the empty string if no short option is specified, or if only the short option
            //is specified.
            std::string short_option() const;

            //Returns the help description for this option
            std::string help() const;

            //Returns the number of arguments this option expects
            char nargs() const;

            //Returns the specified metavar for this option
            std::string metavar() const;

            //Returns the list of valid choices for this option
            std::vector<std::string> choices() const;

            //Returns the action associated with this option
            Action action() const;

            //Returns whether this option is required
            bool required() const;

            //Returns the specified default value
            std::string default_value() const;

            //Returns the group name associated with this argument
            std::string group_name() const;

            //Indicates where this option should appear in the help
            ShowIn show_in() const;

            //Returns true if this is a positional argument
            bool positional() const;

            //Returns treu if the default_value() was set
            bool default_set() const;
        public: //Lifetime
            virtual ~Argument() {}
            Argument(const Argument&) = default;
            Argument(Argument&&) = default;
            Argument& operator=(const Argument&) = delete;
            Argument& operator=(const Argument&&) = delete;
        protected:
            virtual bool valid_action() = 0;
        private: //Data
            std::string long_opt_;
            std::string short_opt_;

            std::string help_;
            std::string metavar_;
            char nargs_ = '1';
            std::vector<std::string> choices_;
            Action action_ = Action::STORE;
            bool required_ = false;

            std::string default_value_;

            std::string group_name_;
            ShowIn show_in_ = ShowIn::USAGE_AND_HELP;
            bool default_set_ = false;
    };

    template<typename T, typename Converter=DefaultConverter<T>>
    class SingleValueArgument : public Argument {
        public: //Constructors
            SingleValueArgument(ArgValue<T>& dest, std::string long_opt, std::string short_opt)
                : Argument(long_opt, short_opt)
                , dest_(dest)
                {}
        public: //Mutators
            void set_dest_to_default() override {
                dest_.set(Converter().from_str(default_value()), Provenance::DEFAULT);
                dest_.set_argument_name(name());
                dest_.set_argument_group(group_name());
            }

            void set_dest_to_value(std::string value) override {
                dest_.set(Converter().from_str(value), Provenance::SPECIFIED);
                dest_.set_argument_name(name());
                dest_.set_argument_group(group_name());
            }

            void set_dest_to_true() override {
                throw ArgParseError("Non-boolean destination can not be set true");
            }
            void set_dest_to_false() override {
                throw ArgParseError("Non-boolean destination can not be set false");
            }

            bool valid_action() override {
                //Sanity check that we aren't processing a boolean action with a non-boolean destination
                if (action() == Action::STORE_TRUE) {
                    std::stringstream msg;
                    msg << "Non-boolean destination can not have STORE_TRUE action (" << long_option() << ")";
                    throw ArgParseError(msg.str());
                } else if (action() == Action::STORE_FALSE) {
                    std::stringstream msg;
                    msg << "Non-boolean destination can not have STORE_FALSE action (" << long_option() << ")";
                    throw ArgParseError(msg.str());
                } else if (action() != Action::STORE) {
                    throw ArgParseError("Unexpected action (expected STORE)");
                }
                return true;
            }
        private: //Data
            ArgValue<T>& dest_;
    };

    //bool specialization for STORE_TRUE/STORE_FALSE
    template<typename Converter>
    class SingleValueArgument<bool,Converter> : public Argument {
        public: //Constructors
            SingleValueArgument(ArgValue<bool>& dest, std::string long_opt, std::string short_opt)
                : Argument(long_opt, short_opt)
                , dest_(dest)
                {}
        public: //Mutators
            void set_dest_to_default() override {
                dest_.set(Converter().from_str(default_value()), Provenance::DEFAULT);
                dest_.set_argument_name(name());
                dest_.set_argument_group(group_name());
            }

            void set_dest_to_value(std::string value) override {
                dest_.set(Converter().from_str(value), Provenance::SPECIFIED);
                dest_.set_argument_name(name());
                dest_.set_argument_group(group_name());
            }

            void set_dest_to_true() override {
                dest_.set(true, Provenance::SPECIFIED);
                dest_.set_argument_name(name());
                dest_.set_argument_group(group_name());
            }

            void set_dest_to_false() override {
                dest_.set(false, Provenance::SPECIFIED);
                dest_.set_argument_name(name());
                dest_.set_argument_group(group_name());
            }

            bool valid_action() override {
                //Any supported action is valid on a boolean destination
                return true;
            }
        private: //Data
            ArgValue<bool>& dest_;
    };
} //namespace

#include "argparse.tpp"

#endif
<commit_msg>Ensure Formatter is correctly destroyed<commit_after>#ifndef ARGPARSE_H
#define ARGPARSE_H
#include <iosfwd>
#include <string>
#include <vector>
#include <iostream>
#include <sstream>
#include <memory>
#include <map>

#include "argparse_formatter.hpp"
#include "argparse_default_converter.hpp"
#include "argparse_error.hpp"
#include "argparse_value.hpp"

namespace argparse {

    class Argument;
    class ArgumentGroup;

    enum class Action {
        STORE,
        STORE_TRUE,
        STORE_FALSE,
        HELP,
        VERSION
    };

    enum class ShowIn {
        USAGE_AND_HELP,
        HELP_ONLY
    };

    class ArgumentParser {
        public:
            //Initializes an argument parser
            ArgumentParser(std::string prog_name, std::string description_str=std::string(), std::ostream& os=std::cout);

            //Overrides the program name
            ArgumentParser& prog(std::string prog, bool basename_only=true);

            //Sets the program version
            ArgumentParser& version(std::string version);

            //Specifies the epilog text at the bottom of the help description
            ArgumentParser& epilog(std::string prog);

            //Adds an argument or option with a single name
            template<typename T, typename Converter=DefaultConverter<T>>
            Argument& add_argument(ArgValue<T>& dest, std::string option);

            //Adds an option with a long and short option name
            template<typename T, typename Converter=DefaultConverter<T>>
            Argument& add_argument(ArgValue<T>& dest, std::string long_opt, std::string short_opt);

            //Adds a group to collect related arguments
            ArgumentGroup& add_argument_group(std::string description_str);

            //Like parse_arg_throw(), but catches exceptions and exits the program
            void parse_args(int argc, const char** argv, int error_exit_code=1, int help_exit_code=0, int version_exit_code=0);

            //Parses the specified command-line arguments and sets the appropriat argument values
            // Returns a vector of Arguments which were specified.
            //If an error occurs throws ArgParseError
            //If an help is requested occurs throws ArgParseHelp
            void parse_args_throw(int argc, const char** argv);
            void parse_args_throw(std::vector<std::string> args);

            //Prints the basic usage
            void print_usage();

            //Prints the usage and full help description for each option
            void print_help();

            //Prints the version information
            void print_version();
        public:
            //Returns the program name
            std::string prog() const;

            std::string version() const;

            //Returns the program description (after usage, but before option descriptions)
            std::string description() const;

            //Returns the epilog (end of help)
            std::string epilog() const;

            //Returns all the argument groups in this parser
            std::vector<ArgumentGroup> argument_groups() const;

        private:
            void add_help_option_if_unspecified();
        private:
            std::string prog_;
            std::string description_;
            std::string epilog_;
            std::string version_;
            std::vector<ArgumentGroup> argument_groups_;

            std::unique_ptr<Formatter> formatter_;
            std::ostream& os_;
            ArgValue<bool> show_help_dummy_; //Dummy variable used as destination for automatically generated help option
    };

    class ArgumentGroup {
        public:

            //Adds an argument or option with a single name
            template<typename T, typename Converter=DefaultConverter<T>>
            Argument& add_argument(ArgValue<T>& dest, std::string option);

            //Adds an option with a long and short option name
            template<typename T, typename Converter=DefaultConverter<T>>
            Argument& add_argument(ArgValue<T>& dest, std::string long_opt, std::string short_opt);

            //Adds an epilog to the group
            ArgumentGroup& epilog(std::string str);

        public:
            //Returns the name of the group
            std::string name() const;

            //Returns the epilog
            std::string epilog() const;

            //Returns the arguments within the group
            const std::vector<std::shared_ptr<Argument>>& arguments() const;
        public:
            ArgumentGroup(const ArgumentGroup&) = default;
            ArgumentGroup(ArgumentGroup&&) = default;
            ArgumentGroup& operator=(const ArgumentGroup&) = delete;
            ArgumentGroup& operator=(const ArgumentGroup&&) = delete;
        private:
            friend class ArgumentParser;
            ArgumentGroup(std::string name_str=std::string());
        private:
            std::string name_;
            std::string epilog_;
            std::vector<std::shared_ptr<Argument>> arguments_;
    };

    class Argument {
        public:
            Argument(std::string long_opt, std::string short_opt);
        public: //Mutators
            //Sets the hlep text
            Argument& help(std::string help_str);

            //Sets the defuault value
            Argument& default_value(const std::string& default_val);

            //Sets the action
            Argument& action(Action action);

            //Sets whether this argument is required
            Argument& required(bool is_required);

            //Sets the associated metavar (if not specified, inferred from argument name, or choices)
            Argument& metavar(std::string metavar_sr);

            //Sets the expected number of arguments
            Argument& nargs(char nargs_type);

            //Sets the valid choices for this option's value
            Argument& choices(std::vector<std::string> choice_values);

            //Sets the group name this argument is associated with
            Argument& group_name(std::string grp);

            //Sets where this option appears in the help
            Argument& show_in(ShowIn show);

            //Sets the target value to the specified default
            virtual void set_dest_to_default() = 0;

            //Sets the target value to the specified value
            virtual void set_dest_to_value(std::string value) = 0;

            //Set the target value to true
            virtual void set_dest_to_true() = 0;

            //Set the target value to false
            virtual void set_dest_to_false() = 0;
        public: //Accessors

            //Returns a discriptive name build from the long/short option
            std::string name() const;

            //Returns the long option name (or positional name) for this argument.
            //Note that this may be a single-letter option if only a short option name was specified
            std::string long_option() const;

            //Returns the short option name for this argument, note that this returns
            //the empty string if no short option is specified, or if only the short option
            //is specified.
            std::string short_option() const;

            //Returns the help description for this option
            std::string help() const;

            //Returns the number of arguments this option expects
            char nargs() const;

            //Returns the specified metavar for this option
            std::string metavar() const;

            //Returns the list of valid choices for this option
            std::vector<std::string> choices() const;

            //Returns the action associated with this option
            Action action() const;

            //Returns whether this option is required
            bool required() const;

            //Returns the specified default value
            std::string default_value() const;

            //Returns the group name associated with this argument
            std::string group_name() const;

            //Indicates where this option should appear in the help
            ShowIn show_in() const;

            //Returns true if this is a positional argument
            bool positional() const;

            //Returns treu if the default_value() was set
            bool default_set() const;
        public: //Lifetime
            virtual ~Argument() {}
            Argument(const Argument&) = default;
            Argument(Argument&&) = default;
            Argument& operator=(const Argument&) = delete;
            Argument& operator=(const Argument&&) = delete;
        protected:
            virtual bool valid_action() = 0;
        private: //Data
            std::string long_opt_;
            std::string short_opt_;

            std::string help_;
            std::string metavar_;
            char nargs_ = '1';
            std::vector<std::string> choices_;
            Action action_ = Action::STORE;
            bool required_ = false;

            std::string default_value_;

            std::string group_name_;
            ShowIn show_in_ = ShowIn::USAGE_AND_HELP;
            bool default_set_ = false;
    };

    template<typename T, typename Converter=DefaultConverter<T>>
    class SingleValueArgument : public Argument {
        public: //Constructors
            SingleValueArgument(ArgValue<T>& dest, std::string long_opt, std::string short_opt)
                : Argument(long_opt, short_opt)
                , dest_(dest)
                {}
        public: //Mutators
            void set_dest_to_default() override {
                dest_.set(Converter().from_str(default_value()), Provenance::DEFAULT);
                dest_.set_argument_name(name());
                dest_.set_argument_group(group_name());
            }

            void set_dest_to_value(std::string value) override {
                dest_.set(Converter().from_str(value), Provenance::SPECIFIED);
                dest_.set_argument_name(name());
                dest_.set_argument_group(group_name());
            }

            void set_dest_to_true() override {
                throw ArgParseError("Non-boolean destination can not be set true");
            }
            void set_dest_to_false() override {
                throw ArgParseError("Non-boolean destination can not be set false");
            }

            bool valid_action() override {
                //Sanity check that we aren't processing a boolean action with a non-boolean destination
                if (action() == Action::STORE_TRUE) {
                    std::stringstream msg;
                    msg << "Non-boolean destination can not have STORE_TRUE action (" << long_option() << ")";
                    throw ArgParseError(msg.str());
                } else if (action() == Action::STORE_FALSE) {
                    std::stringstream msg;
                    msg << "Non-boolean destination can not have STORE_FALSE action (" << long_option() << ")";
                    throw ArgParseError(msg.str());
                } else if (action() != Action::STORE) {
                    throw ArgParseError("Unexpected action (expected STORE)");
                }
                return true;
            }
        private: //Data
            ArgValue<T>& dest_;
    };

    //bool specialization for STORE_TRUE/STORE_FALSE
    template<typename Converter>
    class SingleValueArgument<bool,Converter> : public Argument {
        public: //Constructors
            SingleValueArgument(ArgValue<bool>& dest, std::string long_opt, std::string short_opt)
                : Argument(long_opt, short_opt)
                , dest_(dest)
                {}
        public: //Mutators
            void set_dest_to_default() override {
                dest_.set(Converter().from_str(default_value()), Provenance::DEFAULT);
                dest_.set_argument_name(name());
                dest_.set_argument_group(group_name());
            }

            void set_dest_to_value(std::string value) override {
                dest_.set(Converter().from_str(value), Provenance::SPECIFIED);
                dest_.set_argument_name(name());
                dest_.set_argument_group(group_name());
            }

            void set_dest_to_true() override {
                dest_.set(true, Provenance::SPECIFIED);
                dest_.set_argument_name(name());
                dest_.set_argument_group(group_name());
            }

            void set_dest_to_false() override {
                dest_.set(false, Provenance::SPECIFIED);
                dest_.set_argument_name(name());
                dest_.set_argument_group(group_name());
            }

            bool valid_action() override {
                //Any supported action is valid on a boolean destination
                return true;
            }
        private: //Data
            ArgValue<bool>& dest_;
    };
} //namespace

#include "argparse.tpp"

#endif
<|endoftext|>


<commit_before>#include <rpp/tests.h>
#include <rpp/thread_pool.h>
#include <rpp/timer.h> // performance measurement
#include <atomic>
#include <unordered_set>
using namespace rpp;
using std::mutex;
using std::atomic;
using std::atomic_int;
using std::unordered_set;
using namespace std::this_thread;
using namespace std::chrono_literals;

TestImpl(test_threadpool)
{
    TestInit(test_threadpool)
    {
        print_info("physical_cores: %d\n", thread_pool::physical_cores());
    }

    static int parallelism_count(int numIterations)
    {
        mutex m;
        unordered_set<std::thread::id> ids;
        parallel_for(0, numIterations, [&](int start, int end)
        {
            ::sleep_for(1ms);
            { std::lock_guard<mutex> lock{m};
                ids.insert(::get_id());
            }
        });
        std::lock_guard<mutex> lock{m};
        return (int)ids.size();
    }

    TestCase(threadpool_concurrency)
    {
        AssertThat(parallelism_count(1), 1);
        AssertThat(parallelism_count(128), thread_pool::physical_cores());
    }

    TestCase(generic_task)
    {
        semaphore sync;
        string s = "Data";
        rpp::task_delegate<void()> fun = [x=s, &s, &sync]()
        {
            //printf("generic_task: %s\n", x.c_str());
            s = "completed";
            sync.notify();
        };
        parallel_task([f=move(fun)]()
        {
            f();
        });

        sync.wait();
        AssertThat(s, "completed");
    }

    TestCase(parallel_for_performance)
    {
        auto numbers = vector<int>(13333337);
        int* ptr = numbers.data();
        int  len = (int)numbers.size();

        parallel_for(0, len, [&](int start, int end) {
            for (int i = start; i < end; ++i)
                ptr[i] = i;
        });
        //#pragma loop(hint_parallel(0))
        //for (int i = 0; i < len; ++i)
        //    ptr[i] = rand() % 32768;
        //concurrency::parallel_for(0, len, [&](int index) {
        //    ptr[index] = index;
        //});

        Timer timer;

        atomic<int64_t> sum { 0 };;
        parallel_for(0, (int)numbers.size(), [&](int start, int end) {
            int64_t isum = 0;
            for (int i = start; i < end; ++i)
                isum += ptr[i];
            sum += isum; // only touch atomic int once
        });
        //#pragma loop(hint_parallel(0))
        //for (int i = 0; i < len; ++i)
        //    sum += ptr[i];
        //concurrency::parallel_for(size_t(0), numbers.size(), [&](int index) {
        //    sum += ptr[index];
        //});
        double parallel_elapsed = timer.elapsed();
        print_info("ParallelFor  elapsed: %.3fs  result: %lld\n", parallel_elapsed, (long long)sum);
        AssertThat((long long)sum, 88888931111116LL);

        timer.start();
        int64_t sum2 = 0;
        for (int i = 0; i < len; ++i)
            sum2 += ptr[i];
        double serial_elapsed = timer.elapsed();

        print_info("Singlethread elapsed: %.3fs  result: %lld\n", serial_elapsed, (long long)sum2);
        AssertThat((long long)sum2, 88888931111116LL);
        AssertLessOrEqual(parallel_elapsed, serial_elapsed);
    }

    TestCase(parallel_foreach)
    {
        auto numbers = vector<int>(1337);
        parallel_foreach(numbers, [](int& n) {
            n = 1;
        });

        int checksum = 0;
        for (int i : numbers) checksum += i;
        AssertThat(checksum, 1337);
    }

    TestCase(repeat_tests)
    {
        for (int i = 0; i < 2; ++i)
        {
            test_threadpool_concurrency();
            test_generic_task();
            test_parallel_for_performance();
        }
    }

    TestCaseExpectedEx(parallel_task_exception, std::logic_error)
    {
        int times_launched = 0; // this makes sure the threadpool loop doesn't retrigger our task
        auto* task = rpp::parallel_task([&]() {
            AssertThat(times_launched, 0);
            ++times_launched;
            throw std::logic_error("aaargh!");
        });
        task->wait(); // @note this should rethrow
    }

    TestCase(parallel_task_reentrance)
    {
        int times_launched = 0;
        auto* task = rpp::parallel_task([&] {
            ++times_launched;
            ::sleep_for(10ms);
        });
        task->wait();
        AssertThat(times_launched, 1);

        task = rpp::parallel_task([&] {
            ++times_launched;
            ::sleep_for(10ms);
        });
        task->wait();
        AssertThat(times_launched, 2);
    }

    TestCase(parallel_task_resurrection)
    {
        thread_pool::global().max_task_idle_time(0.3f);
        thread_pool::global().clear_idle_tasks();
        AssertThat(thread_pool::global().active_tasks(), 0);

        atomic_int times_launched {0};
        rpp::parallel_task([&] { times_launched += 1; })->wait(1000);
        AssertThat((int)times_launched, 1);

        print_info("Waiting for pool tasks to die naturally...\n");
        ::sleep_for(0.5s);
        print_info("Attempting pool task resurrection\n");
        rpp::parallel_task([&] {
            times_launched += 1;
        })->wait(1000);
        AssertThat((int)times_launched, 2);

        thread_pool::global().max_task_idle_time(2);
    }

    TestCase(parallel_task_nested_nodeadlocks)
    {
        atomic_int times_launched {0};
        auto func = [&]() {
            times_launched += 1;
            pool_task* subtasks[5] = {
                parallel_task([&]() { times_launched += 1; }),
                parallel_task([&]() { times_launched += 1; }),
                parallel_task([&]() { times_launched += 1; }),
                parallel_task([&]() { times_launched += 1; }),
                parallel_task([&]() { times_launched += 1; }),
            };

            for (auto* task : subtasks)
                AssertThat(task->wait(1000), pool_task::finished);
        };
        pool_task* tasks[4] = {
            parallel_task(func),
            parallel_task(func),
            parallel_task(func),
            parallel_task(func),
        };

        for (auto* task : tasks)
            AssertThat(task->wait(1000), pool_task::finished);

        int expected = 4 * 6;
        AssertThat((int)times_launched, expected);
    }

};<commit_msg>Special case for parallel performance test (CI machines have 1 or 2 cores)<commit_after>#include <rpp/tests.h>
#include <rpp/thread_pool.h>
#include <rpp/timer.h> // performance measurement
#include <atomic>
#include <unordered_set>
using namespace rpp;
using std::mutex;
using std::atomic;
using std::atomic_int;
using std::unordered_set;
using namespace std::this_thread;
using namespace std::chrono_literals;

TestImpl(test_threadpool)
{
    TestInit(test_threadpool)
    {
        print_info("physical_cores: %d\n", thread_pool::physical_cores());
    }

    static int parallelism_count(int numIterations)
    {
        mutex m;
        unordered_set<std::thread::id> ids;
        parallel_for(0, numIterations, [&](int start, int end)
        {
            ::sleep_for(1ms);
            { std::lock_guard<mutex> lock{m};
                ids.insert(::get_id());
            }
        });
        std::lock_guard<mutex> lock{m};
        return (int)ids.size();
    }

    TestCase(threadpool_concurrency)
    {
        AssertThat(parallelism_count(1), 1);
        AssertThat(parallelism_count(128), thread_pool::physical_cores());
    }

    TestCase(generic_task)
    {
        semaphore sync;
        string s = "Data";
        rpp::task_delegate<void()> fun = [x=s, &s, &sync]()
        {
            //printf("generic_task: %s\n", x.c_str());
            s = "completed";
            sync.notify();
        };
        parallel_task([f=move(fun)]()
        {
            f();
        });

        sync.wait();
        AssertThat(s, "completed");
    }

    TestCase(parallel_for_performance)
    {
        auto numbers = vector<int>(13333337);
        int* ptr = numbers.data();
        int  len = (int)numbers.size();

        parallel_for(0, len, [&](int start, int end) {
            for (int i = start; i < end; ++i)
                ptr[i] = i;
        });
        //#pragma loop(hint_parallel(0))
        //for (int i = 0; i < len; ++i)
        //    ptr[i] = rand() % 32768;
        //concurrency::parallel_for(0, len, [&](int index) {
        //    ptr[index] = index;
        //});

        Timer timer1;

        atomic<int64_t> sum { 0 };;
        parallel_for(0, (int)numbers.size(), [&](int start, int end) {
            int64_t isum = 0;
            for (int i = start; i < end; ++i)
                isum += ptr[i];
            sum += isum; // only touch atomic int once
        });
        //#pragma loop(hint_parallel(0))
        //for (int i = 0; i < len; ++i)
        //    sum += ptr[i];
        //concurrency::parallel_for(size_t(0), numbers.size(), [&](int index) {
        //    sum += ptr[index];
        //});
        double parallel_elapsed = timer1.elapsed();
        print_info("ParallelFor  elapsed: %.3fs  result: %lld\n", parallel_elapsed, (long long)sum);
        AssertThat((long long)sum, 88888931111116LL);

        Timer timer2;
        int64_t sum2 = 0;
        for (int i = 0; i < len; ++i)
            sum2 += ptr[i];
        double serial_elapsed = timer2.elapsed();

        print_info("Singlethread elapsed: %.3fs  result: %lld\n", serial_elapsed, (long long)sum2);
        AssertThat((long long)sum2, 88888931111116LL);

        int cores = thread_pool::physical_cores();
        if (cores > 2)
            AssertLessOrEqual(parallel_elapsed, serial_elapsed);
        else // if the system doesn't have enough cores, the overhead should be minimal
            AssertLessOrEqual(parallel_elapsed, serial_elapsed+0.001);
    }

    TestCase(parallel_foreach)
    {
        auto numbers = vector<int>(1337);
        parallel_foreach(numbers, [](int& n) {
            n = 1;
        });

        int checksum = 0;
        for (int i : numbers) checksum += i;
        AssertThat(checksum, 1337);
    }

    TestCase(repeat_tests)
    {
        for (int i = 0; i < 2; ++i)
        {
            test_threadpool_concurrency();
            test_generic_task();
            test_parallel_for_performance();
        }
    }

    TestCaseExpectedEx(parallel_task_exception, std::logic_error)
    {
        int times_launched = 0; // this makes sure the threadpool loop doesn't retrigger our task
        auto* task = rpp::parallel_task([&]() {
            AssertThat(times_launched, 0);
            ++times_launched;
            throw std::logic_error("aaargh!");
        });
        task->wait(); // @note this should rethrow
    }

    TestCase(parallel_task_reentrance)
    {
        int times_launched = 0;
        auto* task = rpp::parallel_task([&] {
            ++times_launched;
            ::sleep_for(10ms);
        });
        task->wait();
        AssertThat(times_launched, 1);

        task = rpp::parallel_task([&] {
            ++times_launched;
            ::sleep_for(10ms);
        });
        task->wait();
        AssertThat(times_launched, 2);
    }

    TestCase(parallel_task_resurrection)
    {
        thread_pool::global().max_task_idle_time(0.3f);
        thread_pool::global().clear_idle_tasks();
        AssertThat(thread_pool::global().active_tasks(), 0);

        atomic_int times_launched {0};
        rpp::parallel_task([&] { times_launched += 1; })->wait(1000);
        AssertThat((int)times_launched, 1);

        print_info("Waiting for pool tasks to die naturally...\n");
        ::sleep_for(0.5s);
        print_info("Attempting pool task resurrection\n");
        rpp::parallel_task([&] {
            times_launched += 1;
        })->wait(1000);
        AssertThat((int)times_launched, 2);

        thread_pool::global().max_task_idle_time(2);
    }

    TestCase(parallel_task_nested_nodeadlocks)
    {
        atomic_int times_launched {0};
        auto func = [&]() {
            times_launched += 1;
            pool_task* subtasks[5] = {
                parallel_task([&]() { times_launched += 1; }),
                parallel_task([&]() { times_launched += 1; }),
                parallel_task([&]() { times_launched += 1; }),
                parallel_task([&]() { times_launched += 1; }),
                parallel_task([&]() { times_launched += 1; }),
            };

            for (auto* task : subtasks)
                AssertThat(task->wait(1000), pool_task::finished);
        };
        pool_task* tasks[4] = {
            parallel_task(func),
            parallel_task(func),
            parallel_task(func),
            parallel_task(func),
        };

        for (auto* task : tasks)
            AssertThat(task->wait(1000), pool_task::finished);

        int expected = 4 * 6;
        AssertThat((int)times_launched, expected);
    }

};<|endoftext|>


<commit_before>// BMASHINA
//
// 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/.
//
// Copyright 2017 [bk]door.maus

#ifndef BMASHINA_DECORATOR_HPP
#define BMASHINA_DECORATOR_HPP

#include <cassert>
#include "bmashina/node.hpp"

#ifndef BMASHINA_DISABLE_EXCEPTION_HANDLING
#include <stdexcept>
#endif

namespace bmashina
{
	template <typename M>
	class BasicDecorator : public BasicNode<M>
	{
	public:
		using typename BasicNode<M>::Node;
		using typename BasicNode<M>::Executor;

		BasicDecorator() = default;
		~BasicDecorator() = default;

		Status update(Executor& executor) override;

	protected:
		bool has_child();
		Node& child();
	};
}

template <typename M>
bmashina::Status bmashina::BasicDecorator<M>::update(Executor& executor)
{
	if (has_child())
	{
		return child().update(executor);
	}

	return Status::success;
}

template <typename M>
bool bmashina::BasicDecorator<M>::has_child()
{
	if (!this->attached())
	{
		return false;
	}

	return this->tree().children_begin(*this) != this->tree().children_end(*this);
}

template <typename M>
typename bmashina::BasicDecorator<M>::Node& bmashina::BasicDecorator<M>::child()
{
	assert(has_child());

#ifndef BMASHINA_DISABLE_EXCEPTION_HANDLING
	if (!has_child())
	{
		throw std::runtime_error("decorator has no child");
	}
#endif

	return *this->tree().children_begin(*this);
}

#endif
<commit_msg>Fix for Visual Studio.<commit_after>// BMASHINA
//
// 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/.
//
// Copyright 2017 [bk]door.maus

#ifndef BMASHINA_DECORATOR_HPP
#define BMASHINA_DECORATOR_HPP

#include <cassert>
#include "bmashina/node.hpp"

#ifndef BMASHINA_DISABLE_EXCEPTION_HANDLING
#include <stdexcept>
#endif

namespace bmashina
{
	template <typename M>
	class BasicDecorator : public BasicNode<M>
	{
	public:
		typedef BasicNode<M> Node;
		using typename BasicNode<M>::Executor;

		BasicDecorator() = default;
		~BasicDecorator() = default;

		Status update(Executor& executor) override;

	protected:
		bool has_child();
		Node& child();
	};
}

template <typename M>
bmashina::Status bmashina::BasicDecorator<M>::update(Executor& executor)
{
	if (has_child())
	{
		return child().update(executor);
	}

	return Status::success;
}

template <typename M>
bool bmashina::BasicDecorator<M>::has_child()
{
	if (!this->attached())
	{
		return false;
	}

	return this->tree().children_begin(*this) != this->tree().children_end(*this);
}

template <typename M>
typename bmashina::BasicDecorator<M>::Node& bmashina::BasicDecorator<M>::child()
{
	assert(has_child());

#ifndef BMASHINA_DISABLE_EXCEPTION_HANDLING
	if (!has_child())
	{
		throw std::runtime_error("decorator has no child");
	}
#endif

	return *this->tree().children_begin(*this);
}

#endif
<|endoftext|>


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

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

#include "chainparams.h"
#include "clientversion.h"
#include "config.h"
#include "fs.h"
#include "httprpc.h"
#include "httpserver.h"
#include "init.h"
#include "noui.h"
#include "rpc/server.h"
#include "scheduler.h"
#include "unlimited.h"
#include "util.h"
#include "utilstrencodings.h"
#include "unlimited.h"
//#include "versionbits.h"
#include "forks_csv.h"

#include <boost/algorithm/string/predicate.hpp>
#include <boost/thread.hpp>

#include <stdio.h>

/* Introduction text for doxygen: */

/*! \mainpage Developer documentation
 *
 * \section intro_sec Introduction
 *
 //www.bitcoin.org/),
 * This is the developer documentation of the reference client for an experimental new digital currency called Bitcoin
 (https:
 * which enables instant payments to anyone, anywhere in the world. Bitcoin uses peer-to-peer technology to operate
 * with no central authority: managing transactions and issuing money are carried out collectively by the network.
 *
 * The software is a community-driven open source project, released under the MIT license.
 *
 * \section Navigation
 * Use the buttons Namespaces, Classes or Files at the top of the page to start
 navigating the code.
 */

static bool fDaemon;

void WaitForShutdown(boost::thread_group *threadGroup)
{
    bool fShutdown = ShutdownRequested();
    // Tell the main threads to shutdown.
    while (!fShutdown)
    {
        MilliSleep(200);
        fShutdown = ShutdownRequested();
    }
    if (threadGroup)
    {
        Interrupt(*threadGroup);
        threadGroup->join_all();
    }
}

//////////////////////////////////////////////////////////////////////////////
//
// Start
//
bool AppInit(int argc, char *argv[])
{
    boost::thread_group threadGroup;
    CScheduler scheduler;

    auto &config = const_cast<Config &>(GetConfig());

    bool fRet = false;

    //
    // Parameters
    //
    // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main()
    AllowedArgs::Bitcoind allowedArgs(&tweaks);
    try
    {
        ParseParameters(argc, argv, allowedArgs);
    }
    catch (const std::exception &e)
    {
        fprintf(stderr, "Error parsing program options: %s\n", e.what());
        return false;
    }

    // Process help and version before taking care about datadir
    if (mapArgs.count("-?") || mapArgs.count("-h") || mapArgs.count("-help") || mapArgs.count("-version"))
    {
        std::string strUsage =
            strprintf(_("%s Daemon"), _(PACKAGE_NAME)) + " " + _("version") + " " + FormatFullVersion() + "\n";

        if (mapArgs.count("-version"))
        {
            strUsage += FormatParagraph(LicenseInfo());
        }
        else
        {
            strUsage += "\n" + _("Usage:") + "\n" + "  bitcoind [options]                     " +
                        strprintf(_("Start %s Daemon"), _(PACKAGE_NAME)) + "\n";

            strUsage += "\n" + allowedArgs.helpMessage();
        }

        fprintf(stdout, "%s", strUsage.c_str());
        return true;
    }

    // bip135 begin
    // dump default deployment info and exit, if requested
    if (mapArgs.count("-dumpforks")) {
        std::string strVersion = "# " + strprintf(_("%s Daemon"), _(PACKAGE_NAME)) + " " + _("version") + " " + FormatFullVersion();
        fprintf(stdout, "%s\n%s", strVersion.c_str(), FORKS_CSV_FILE_HEADER);
        fprintf(stdout, "%s", NetworkDeploymentInfoCSV(CBaseChainParams::MAIN).c_str());
        fprintf(stdout, "%s", NetworkDeploymentInfoCSV(CBaseChainParams::UNL).c_str());
        fprintf(stdout, "%s", NetworkDeploymentInfoCSV(CBaseChainParams::TESTNET).c_str());
        fprintf(stdout, "%s", NetworkDeploymentInfoCSV(CBaseChainParams::REGTEST).c_str());
        return true;
    }
    // bip135 end

    try
    {
        if (!fs::is_directory(GetDataDir(false)))
        {
            fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str());
            return false;
        }
        try
        {
            ReadConfigFile(mapArgs, mapMultiArgs, allowedArgs);
        }
        catch (const std::exception &e)
        {
            fprintf(stderr, "Error reading configuration file: %s\n", e.what());
            return false;
        }
        // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
        try
        {
            SelectParams(ChainNameFromCommandLine());
        }
        catch (const std::exception &e)
        {
            fprintf(stderr, "Error: %s\n", e.what());
            return false;
        }

        // Command-line RPC
        bool fCommandLine = false;
        for (int i = 1; i < argc; i++)
        {
            if (!IsSwitchChar(argv[i][0]) && !boost::algorithm::istarts_with(argv[i], "bitcoin:") &&
                !boost::algorithm::istarts_with(argv[i], "bitcoincash:"))
            {
                fCommandLine = true;
                break;
            }
        }

        if (fCommandLine)
        {
            fprintf(stderr, "Error: There is no RPC client functionality in bitcoind anymore. Use the bitcoin-cli "
                            "utility instead.\n");
            return false;
        }
#ifndef WIN32
        fDaemon = GetBoolArg("-daemon", false);
        if (fDaemon)
        {
            fprintf(stdout, "Bitcoin server starting\n");

            // Daemonize
            pid_t pid = fork();
            if (pid < 0)
            {
                fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
                return false;
            }
            if (pid > 0) // Parent process, pid is child process id
            {
                return true;
            }
            // Child process falls through to rest of initialization

            pid_t sid = setsid();
            if (sid < 0)
                fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
        }
#endif
        SoftSetBoolArg("-server", true);

        // Set this early so that parameter interactions go to console
        InitLogging();
        InitParameterInteraction();
        fRet = AppInit2(config, threadGroup, scheduler);
    }
    catch (const std::exception &e)
    {
        PrintExceptionContinue(&e, "AppInit()");
    }
    catch (...)
    {
        PrintExceptionContinue(NULL, "AppInit()");
    }

    UnlimitedSetup();

    if (!fRet)
    {
        Interrupt(threadGroup);
        // threadGroup.join_all(); was left out intentionally here, because we didn't re-test all of
        // the startup-failure cases to make sure they don't result in a hang due to some
        // thread-blocking-waiting-for-another-thread-during-startup case
    }
    else
    {
        WaitForShutdown(&threadGroup);
    }
    Shutdown();

    return fRet;
}

int main(int argc, char *argv[])
{
    SetupEnvironment();

    // Connect bitcoind signal handlers
    noui_connect();

    return (AppInit(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE);
}
<commit_msg>[review] Use GetBoolArg to check dumpforks<commit_after>// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2017 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

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

#include "chainparams.h"
#include "clientversion.h"
#include "config.h"
#include "fs.h"
#include "httprpc.h"
#include "httpserver.h"
#include "init.h"
#include "noui.h"
#include "rpc/server.h"
#include "scheduler.h"
#include "unlimited.h"
#include "util.h"
#include "utilstrencodings.h"
#include "unlimited.h"
//#include "versionbits.h"
#include "forks_csv.h"

#include <boost/algorithm/string/predicate.hpp>
#include <boost/thread.hpp>

#include <stdio.h>

/* Introduction text for doxygen: */

/*! \mainpage Developer documentation
 *
 * \section intro_sec Introduction
 *
 //www.bitcoin.org/),
 * This is the developer documentation of the reference client for an experimental new digital currency called Bitcoin
 (https:
 * which enables instant payments to anyone, anywhere in the world. Bitcoin uses peer-to-peer technology to operate
 * with no central authority: managing transactions and issuing money are carried out collectively by the network.
 *
 * The software is a community-driven open source project, released under the MIT license.
 *
 * \section Navigation
 * Use the buttons Namespaces, Classes or Files at the top of the page to start
 navigating the code.
 */

static bool fDaemon;

void WaitForShutdown(boost::thread_group *threadGroup)
{
    bool fShutdown = ShutdownRequested();
    // Tell the main threads to shutdown.
    while (!fShutdown)
    {
        MilliSleep(200);
        fShutdown = ShutdownRequested();
    }
    if (threadGroup)
    {
        Interrupt(*threadGroup);
        threadGroup->join_all();
    }
}

//////////////////////////////////////////////////////////////////////////////
//
// Start
//
bool AppInit(int argc, char *argv[])
{
    boost::thread_group threadGroup;
    CScheduler scheduler;

    auto &config = const_cast<Config &>(GetConfig());

    bool fRet = false;

    //
    // Parameters
    //
    // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main()
    AllowedArgs::Bitcoind allowedArgs(&tweaks);
    try
    {
        ParseParameters(argc, argv, allowedArgs);
    }
    catch (const std::exception &e)
    {
        fprintf(stderr, "Error parsing program options: %s\n", e.what());
        return false;
    }

    // Process help and version before taking care about datadir
    if (mapArgs.count("-?") || mapArgs.count("-h") || mapArgs.count("-help") || mapArgs.count("-version"))
    {
        std::string strUsage =
            strprintf(_("%s Daemon"), _(PACKAGE_NAME)) + " " + _("version") + " " + FormatFullVersion() + "\n";

        if (mapArgs.count("-version"))
        {
            strUsage += FormatParagraph(LicenseInfo());
        }
        else
        {
            strUsage += "\n" + _("Usage:") + "\n" + "  bitcoind [options]                     " +
                        strprintf(_("Start %s Daemon"), _(PACKAGE_NAME)) + "\n";

            strUsage += "\n" + allowedArgs.helpMessage();
        }

        fprintf(stdout, "%s", strUsage.c_str());
        return true;
    }

    // bip135 begin
    // dump default deployment info and exit, if requested
    if (GetBoolArg("-dumpforks", false)) {
        std::string strVersion = "# " + strprintf(_("%s Daemon"), _(PACKAGE_NAME)) + " " + _("version") + " " + FormatFullVersion();
        fprintf(stdout, "%s\n%s", strVersion.c_str(), FORKS_CSV_FILE_HEADER);
        fprintf(stdout, "%s", NetworkDeploymentInfoCSV(CBaseChainParams::MAIN).c_str());
        fprintf(stdout, "%s", NetworkDeploymentInfoCSV(CBaseChainParams::UNL).c_str());
        fprintf(stdout, "%s", NetworkDeploymentInfoCSV(CBaseChainParams::TESTNET).c_str());
        fprintf(stdout, "%s", NetworkDeploymentInfoCSV(CBaseChainParams::REGTEST).c_str());
        return true;
    }
    // bip135 end

    try
    {
        if (!fs::is_directory(GetDataDir(false)))
        {
            fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str());
            return false;
        }
        try
        {
            ReadConfigFile(mapArgs, mapMultiArgs, allowedArgs);
        }
        catch (const std::exception &e)
        {
            fprintf(stderr, "Error reading configuration file: %s\n", e.what());
            return false;
        }
        // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
        try
        {
            SelectParams(ChainNameFromCommandLine());
        }
        catch (const std::exception &e)
        {
            fprintf(stderr, "Error: %s\n", e.what());
            return false;
        }

        // Command-line RPC
        bool fCommandLine = false;
        for (int i = 1; i < argc; i++)
        {
            if (!IsSwitchChar(argv[i][0]) && !boost::algorithm::istarts_with(argv[i], "bitcoin:") &&
                !boost::algorithm::istarts_with(argv[i], "bitcoincash:"))
            {
                fCommandLine = true;
                break;
            }
        }

        if (fCommandLine)
        {
            fprintf(stderr, "Error: There is no RPC client functionality in bitcoind anymore. Use the bitcoin-cli "
                            "utility instead.\n");
            return false;
        }
#ifndef WIN32
        fDaemon = GetBoolArg("-daemon", false);
        if (fDaemon)
        {
            fprintf(stdout, "Bitcoin server starting\n");

            // Daemonize
            pid_t pid = fork();
            if (pid < 0)
            {
                fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
                return false;
            }
            if (pid > 0) // Parent process, pid is child process id
            {
                return true;
            }
            // Child process falls through to rest of initialization

            pid_t sid = setsid();
            if (sid < 0)
                fprintf(stderr, "Error: setsid() returned %d errno %d\n", sid, errno);
        }
#endif
        SoftSetBoolArg("-server", true);

        // Set this early so that parameter interactions go to console
        InitLogging();
        InitParameterInteraction();
        fRet = AppInit2(config, threadGroup, scheduler);
    }
    catch (const std::exception &e)
    {
        PrintExceptionContinue(&e, "AppInit()");
    }
    catch (...)
    {
        PrintExceptionContinue(NULL, "AppInit()");
    }

    UnlimitedSetup();

    if (!fRet)
    {
        Interrupt(threadGroup);
        // threadGroup.join_all(); was left out intentionally here, because we didn't re-test all of
        // the startup-failure cases to make sure they don't result in a hang due to some
        // thread-blocking-waiting-for-another-thread-during-startup case
    }
    else
    {
        WaitForShutdown(&threadGroup);
    }
    Shutdown();

    return fRet;
}

int main(int argc, char *argv[])
{
    SetupEnvironment();

    // Connect bitcoind signal handlers
    noui_connect();

    return (AppInit(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE);
}
<|endoftext|>


<commit_before>#include "set.hpp"

#include "btree/modify_oper.hpp"

#include "buffer_cache/co_functions.hpp"

#include "btree/coro_wrappers.hpp"

struct btree_set_oper_t : public btree_modify_oper_t {

    explicit btree_set_oper_t(data_provider_t *data, set_type_t type, mcflags_t mcflags, exptime_t exptime, cas_t req_cas)
        : btree_modify_oper_t(), data(data), type(type), mcflags(mcflags), exptime(exptime), req_cas(req_cas)
    {
        pm_cmd_set.begin(&start_time);
    }

    ~btree_set_oper_t() {
        pm_cmd_set.end(&start_time);
    }

    bool operate(transaction_t *txn, btree_value *old_value, large_buf_lock_t& old_large_buflock, btree_value **new_value, large_buf_lock_t& new_large_buflock) {
        try {
            if ((old_value && type == set_type_add) || (!old_value && type == set_type_replace)) {
                result = store_t::sr_not_stored;
                data->discard();
                return false;
            }

            if (type == set_type_cas) { // TODO: CAS stats
                if (!old_value) {
                    result = store_t::sr_not_found;
                    data->discard();
                    return false;
                }
                if (!old_value->has_cas() || old_value->cas() != req_cas) {
                    result = store_t::sr_exists;
                    data->discard();
                    return false;
                }
            }

            if (data->get_size() > MAX_VALUE_SIZE) {
                result = store_t::sr_too_large;
                data->discard();
                /* To be standards-compliant we must delete the old value when an effort is made to
                replace it with a value that is too large. */
                *new_value = NULL;
                return true;
            }

            value.value_size(0);
            if (type == set_type_cas || (old_value && old_value->has_cas())) {
                // Turns the flag on and makes
                // room. run_btree_modify_oper() will set an actual CAS
                // later. TODO: We should probably have a separate
                // function for this.
                metadata_write(&value.metadata_flags, value.contents, mcflags, exptime, 0xCA5ADDED);
            } else {
                metadata_write(&value.metadata_flags, value.contents, mcflags, exptime);
            }

            value.value_size(data->get_size());

            large_buf_lock_t large_buflock;
            buffer_group_t buffer_group;

            rassert(data->get_size() <= MAX_VALUE_SIZE);
            if (data->get_size() <= MAX_IN_NODE_VALUE_SIZE) {
                buffer_group.add_buffer(data->get_size(), value.value());
                data->get_data_into_buffers(&buffer_group);
            } else {
                large_buflock.set(new large_buf_t(txn));
                large_buflock.lv()->allocate(data->get_size(), value.large_buf_ref_ptr());
                for (int64_t i = 0; i < large_buflock.lv()->get_num_segments(); i++) {
                    uint16_t size;
                    void *data = large_buflock.lv()->get_segment_write(i, &size);
                    buffer_group.add_buffer(size, data);
                }

                try {
                    data->get_data_into_buffers(&buffer_group);
                } catch (...) {
                    large_buflock.lv()->mark_deleted();
                    large_buflock.release();
                    throw;
                }
            }

            result = store_t::sr_stored;
            *new_value = &value;
            new_large_buflock.swap(large_buflock);
            return true;

        } catch (data_provider_failed_exc_t) {
            result = store_t::sr_data_provider_failed;
            return false;
        }
    }

    ticks_t start_time;

    data_provider_t *data;
    set_type_t type;
    mcflags_t mcflags;
    exptime_t exptime;
    cas_t req_cas;

    union {
        char value_memory[MAX_BTREE_VALUE_SIZE];
        btree_value value;
    };

    store_t::set_result_t result;
};

store_t::set_result_t btree_set(const btree_key *key, btree_slice_t *slice, data_provider_t *data, set_type_t type, mcflags_t mcflags, exptime_t exptime, cas_t req_cas) {
    btree_set_oper_t oper(data, type, mcflags, exptime, req_cas);
    run_btree_modify_oper(&oper, slice, key);
    return oper.result;
}
<commit_msg>A post-lunch commit.<commit_after>#include "set.hpp"

#include "btree/modify_oper.hpp"

#include "buffer_cache/co_functions.hpp"

#include "btree/coro_wrappers.hpp"

struct btree_set_oper_t : public btree_modify_oper_t {

    explicit btree_set_oper_t(data_provider_t *data, set_type_t type, mcflags_t mcflags, exptime_t exptime, cas_t req_cas)
        : btree_modify_oper_t(), data(data), type(type), mcflags(mcflags), exptime(exptime), req_cas(req_cas)
    {
        pm_cmd_set.begin(&start_time);
    }

    ~btree_set_oper_t() {
        pm_cmd_set.end(&start_time);
    }

    bool operate(transaction_t *txn, btree_value *old_value, large_buf_lock_t& old_large_buflock, btree_value **new_value, large_buf_lock_t& new_large_buflock) {
        try {
            if ((old_value && type == set_type_add) || (!old_value && type == set_type_replace)) {
                result = store_t::sr_not_stored;
                data->discard();
                return false;
            }

            if (type == set_type_cas) { // TODO: CAS stats
                if (!old_value) {
                    result = store_t::sr_not_found;
                    data->discard();
                    return false;
                }
                if (!old_value->has_cas() || old_value->cas() != req_cas) {
                    result = store_t::sr_exists;
                    data->discard();
                    return false;
                }
            }

            if (data->get_size() > MAX_VALUE_SIZE) {
                result = store_t::sr_too_large;
                data->discard();
                /* To be standards-compliant we must delete the old value when an effort is made to
                replace it with a value that is too large. */
                *new_value = NULL;
                return true;
            }

            value.value_size(0);
            if (type == set_type_cas || (old_value && old_value->has_cas())) {
                // Turns the flag on and makes
                // room. run_btree_modify_oper() will set an actual CAS
                // later. TODO: We should probably have a separate
                // function for this.
                metadata_write(&value.metadata_flags, value.contents, mcflags, exptime, 0xCA5ADDED);
            } else {
                metadata_write(&value.metadata_flags, value.contents, mcflags, exptime);
            }

            value.value_size(data->get_size());

            large_buf_lock_t large_buflock;
            buffer_group_t buffer_group;

            rassert(data->get_size() <= MAX_VALUE_SIZE);
            if (data->get_size() <= MAX_IN_NODE_VALUE_SIZE) {
                buffer_group.add_buffer(data->get_size(), value.value());
                data->get_data_into_buffers(&buffer_group);
            } else {
                large_buflock.set(new large_buf_t(txn));
                large_buflock.lv()->allocate(data->get_size(), value.large_buf_ref_ptr());
                for (int64_t i = 0; i < large_buflock.lv()->get_num_segments(); i++) {
                    uint16_t size;
                    void *data = large_buflock.lv()->get_segment_write(i, &size);
                    buffer_group.add_buffer(size, data);
                }

                try {
                    data->get_data_into_buffers(&buffer_group);
                } catch (...) {
                    large_buflock.lv()->mark_deleted();
                    large_buflock.release();
                    throw;
                }
            }

            result = store_t::sr_stored;
            *new_value = &value;
            new_large_buflock.swap(large_buflock);
            return true;

        } catch (data_provider_failed_exc_t) {
            result = store_t::sr_data_provider_failed;
            return false;
        }
    }

    ticks_t start_time;

    data_provider_t *data;
    set_type_t type;
    mcflags_t mcflags;
    exptime_t exptime;
    cas_t req_cas;

    union {
        char value_memory[MAX_BTREE_VALUE_SIZE];
        btree_value value;
    };

    store_t::set_result_t result;
};

store_t::set_result_t btree_set(const btree_key *key, btree_slice_t *slice, data_provider_t *data, set_type_t type, mcflags_t mcflags, exptime_t exptime, cas_t req_cas) {
    btree_set_oper_t oper(data, type, mcflags, exptime, req_cas);
    run_btree_modify_oper(&oper, slice, key);
    return oper.result;
}
<|endoftext|>