<commit_before>#include <cmath>
#include <vector>
#include <random>
#include <numeric>
#include <fstream>
#include <iomanip>
#include <iterator>
#include <iostream>
#include <iterator>
#include <algorithm>

#include "caf/all.hpp"

#if defined __APPLE__ || defined(MACOSX)
    #include <OpenCL/opencl.h>
#else
    #include <CL/opencl.h>
#endif

using namespace std;
using namespace caf;

namespace {

constexpr const char* kernel_name = "kernel_wah_index";
constexpr const char* kernel_file = "kernel_wah_bitindex.cl";

class config : public actor_system_config {
public:
  string filename = "";
  uint32_t bound = 0;
  string device_name = "GeForce GT 650M";
  uint32_t batch_size = 1023;
  uint32_t jobs = 1;
  uint32_t work_groups = 1;
  bool print_results;
  config() {
    opt_group{custom_options_, "global"}
    .add(filename, "data-file,f", "file with test data (one value per line)")
    .add(bound, "bound,b", "maximum value (0 will scan values)")
    .add(device_name, "device,d", "device for computation (GeForce GTX 780M)")
    .add(batch_size, "batch-size,b", "values indexed in one batch (1023)")
    .add(print_results, "print,p", "print resulting bitmap index")
    .add(work_groups, "work-groups,w", "Use work-groups")
    .add(jobs, "jobs,j", "jobs sent to GPU (1)");
  }
};

void eval_err(cl_int err, string msg = "") {
  if (err != CL_SUCCESS)
    cout << "[!!!] " << msg << err << endl;
  else
    cout << "DONE" << endl;
}

} // namespace <anonymous>

void caf_main(actor_system&, const config& cfg) {

  vector<uint32_t> values;
  if (cfg.filename.empty()) {
    values = {10,  7, 22,  6,  7,  1,  9, 42,  2,  5,
              13,  3,  2,  1,  0,  1, 18, 18,  3, 13,
               5,  9,  0,  3,  2, 19,  5, 23, 22, 10,
               6, 22};
  } else {
    cout << "Reading data from '" << cfg.filename << "' ... " << flush;
    ifstream source{cfg.filename, std::ios::in};
    uint32_t next;
    while (source >> next) {
      values.push_back(next);
    }
  }
  cout << "'" << values.size() << "' values." << endl;
  auto bound = cfg.bound;
  if (bound == 0 && !values.empty()) {
    auto itr = max_element(values.begin(), values.end());
    bound = *itr;
  }
  cout << "Maximum value is '" << bound << "'." << endl;

  // read cl kernel file
  auto filename = string("./include/") + kernel_file;
  cout << "Reading source from '" << filename << "' ... " << flush;
  ifstream read_source{filename, std::ios::in};
  string source_contents;
  if (read_source) {
      read_source.seekg(0, std::ios::end);
      source_contents.resize(read_source.tellg());
      read_source.seekg(0, std::ios::beg);
      read_source.read(&source_contents[0], source_contents.size());
      read_source.close();
  } else {
      cout << strerror(errno) << "[!!!] " << endl;
      return;
  }
  cout << "DONE" << endl;

  // #### OLD PROGRAM ####

  cl_int err{0};

  // find up to two available platforms
  cout << "Getting platform id(s) ..." << flush;
  uint32_t num_platforms;
  err = clGetPlatformIDs(0, nullptr, &num_platforms);
  vector<cl_platform_id> platform_ids(num_platforms);
  err = clGetPlatformIDs(platform_ids.size(), platform_ids.data(),
                         &num_platforms);
  eval_err(err);

  // find gpu devices on our platform
  int platform_used = 0;
  uint32_t num_gpu_devices = 0;
  err = clGetDeviceIDs(platform_ids[platform_used], CL_DEVICE_TYPE_GPU, 0,
                       nullptr, &num_gpu_devices);
  if (err != CL_SUCCESS) {
      cout << "[!!!] Error getting number of gpu devices for platform '"
           << platform_ids[platform_used] << "'." << endl;
      return;
  }
  vector<cl_device_id> gpu_devices(num_gpu_devices);
  err = clGetDeviceIDs(platform_ids[platform_used], CL_DEVICE_TYPE_GPU,
                       num_gpu_devices, gpu_devices.data(), nullptr);
  if (err != CL_SUCCESS) {
      cout << "[!!!] Error getting CL_DEVICE_TYPE_GPU for platform '"
           << platform_ids[platform_used] << "'." << endl;
      return;
  }

  // choose device
  int device_used{0};
  bool found = false;
  if (!cfg.device_name.empty()) {
    for (uint32_t i = 0; i < num_gpu_devices; ++i) {
      size_t return_size;
      err = clGetDeviceInfo(gpu_devices[i], CL_DEVICE_NAME, 0, nullptr,
                            &return_size);
      vector<char> name(return_size);
      err = clGetDeviceInfo(gpu_devices[i], CL_DEVICE_NAME, return_size,
                            name.data(), &return_size);
      string as_string(name.data());
      if (as_string == cfg.device_name) {
        cout << "Using '" << as_string << "'" << endl;
        device_used = i;
        found = true;
        break;
      }
    }
    if (!found) {
      cout << "Device " << cfg.device_name << " not found." << endl;
      return;
    }
  } else {
    size_t return_size;
    err = clGetDeviceInfo(gpu_devices[device_used], CL_DEVICE_NAME, 0, nullptr,
                          &return_size);
    vector<char> name(return_size);
    err = clGetDeviceInfo(gpu_devices[device_used], CL_DEVICE_NAME, return_size,
                          name.data(), &return_size);
    cout << "Using '" << string(name.data()) << "'" << endl;
  }
  cl_device_id device_id_used = gpu_devices[device_used];
  size_t max_work_group_size;
  err = clGetDeviceInfo(device_id_used, CL_DEVICE_MAX_WORK_GROUP_SIZE,
                        sizeof(size_t), &max_work_group_size, NULL);

  // create a context
  auto pfn_notify = [](const char *errinfo, const void *, size_t, void *) {
      std::cerr << "\n##### Error message via pfn_notify #####\n"
                << string(errinfo)
                << "\n########################################";
  };
  cout << "Creating context ... " << flush;
  cl_context context = clCreateContext(0, 1, &device_id_used, pfn_notify,
                                       nullptr, &err);
  eval_err(err);

  // create a command queue
  cout << "Creating command queue ... " << flush;
  cl_command_queue cmd_queue = clCreateCommandQueue(context, device_id_used,
                                                    CL_QUEUE_PROFILING_ENABLE,
                                                    &err);
  eval_err(err);

  // create program object from kernel source
  cout << "Creating program object from source ... " << flush;
  const char* strings = source_contents.c_str();
  size_t      lengths = source_contents.size();
  cl_program program = clCreateProgramWithSource(context, 1, &strings,
                                                 &lengths, &err);
  eval_err(err);

  // build programm from program object
  cout << "Building program from program object ... " << flush;
  err = clBuildProgram(program, 0, nullptr, "", nullptr,
                       nullptr);
  eval_err(err);

  // create kernel
  cout << "Creating kernel object ... " << flush;
  cl_kernel kernel = clCreateKernel(program, kernel_name, &err);
  eval_err(err);

  auto batch_size = min(cfg.batch_size, static_cast<uint32_t>(values.size()));
  auto wg_size = min(batch_size, static_cast<uint32_t>(max_work_group_size));
  auto wg_num = cfg.work_groups;
  auto gl_size = wg_size * wg_num; // must be multiple of wg_size
  auto processed = 0;
  auto remaining = static_cast<uint32_t>(values.size());
  auto in_out_flags = CL_MEM_READ_WRITE;
  auto out_flags    = CL_MEM_READ_WRITE | CL_MEM_HOST_READ_ONLY;
  auto buf_flags    = CL_MEM_READ_WRITE | CL_MEM_HOST_NO_ACCESS;
  auto blocking = CL_FALSE;
  while (remaining > 0) {
    auto arg_size = min(remaining, wg_num * wg_size);
    auto arg_bytes = arg_size * sizeof(uint32_t);
    vector<uint32_t> input{
      make_move_iterator(begin(values) + processed),
      make_move_iterator(begin(values) + processed + arg_size)
    };
    processed += arg_size;
    remaining -= arg_size;
    auto full = arg_size / wg_size;
    auto partial = arg_size - (full * wg_size);
    vector<uint32_t> config(2 + (full * 2));
    config[0] = arg_size;
    config[1] = full;
    for (uint32_t i = 0; i < full; ++i) {
      config[2 * i + 2] = wg_size;
      config[2 * i + 3] = wg_size * 2;
    }
    if (partial > 0) {
      config[1] += 1;
      config.emplace_back(partial);
      config.emplace_back(partial * 2);
    }
    auto cfg_bytes = config.size() * sizeof(uint32_t);
    cout << "Config for " << arg_size << " values:" << endl;
    for (size_t i = 0; i < config.size(); ++i)
      cout << "> config[" << i << "]: " << config[i] << endl;

    vector<cl_event> eq_events;

    // create input buffers
    cout << "Creating buffer 'config' ... " << flush;
    cl_mem buf_config = clCreateBuffer(context, in_out_flags, cfg_bytes,
                                       nullptr, &err);
    eval_err(err);
    cout << "Creating buffer 'input' ... " << flush;
    cl_mem buf_input = clCreateBuffer(context, in_out_flags, arg_bytes,
                                      nullptr, &err);
    eval_err(err);
    cout << "Creating buffer 'index' ... " << flush;
    cl_mem buf_index = clCreateBuffer(context, out_flags, arg_bytes * 2,
                                      nullptr, &err);
    eval_err(err);
    cout << "Creating buffer 'offsets' ... " << flush;
    cl_mem buf_offsets = clCreateBuffer(context, out_flags, arg_bytes,
                                        nullptr, &err);
    eval_err(err);
    cout << "Creating buffer 'rids' ... " << flush;
    cl_mem buf_rids = clCreateBuffer(context, buf_flags, arg_bytes,
                                     nullptr, &err);
    eval_err(err);
    cout << "Creating buffer 'chids' ... " << flush;
    cl_mem buf_chids = clCreateBuffer(context, buf_flags, arg_bytes,
                                      nullptr, &err);
    eval_err(err);
    cout << "Creating buffer 'lits' ... " << flush;
    cl_mem buf_lits = clCreateBuffer(context, buf_flags, arg_bytes,
                                     nullptr, &err);
    eval_err(err);

    // copy data to GPU
    cl_event config_cpy;
    cout << "Writing 'config' ... " << flush;
    err = clEnqueueWriteBuffer(cmd_queue, buf_config, blocking, 0,
                               cfg_bytes, config.data(), 0,
                               nullptr, &config_cpy);
    eval_err(err);
    eq_events.push_back(config_cpy);
    cl_event input_copy;
    cout << "Writing 'input' ... " << flush;
    err = clEnqueueWriteBuffer(cmd_queue, buf_input, blocking, 0,
                               arg_bytes, input.data(), 0,
                               nullptr, &input_copy);
    eval_err(err);
    eq_events.push_back(input_copy);

    // set arguments
    cout << "Setting kernel argument 0 ... " << flush;
    err = clSetKernelArg(kernel, 0, sizeof(cl_mem), (void*) &buf_config);
    eval_err(err);
    cout << "Setting kernel argument 1 ... " << flush;
    err = clSetKernelArg(kernel, 1, sizeof(cl_mem), (void*) &buf_input);
    eval_err(err);
    cout << "Setting kernel argument 2 ... " << flush;
    err = clSetKernelArg(kernel, 2, sizeof(cl_mem), (void*) &buf_index);
    eval_err(err);
    cout << "Setting kernel argument 3 ... " << flush;
    err = clSetKernelArg(kernel, 3, sizeof(cl_mem), (void*) &buf_offsets);
    eval_err(err);
    cout << "Setting kernel argument 4 ... " << flush;
    err = clSetKernelArg(kernel, 4, sizeof(cl_mem), (void*) &buf_rids);
    eval_err(err);
    cout << "Setting kernel argument 5 ... " << flush;
    err = clSetKernelArg(kernel, 5, sizeof(cl_mem), (void*) &buf_chids);
    eval_err(err);
    cout << "Setting kernel argument 6 ... " << flush;
    err = clSetKernelArg(kernel, 6, sizeof(cl_mem), (void*) &buf_lits);
    eval_err(err);

    // enqueue kernel
    cout << "Enqueueing kernel to command queue ... " << flush;
    vector<size_t> global_work{gl_size};
    vector<size_t> local_work{wg_size};
    cl_event kernel_exec;
    err = clEnqueueNDRangeKernel(cmd_queue, kernel, 1, nullptr,
                                 global_work.data(), local_work.data(),
                                 eq_events.size(), eq_events.data(),
                                 &kernel_exec);
    eval_err(err);

    // get results from gpu
    vector<uint32_t> keys(arg_size);
    vector<uint32_t> index(arg_size * 2);
    vector<uint32_t> offsets(arg_size);
    cout << "Reading 'config' ... " << flush;
    err = clEnqueueReadBuffer(cmd_queue, buf_config, blocking, 0,
                              cfg_bytes, config.data(), 1,
                              &kernel_exec, nullptr);
    eval_err(err);
    cout << "Reading 'keys' ... " << flush;
    err = clEnqueueReadBuffer(cmd_queue, buf_input, blocking, 0,
                              arg_bytes, keys.data(), 1,
                              &kernel_exec, nullptr);
    eval_err(err);
    cout << "Reading 'index' ... " << flush;
    err = clEnqueueReadBuffer(cmd_queue, buf_index, blocking, 0,
                              arg_bytes * 2, index.data(), 1,
                              &kernel_exec, nullptr);
    eval_err(err);
    cout << "Reading 'offsets' ... " << flush;
    err = clEnqueueReadBuffer(cmd_queue, buf_offsets, blocking, 0,
                              arg_bytes, offsets.data(), 1,
                              &kernel_exec, nullptr);
    eval_err(err);
    clFinish(cmd_queue);

    for (auto& ev : eq_events)
      clReleaseEvent(ev);
    clReleaseEvent(kernel_exec);

    clReleaseMemObject(buf_config);
    clReleaseMemObject(buf_input);
    clReleaseMemObject(buf_index);
    clReleaseMemObject(buf_offsets);
    clReleaseMemObject(buf_rids);
    clReleaseMemObject(buf_chids);
    clReleaseMemObject(buf_lits);
  }
  // clean up
  cout << "Releasing memory ... " << flush;
  clReleaseKernel(kernel);
  clReleaseProgram(program);
  clReleaseCommandQueue(cmd_queue);
  clReleaseContext(context);
  cout << "DONE" << endl;
}

CAF_MAIN()
<commit_msg>Slim down output of plain opencl program<commit_after>#include <cmath>
#include <vector>
#include <chrono>
#include <random>
#include <numeric>
#include <fstream>
#include <iomanip>
#include <iterator>
#include <iostream>
#include <iterator>
#include <algorithm>

#include "caf/all.hpp"

#if defined __APPLE__ || defined(MACOSX)
    #include <OpenCL/opencl.h>
#else
    #include <CL/opencl.h>
#endif

using namespace std;
using namespace caf;

namespace {

constexpr const char* kernel_name = "kernel_wah_index";
constexpr const char* kernel_file = "kernel_wah_bitindex.cl";

class config : public actor_system_config {
public:
  string filename = "";
  uint32_t bound = 0;
  string device_name = "GeForce GT 650M";
  uint32_t batch_size = 1023;
  uint32_t jobs = 1;
  uint32_t work_groups = 1;
  bool print_results;
  config() {
    opt_group{custom_options_, "global"}
    .add(filename, "data-file,f", "file with test data (one value per line)")
    .add(bound, "bound,b", "maximum value (0 will scan values)")
    .add(device_name, "device,d", "device for computation (GeForce GTX 780M)")
    .add(batch_size, "batch-size,b", "values indexed in one batch (1023)")
    .add(print_results, "print,p", "print resulting bitmap index")
    .add(work_groups, "work-groups,w", "Use work-groups")
    .add(jobs, "jobs,j", "jobs sent to GPU (1)");
  }
};

void eval_err(cl_int err, string msg = "") {
  if (err != CL_SUCCESS) {
    cout << "[!!!] " << msg << err << endl;
    exit(err);
  }
}

} // namespace <anonymous>

void caf_main(actor_system&, const config& cfg) {

  vector<uint32_t> values;
  if (cfg.filename.empty()) {
    values = {10,  7, 22,  6,  7,  1,  9, 42,  2,  5,
              13,  3,  2,  1,  0,  1, 18, 18,  3, 13,
               5,  9,  0,  3,  2, 19,  5, 23, 22, 10,
               6, 22};
  } else {
    ifstream source{cfg.filename, std::ios::in};
    uint32_t next;
    while (source >> next) {
      values.push_back(next);
    }
  }
  auto bound = cfg.bound;
  if (bound == 0 && !values.empty()) {
    auto itr = max_element(values.begin(), values.end());
    bound = *itr;
  }

  // read cl kernel file
  auto filename = string("./include/") + kernel_file;
  ifstream read_source{filename, std::ios::in};
  string source_contents;
  if (read_source) {
      read_source.seekg(0, std::ios::end);
      source_contents.resize(read_source.tellg());
      read_source.seekg(0, std::ios::beg);
      read_source.read(&source_contents[0], source_contents.size());
      read_source.close();
  } else {
      cout << strerror(errno) << "[!!!] " << endl;
      return;
  }

  cl_int err{0};

  // find up to two available platforms
  uint32_t num_platforms;
  err = clGetPlatformIDs(0, nullptr, &num_platforms);
  vector<cl_platform_id> platform_ids(num_platforms);
  err = clGetPlatformIDs(platform_ids.size(), platform_ids.data(),
                         &num_platforms);
  eval_err(err);

  // find gpu devices on our platform
  int platform_used = 0;
  uint32_t num_gpu_devices = 0;
  err = clGetDeviceIDs(platform_ids[platform_used], CL_DEVICE_TYPE_GPU, 0,
                       nullptr, &num_gpu_devices);
  eval_err(err);
  vector<cl_device_id> gpu_devices(num_gpu_devices);
  err = clGetDeviceIDs(platform_ids[platform_used], CL_DEVICE_TYPE_GPU,
                       num_gpu_devices, gpu_devices.data(), nullptr);
  eval_err(err);

  // choose device
  int device_used{0};
  bool found = false;
  if (!cfg.device_name.empty()) {
    for (uint32_t i = 0; i < num_gpu_devices; ++i) {
      size_t return_size;
      err = clGetDeviceInfo(gpu_devices[i], CL_DEVICE_NAME, 0, nullptr,
                            &return_size);
      vector<char> name(return_size);
      err = clGetDeviceInfo(gpu_devices[i], CL_DEVICE_NAME, return_size,
                            name.data(), &return_size);
      string as_string(name.data());
      if (as_string == cfg.device_name) {
        device_used = i;
        found = true;
        break;
      }
    }
    if (!found) {
      cout << "Device " << cfg.device_name << " not found." << endl;
      return;
    }
  } else {
    size_t return_size;
    err = clGetDeviceInfo(gpu_devices[device_used], CL_DEVICE_NAME, 0, nullptr,
                          &return_size);
    vector<char> name(return_size);
    err = clGetDeviceInfo(gpu_devices[device_used], CL_DEVICE_NAME, return_size,
                          name.data(), &return_size);
    cout << "Using '" << string(name.data()) << "'" << endl;
  }
  cl_device_id device_id_used = gpu_devices[device_used];
  size_t max_work_group_size;
  err = clGetDeviceInfo(device_id_used, CL_DEVICE_MAX_WORK_GROUP_SIZE,
                        sizeof(size_t), &max_work_group_size, NULL);

  // create a context
  auto pfn_notify = [](const char *errinfo, const void *, size_t, void *) {
      std::cerr << "\n##### Error message via pfn_notify #####\n"
                << string(errinfo)
                << "\n########################################";
  };
  cl_context context = clCreateContext(0, 1, &device_id_used, pfn_notify,
                                       nullptr, &err);
  eval_err(err);

  // create a command queue
  cl_command_queue cmd_queue = clCreateCommandQueue(context, device_id_used,
                                                    CL_QUEUE_PROFILING_ENABLE,
                                                    &err);
  eval_err(err);

  // create program object from kernel source
  const char* strings = source_contents.c_str();
  size_t      lengths = source_contents.size();
  cl_program program = clCreateProgramWithSource(context, 1, &strings,
                                                 &lengths, &err);
  eval_err(err);

  // build programm from program object
  err = clBuildProgram(program, 0, nullptr, "", nullptr,
                       nullptr);
  eval_err(err);

  // create kernel
  cl_kernel kernel = clCreateKernel(program, kernel_name, &err);
  eval_err(err);

  auto batch_size = min(cfg.batch_size, static_cast<uint32_t>(values.size()));
  auto wg_size = min(batch_size, static_cast<uint32_t>(max_work_group_size));
  auto wg_num = cfg.work_groups;
  auto gl_size = wg_size * wg_num; // must be multiple of wg_size
  auto processed = 0;
  auto remaining = static_cast<uint32_t>(values.size());
  auto in_out_flags = CL_MEM_READ_WRITE;
  auto out_flags    = CL_MEM_READ_WRITE | CL_MEM_HOST_READ_ONLY;
  auto buf_flags    = CL_MEM_READ_WRITE | CL_MEM_HOST_NO_ACCESS;
  auto blocking = CL_FALSE;
   auto start = chrono::high_resolution_clock::now();
  while (remaining > 0) {
    auto arg_size = min(remaining, wg_num * wg_size);
    auto arg_bytes = arg_size * sizeof(uint32_t);
    vector<uint32_t> input{
      begin(values) + processed,
      begin(values) + processed + arg_size
      //make_move_iterator(begin(values) + processed),
      //make_move_iterator(begin(values) + processed + arg_size)
    };
    processed += arg_size;
    remaining -= arg_size;
    auto full = arg_size / wg_size;
    auto partial = arg_size - (full * wg_size);
    vector<uint32_t> config(2 + (full * 2));
    config[0] = arg_size;
    config[1] = full;
    for (uint32_t i = 0; i < full; ++i) {
      config[2 * i + 2] = wg_size;
      config[2 * i + 3] = wg_size * 2;
    }
    if (partial > 0) {
      config[1] += 1;
      config.emplace_back(partial);
      config.emplace_back(partial * 2);
    }
    auto cfg_bytes = config.size() * sizeof(uint32_t);

    vector<cl_event> eq_events;

    // create input buffers
    cl_mem buf_config = clCreateBuffer(context, in_out_flags, cfg_bytes,
                                       nullptr, &err);
    eval_err(err);
    cl_mem buf_input = clCreateBuffer(context, in_out_flags, arg_bytes,
                                      nullptr, &err);
    eval_err(err);
    cl_mem buf_index = clCreateBuffer(context, out_flags, arg_bytes * 2,
                                      nullptr, &err);
    eval_err(err);
    cl_mem buf_offsets = clCreateBuffer(context, out_flags, arg_bytes,
                                        nullptr, &err);
    eval_err(err);
    cl_mem buf_rids = clCreateBuffer(context, buf_flags, arg_bytes,
                                     nullptr, &err);
    eval_err(err);
    cl_mem buf_chids = clCreateBuffer(context, buf_flags, arg_bytes,
                                      nullptr, &err);
    eval_err(err);
    cl_mem buf_lits = clCreateBuffer(context, buf_flags, arg_bytes,
                                     nullptr, &err);
    eval_err(err);

    // copy data to GPU
    cl_event config_cpy;
    err = clEnqueueWriteBuffer(cmd_queue, buf_config, blocking, 0,
                               cfg_bytes, config.data(), 0,
                               nullptr, &config_cpy);
    eval_err(err);
    eq_events.push_back(config_cpy);
    cl_event input_copy;
    err = clEnqueueWriteBuffer(cmd_queue, buf_input, blocking, 0,
                               arg_bytes, input.data(), 0,
                               nullptr, &input_copy);
    eval_err(err);
    eq_events.push_back(input_copy);

    // set arguments
    err = clSetKernelArg(kernel, 0, sizeof(cl_mem), (void*) &buf_config);
    eval_err(err);
    err = clSetKernelArg(kernel, 1, sizeof(cl_mem), (void*) &buf_input);
    eval_err(err);
    err = clSetKernelArg(kernel, 2, sizeof(cl_mem), (void*) &buf_index);
    eval_err(err);
    err = clSetKernelArg(kernel, 3, sizeof(cl_mem), (void*) &buf_offsets);
    eval_err(err);
    err = clSetKernelArg(kernel, 4, sizeof(cl_mem), (void*) &buf_rids);
    eval_err(err);
    err = clSetKernelArg(kernel, 5, sizeof(cl_mem), (void*) &buf_chids);
    eval_err(err);
    err = clSetKernelArg(kernel, 6, sizeof(cl_mem), (void*) &buf_lits);
    eval_err(err);

    // enqueue kernel
    vector<size_t> global_work{gl_size};
    vector<size_t> local_work{wg_size};
    cl_event kernel_exec;
    err = clEnqueueNDRangeKernel(cmd_queue, kernel, 1, nullptr,
                                 global_work.data(), local_work.data(),
                                 eq_events.size(), eq_events.data(),
                                 &kernel_exec);
    eval_err(err);

    // get results from gpu
    vector<uint32_t> keys(arg_size);
    vector<uint32_t> index(arg_size * 2);
    vector<uint32_t> offsets(arg_size);
    err = clEnqueueReadBuffer(cmd_queue, buf_config, blocking, 0,
                              cfg_bytes, config.data(), 1,
                              &kernel_exec, nullptr);
    eval_err(err);
    err = clEnqueueReadBuffer(cmd_queue, buf_input, blocking, 0,
                              arg_bytes, keys.data(), 1,
                              &kernel_exec, nullptr);
    eval_err(err);
    err = clEnqueueReadBuffer(cmd_queue, buf_index, blocking, 0,
                              arg_bytes * 2, index.data(), 1,
                              &kernel_exec, nullptr);
    eval_err(err);
    err = clEnqueueReadBuffer(cmd_queue, buf_offsets, blocking, 0,
                              arg_bytes, offsets.data(), 1,
                              &kernel_exec, nullptr);
    eval_err(err);
    clFinish(cmd_queue);

    for (auto& ev : eq_events)
      clReleaseEvent(ev);
    clReleaseEvent(kernel_exec);

    clReleaseMemObject(buf_config);
    clReleaseMemObject(buf_input);
    clReleaseMemObject(buf_index);
    clReleaseMemObject(buf_offsets);
    clReleaseMemObject(buf_rids);
    clReleaseMemObject(buf_chids);
    clReleaseMemObject(buf_lits);
  }
  auto stop = chrono::high_resolution_clock::now();
  // clean up
  clReleaseKernel(kernel);
  clReleaseProgram(program);
  clReleaseCommandQueue(cmd_queue);
  clReleaseContext(context);
  cout << "Time: '"
       << chrono::duration_cast<chrono::milliseconds>(stop - start).count()
       << "' ms" << endl;
}

CAF_MAIN()
<|endoftext|>


<commit_before>/**
 * po     n  c       i
 * poor mans cgroups interface
 *
 * Copyright 2016 by LRR-TUM
 * Jens Breitbart     <j.breitbart@tum.de>
 *
 * Licensed under GNU Lesser General Public License 2.1 or later.
 * Some rights reserved. See LICENSE
 */

#include "ponci/ponci.hpp"

#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>

#include <cassert>
#include <cstdio>
#include <cstring>

#include <dirent.h>
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>

// default mount path
static std::string path_prefix("/sys/fs/cgroup/");

// TODO function to change prefix?

/////////////////////////////////////////////////////////////////
// PROTOTYPES
/////////////////////////////////////////////////////////////////
static inline std::string cgroup_path(const char *name);

template <typename T> static inline void write_vector_to_file(const std::string &filename, const std::vector<T> &vec);
template <typename T> static inline void write_array_to_file(const std::string &filename, T *arr, size_t size);
template <typename T> static inline void write_value_to_file(const std::string &filename, T val);
template <> inline void write_value_to_file<const char *>(const std::string &filename, const char *val);
template <typename T> static inline void append_value_to_file(const std::string &filename, T val);

static inline std::string read_line_from_file(const std::string &filename);
template <typename T> static inline std::vector<T> read_lines_from_file(const std::string &filename);

template <typename T> static inline T string_to_T(const std::string &s, std::size_t &done);
// template <> inline unsigned long string_to_T<unsigned long>(const std::string &s, std::size_t &done);
template <> inline int string_to_T<int>(const std::string &s, std::size_t &done);

static std::vector<int> get_tids_from_pid(const int pid);

/////////////////////////////////////////////////////////////////
// EXPORTED FUNCTIONS
/////////////////////////////////////////////////////////////////
void cgroup_create(const char *name) {
	const int err = mkdir(cgroup_path(name).c_str(), S_IRWXU | S_IRWXG);

	if (err != 0 && errno != EEXIST) throw std::runtime_error(strerror(errno));
	errno = 0;
}

void cgroup_delete(const char *name) {
	const int err = rmdir(cgroup_path(name).c_str());

	if (err != 0) throw std::runtime_error(strerror(errno));
}
void cgroup_add_me(const char *name) {
	pid_t me = static_cast<pid_t>(syscall(SYS_gettid));
	cgroup_add_task(name, me);
}

void cgroup_add_task(const char *name, const pid_t tid) {
	std::string filename = cgroup_path(name) + std::string("tasks");

	append_value_to_file(filename, tid);
}

void cgroup_set_cpus(const char *name, const size_t *cpus, size_t size) {
	std::string filename = cgroup_path(name) + std::string("cpuset.cpus");

	write_array_to_file(filename, cpus, size);
}

void cgroup_set_cpus(const std::string &name, const std::vector<unsigned char> &cpus) {
	std::string filename = cgroup_path(name.c_str()) + std::string("cpuset.cpus");
	write_vector_to_file(filename, cpus);
}

void cgroup_set_mems(const char *name, const size_t *mems, size_t size) {
	std::string filename = cgroup_path(name) + std::string("cpuset.mems");

	write_array_to_file(filename, mems, size);
}

void cgroup_set_mems(const std::string &name, const std::vector<unsigned char> &mems) {
	std::string filename = cgroup_path(name.c_str()) + std::string("cpuset.mems");
	write_vector_to_file(filename, mems);
}

void cgroup_set_memory_migrate(const char *name, size_t flag) {
	assert(flag == 0 || flag == 1);
	std::string filename = cgroup_path(name) + std::string("cpuset.memory_migrate");

	write_value_to_file(filename, flag);
}

void cgroup_set_cpus_exclusive(const char *name, size_t flag) {
	assert(flag == 0 || flag == 1);
	std::string filename = cgroup_path(name) + std::string("cpuset.cpu_exclusive");

	write_value_to_file(filename, flag);
}

void cgroup_set_mem_hardwall(const char *name, size_t flag) {
	assert(flag == 0 || flag == 1);
	std::string filename = cgroup_path(name) + std::string("cpuset.mem_hardwall");

	write_value_to_file(filename, flag);
}

void cgroup_set_scheduling_domain(const char *name, int flag) {
	assert(flag >= -1 && flag <= 5);
	std::string filename = cgroup_path(name) + std::string("cpuset.sched_relax_domain_level");

	write_value_to_file(filename, flag);
}

void cgroup_freeze(const char *name) {
	assert(strcmp(name, "") != 0);
	std::string filename = cgroup_path(name) + std::string("freezer.state");

	write_value_to_file(filename, "FROZEN");
}

void cgroup_thaw(const char *name) {
	assert(strcmp(name, "") != 0);
	std::string filename = cgroup_path(name) + std::string("freezer.state");

	write_value_to_file(filename, "THAWED");
}

void cgroup_wait_frozen(const char *name) {
	assert(strcmp(name, "") != 0);
	std::string filename = cgroup_path(name) + std::string("freezer.state");

	std::string temp;
	while (temp != "FROZEN\n") {
		temp = read_line_from_file(filename);
	}
}

void cgroup_wait_thawed(const char *name) {
	assert(strcmp(name, "") != 0);
	std::string filename = cgroup_path(name) + std::string("freezer.state");

	std::string temp;
	while (temp != "THAWED\n") {
		temp = read_line_from_file(filename);
	}
}

void cgroup_kill(const char *name) {
	auto tids = get_tids_from_pid(getpid());

	// get all pids
	std::vector<int> pids = read_lines_from_file<int>(cgroup_path(name) + std::string("tasks"));

	// send kill
	for (__pid_t pid : pids) {
		if (std::find(tids.begin(), tids.end(), pid) != tids.end()) {
			// pid in tids
		} else {
			// pid not in tids
			if (kill(pid, SIGTERM) != 0) {
				throw std::runtime_error(strerror(errno));
			}
		}
	}

	// wait until tasks empty
	while (pids.size() != 0) {
		pids = read_lines_from_file<int>(cgroup_path(name) + std::string("tasks"));
	}

	cgroup_delete(name);
}

/////////////////////////////////////////////////////////////////
// INTERNAL FUNCTIONS
/////////////////////////////////////////////////////////////////

static inline std::string cgroup_path(const char *name) {
	std::string res(path_prefix);
	if (strcmp(name, "") != 0) {
		res.append(name);
		res.append("/");
	}
	return res;
}

template <typename T> static inline void write_vector_to_file(const std::string &filename, const std::vector<T> &vec) {
	write_array_to_file(filename, &vec[0], vec.size());
}

template <typename T> static inline void write_array_to_file(const std::string &filename, T *arr, size_t size) {
	assert(size > 0);
	assert(filename.compare("") != 0);

	std::string str;
	for (size_t i = 0; i < size; ++i) {
		str.append(std::to_string(arr[i]));
		str.append(",");
	}

	write_value_to_file(filename, str.c_str());
}

template <typename T> static inline void append_value_to_file(const std::string &filename, T val) {
	assert(filename.compare("") != 0);

	FILE *f = fopen(filename.c_str(), "a+");
	if (f == nullptr) {
		throw std::runtime_error(strerror(errno));
	}
	std::string str = std::to_string(val);

	if (fputs(str.c_str(), f) == EOF && ferror(f) != 0) {
		throw std::runtime_error(strerror(errno));
	}
	if (fclose(f) != 0) {
		throw std::runtime_error(strerror(errno));
	}
}

template <typename T> static inline void write_value_to_file(const std::string &filename, T val) {
	write_value_to_file(filename, std::to_string(val).c_str());
}

template <> void write_value_to_file<const char *>(const std::string &filename, const char *val) {
	assert(filename.compare("") != 0);

	FILE *file = fopen(filename.c_str(), "w+");

	if (file == nullptr) {
		throw std::runtime_error(strerror(errno));
	}

	int status = fputs(val, file);
	if (status <= 0 || ferror(file) != 0) {
		throw std::runtime_error(strerror(errno));
	}

	if (fclose(file) != 0) {
		throw std::runtime_error(strerror(errno));
	}
}

static inline std::string read_line_from_file(const std::string &filename) {
	assert(filename.compare("") != 0);

	FILE *file = fopen(filename.c_str(), "r");

	if (file == nullptr) {
		throw std::runtime_error(strerror(errno));
	}

	char temp[255];
	if (fgets(temp, 255, file) == nullptr && !feof(file)) {
		throw std::runtime_error("Error while reading file in libponci. Buffer to small?");
	}

	if (feof(file)) return std::string();

	if (fclose(file) != 0) {
		throw std::runtime_error(strerror(errno));
	}

	return std::string(temp);
}

template <typename T> static inline std::vector<T> read_lines_from_file(const std::string &filename) {
	assert(filename.compare("") != 0);

	FILE *file = fopen(filename.c_str(), "r");

	if (file == nullptr) {
		throw std::runtime_error(strerror(errno));
	}

	std::vector<T> ret;

	char temp[255];
	while (true) {
		if (fgets(temp, 255, file) == nullptr && !feof(file)) {
			throw std::runtime_error("Error while reading file in libponci. Buffer to small?");
		}
		if (feof(file)) break;
		std::size_t done = 0;
		T i = string_to_T<T>(std::string(temp), done);
		if (done != 0) ret.push_back(i);
	}

	if (fclose(file) != 0) {
		throw std::runtime_error(strerror(errno));
	}

	return ret;
}

template <> int string_to_T<int>(const std::string &s, std::size_t &done) { return stoi(s, &done); }

#if 0
template <> unsigned long string_to_T<unsigned long>(const std::string &s, std::size_t &done) {
	return stoul(s, &done);
}
#endif

static std::vector<int> get_tids_from_pid(const int pid) {
	std::string path("/proc/" + std::to_string(pid) + "/task/");
	dirent *dent;
	DIR *srcdir = opendir(path.c_str());

	if (srcdir == nullptr) {
		throw std::runtime_error(strerror(errno));
	}

	std::vector<int> ret;

	while ((dent = readdir(srcdir)) != nullptr) {
		struct stat st;

		if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) continue;

		if (fstatat(dirfd(srcdir), dent->d_name, &st, 0) < 0) {
			continue;
		}

		if (S_ISDIR(st.st_mode)) {
			std::size_t i;
			ret.push_back(string_to_T<int>(std::string(dent->d_name), i));
		}
	}
	closedir(srcdir);
	return ret;
}
<commit_msg>Added support for PONCI_PATH env variable.<commit_after>/**
 * po     n  c       i
 * poor mans cgroups interface
 *
 * Copyright 2016 by LRR-TUM
 * Jens Breitbart     <j.breitbart@tum.de>
 *
 * Licensed under GNU Lesser General Public License 2.1 or later.
 * Some rights reserved. See LICENSE
 */

#include "ponci/ponci.hpp"

#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>

#include <cassert>
#include <cstdio>
#include <cstring>

#include <dirent.h>
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>

// default mount path
static std::string path_prefix("/sys/fs/cgroup/");

/////////////////////////////////////////////////////////////////
// PROTOTYPES
/////////////////////////////////////////////////////////////////
static inline std::string cgroup_path(const char *name);

template <typename T> static inline void write_vector_to_file(const std::string &filename, const std::vector<T> &vec);
template <typename T> static inline void write_array_to_file(const std::string &filename, T *arr, size_t size);
template <typename T> static inline void write_value_to_file(const std::string &filename, T val);
template <> inline void write_value_to_file<const char *>(const std::string &filename, const char *val);
template <typename T> static inline void append_value_to_file(const std::string &filename, T val);

static inline std::string read_line_from_file(const std::string &filename);
template <typename T> static inline std::vector<T> read_lines_from_file(const std::string &filename);

template <typename T> static inline T string_to_T(const std::string &s, std::size_t &done);
// template <> inline unsigned long string_to_T<unsigned long>(const std::string &s, std::size_t &done);
template <> inline int string_to_T<int>(const std::string &s, std::size_t &done);

static std::vector<int> get_tids_from_pid(const int pid);

/////////////////////////////////////////////////////////////////
// EXPORTED FUNCTIONS
/////////////////////////////////////////////////////////////////
void cgroup_create(const char *name) {
	const int err = mkdir(cgroup_path(name).c_str(), S_IRWXU | S_IRWXG);

	if (err != 0 && errno != EEXIST) throw std::runtime_error(strerror(errno));
	errno = 0;
}

void cgroup_delete(const char *name) {
	const int err = rmdir(cgroup_path(name).c_str());

	if (err != 0) throw std::runtime_error(strerror(errno));
}
void cgroup_add_me(const char *name) {
	pid_t me = static_cast<pid_t>(syscall(SYS_gettid));
	cgroup_add_task(name, me);
}

void cgroup_add_task(const char *name, const pid_t tid) {
	std::string filename = cgroup_path(name) + std::string("tasks");

	append_value_to_file(filename, tid);
}

void cgroup_set_cpus(const char *name, const size_t *cpus, size_t size) {
	std::string filename = cgroup_path(name) + std::string("cpuset.cpus");

	write_array_to_file(filename, cpus, size);
}

void cgroup_set_cpus(const std::string &name, const std::vector<unsigned char> &cpus) {
	std::string filename = cgroup_path(name.c_str()) + std::string("cpuset.cpus");
	write_vector_to_file(filename, cpus);
}

void cgroup_set_mems(const char *name, const size_t *mems, size_t size) {
	std::string filename = cgroup_path(name) + std::string("cpuset.mems");

	write_array_to_file(filename, mems, size);
}

void cgroup_set_mems(const std::string &name, const std::vector<unsigned char> &mems) {
	std::string filename = cgroup_path(name.c_str()) + std::string("cpuset.mems");
	write_vector_to_file(filename, mems);
}

void cgroup_set_memory_migrate(const char *name, size_t flag) {
	assert(flag == 0 || flag == 1);
	std::string filename = cgroup_path(name) + std::string("cpuset.memory_migrate");

	write_value_to_file(filename, flag);
}

void cgroup_set_cpus_exclusive(const char *name, size_t flag) {
	assert(flag == 0 || flag == 1);
	std::string filename = cgroup_path(name) + std::string("cpuset.cpu_exclusive");

	write_value_to_file(filename, flag);
}

void cgroup_set_mem_hardwall(const char *name, size_t flag) {
	assert(flag == 0 || flag == 1);
	std::string filename = cgroup_path(name) + std::string("cpuset.mem_hardwall");

	write_value_to_file(filename, flag);
}

void cgroup_set_scheduling_domain(const char *name, int flag) {
	assert(flag >= -1 && flag <= 5);
	std::string filename = cgroup_path(name) + std::string("cpuset.sched_relax_domain_level");

	write_value_to_file(filename, flag);
}

void cgroup_freeze(const char *name) {
	assert(strcmp(name, "") != 0);
	std::string filename = cgroup_path(name) + std::string("freezer.state");

	write_value_to_file(filename, "FROZEN");
}

void cgroup_thaw(const char *name) {
	assert(strcmp(name, "") != 0);
	std::string filename = cgroup_path(name) + std::string("freezer.state");

	write_value_to_file(filename, "THAWED");
}

void cgroup_wait_frozen(const char *name) {
	assert(strcmp(name, "") != 0);
	std::string filename = cgroup_path(name) + std::string("freezer.state");

	std::string temp;
	while (temp != "FROZEN\n") {
		temp = read_line_from_file(filename);
	}
}

void cgroup_wait_thawed(const char *name) {
	assert(strcmp(name, "") != 0);
	std::string filename = cgroup_path(name) + std::string("freezer.state");

	std::string temp;
	while (temp != "THAWED\n") {
		temp = read_line_from_file(filename);
	}
}

void cgroup_kill(const char *name) {
	auto tids = get_tids_from_pid(getpid());

	// get all pids
	std::vector<int> pids = read_lines_from_file<int>(cgroup_path(name) + std::string("tasks"));

	// send kill
	for (__pid_t pid : pids) {
		if (std::find(tids.begin(), tids.end(), pid) != tids.end()) {
			// pid in tids
		} else {
			// pid not in tids
			if (kill(pid, SIGTERM) != 0) {
				throw std::runtime_error(strerror(errno));
			}
		}
	}

	// wait until tasks empty
	while (pids.size() != 0) {
		pids = read_lines_from_file<int>(cgroup_path(name) + std::string("tasks"));
	}

	cgroup_delete(name);
}

/////////////////////////////////////////////////////////////////
// INTERNAL FUNCTIONS
/////////////////////////////////////////////////////////////////

static inline std::string cgroup_path(const char *name) {
	const char *env = std::getenv("PONCI_PATH");
	if (env != nullptr) {
		path_prefix = std::string(env);
	}
	std::string res(path_prefix);
	if (strcmp(name, "") != 0) {
		res.append(name);
		res.append("/");
	}
	return res;
}

template <typename T> static inline void write_vector_to_file(const std::string &filename, const std::vector<T> &vec) {
	write_array_to_file(filename, &vec[0], vec.size());
}

template <typename T> static inline void write_array_to_file(const std::string &filename, T *arr, size_t size) {
	assert(size > 0);
	assert(filename.compare("") != 0);

	std::string str;
	for (size_t i = 0; i < size; ++i) {
		str.append(std::to_string(arr[i]));
		str.append(",");
	}

	write_value_to_file(filename, str.c_str());
}

template <typename T> static inline void append_value_to_file(const std::string &filename, T val) {
	assert(filename.compare("") != 0);

	FILE *f = fopen(filename.c_str(), "a+");
	if (f == nullptr) {
		throw std::runtime_error(strerror(errno));
	}
	std::string str = std::to_string(val);

	if (fputs(str.c_str(), f) == EOF && ferror(f) != 0) {
		throw std::runtime_error(strerror(errno));
	}
	if (fclose(f) != 0) {
		throw std::runtime_error(strerror(errno));
	}
}

template <typename T> static inline void write_value_to_file(const std::string &filename, T val) {
	write_value_to_file(filename, std::to_string(val).c_str());
}

template <> void write_value_to_file<const char *>(const std::string &filename, const char *val) {
	assert(filename.compare("") != 0);

	FILE *file = fopen(filename.c_str(), "w+");

	if (file == nullptr) {
		throw std::runtime_error(strerror(errno));
	}

	int status = fputs(val, file);
	if (status <= 0 || ferror(file) != 0) {
		throw std::runtime_error(strerror(errno));
	}

	if (fclose(file) != 0) {
		throw std::runtime_error(strerror(errno));
	}
}

static inline std::string read_line_from_file(const std::string &filename) {
	assert(filename.compare("") != 0);

	FILE *file = fopen(filename.c_str(), "r");

	if (file == nullptr) {
		throw std::runtime_error(strerror(errno));
	}

	char temp[255];
	if (fgets(temp, 255, file) == nullptr && !feof(file)) {
		throw std::runtime_error("Error while reading file in libponci. Buffer to small?");
	}

	if (feof(file)) return std::string();

	if (fclose(file) != 0) {
		throw std::runtime_error(strerror(errno));
	}

	return std::string(temp);
}

template <typename T> static inline std::vector<T> read_lines_from_file(const std::string &filename) {
	assert(filename.compare("") != 0);

	FILE *file = fopen(filename.c_str(), "r");

	if (file == nullptr) {
		throw std::runtime_error(strerror(errno));
	}

	std::vector<T> ret;

	char temp[255];
	while (true) {
		if (fgets(temp, 255, file) == nullptr && !feof(file)) {
			throw std::runtime_error("Error while reading file in libponci. Buffer to small?");
		}
		if (feof(file)) break;
		std::size_t done = 0;
		T i = string_to_T<T>(std::string(temp), done);
		if (done != 0) ret.push_back(i);
	}

	if (fclose(file) != 0) {
		throw std::runtime_error(strerror(errno));
	}

	return ret;
}

template <> int string_to_T<int>(const std::string &s, std::size_t &done) { return stoi(s, &done); }

#if 0
template <> unsigned long string_to_T<unsigned long>(const std::string &s, std::size_t &done) {
	return stoul(s, &done);
}
#endif

static std::vector<int> get_tids_from_pid(const int pid) {
	std::string path("/proc/" + std::to_string(pid) + "/task/");
	dirent *dent;
	DIR *srcdir = opendir(path.c_str());

	if (srcdir == nullptr) {
		throw std::runtime_error(strerror(errno));
	}

	std::vector<int> ret;

	while ((dent = readdir(srcdir)) != nullptr) {
		struct stat st;

		if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) continue;

		if (fstatat(dirfd(srcdir), dent->d_name, &st, 0) < 0) {
			continue;
		}

		if (S_ISDIR(st.st_mode)) {
			std::size_t i;
			ret.push_back(string_to_T<int>(std::string(dent->d_name), i));
		}
	}
	closedir(srcdir);
	return ret;
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
//     Free Software Foundation, Inc.,
//     51 Franklin Street, Fifth Floor,
//     Boston, MA
//     02110-1301
//     USA
//
////////////////////////////////////////////////////////////////////////////////

#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <pwd.h>
#include <time.h>

#include "Context.h"
#include "Date.h"
#include "Duration.h"
#include "text.h"
#include "util.h"
#include "main.h"

#ifdef HAVE_LIBNCURSES
#include <ncurses.h>
#endif

// Global context for use by all.
extern Context context;

////////////////////////////////////////////////////////////////////////////////
// Scans all tasks, and for any recurring tasks, determines whether any new
// child tasks need to be generated to fill gaps.
void handleRecurrence ()
{
  std::vector <Task> tasks;
  Filter filter;
  context.tdb.loadPending (tasks, filter);

  std::vector <Task> modified;

  // Look at all tasks and find any recurring ones.
  foreach (t, tasks)
  {
    if (t->getStatus () == Task::recurring)
    {
      // Generate a list of due dates for this recurring task, regardless of
      // the mask.
      std::vector <Date> due;
      if (!generateDueDates (*t, due))
      {
        std::cout << "Task ("
                  << trim (t->get ("description"))
                  << ") has past its 'until' date, and has been deleted.\n";

        // Determine the end date.
        char endTime[16];
        sprintf (endTime, "%u", (unsigned int) time (NULL));
        t->set ("end", endTime);
        t->setStatus (Task::deleted);
        context.tdb.update (*t);
        continue;
      }

      // Get the mask from the parent task.
      std::string mask = t->get ("mask");

      // Iterate over the due dates, and check each against the mask.
      bool changed = false;
      unsigned int i = 0;
      foreach (d, due)
      {
        if (mask.length () <= i)
        {
          changed = true;

          Task rec (*t);                         // Clone the parent.
          rec.set ("uuid", uuid ());             // New UUID.
          rec.set ("parent", t->get ("uuid"));   // Remember mom.

          char dueDate[16];
          sprintf (dueDate, "%u", (unsigned int) d->toEpoch ());
          rec.set ("due", dueDate);              // Store generated due date.

          if (t->get ("wait").size())
          {
            Date old_wait (atoi (t->get ("wait").c_str ()));
            Date old_due (atoi (t->get ("due").c_str ()));
            Date due (*d);
            sprintf (dueDate, "%u", (unsigned int) (due + (old_wait - old_due)).toEpoch ());
            rec.set ("wait", dueDate);
            rec.setStatus (Task::waiting);
            mask += 'W';
          }
          else
          {
            mask += '-';
            rec.setStatus (Task::pending);
          }

          char indexMask[12];
          sprintf (indexMask, "%u", (unsigned int) i);
          rec.set ("imask", indexMask);          // Store index into mask.

          rec.remove ("mask");                   // Remove the mask of the parent.

          // Add the new task to the vector, for immediate use.
          modified.push_back (rec);

          // Add the new task to the DB.
          context.tdb.add (rec);
        }

        ++i;
      }

      // Only modify the parent if necessary.
      if (changed)
      {
        t->set ("mask", mask);
        context.tdb.update (*t);
      }
    }
    else
      modified.push_back (*t);
  }

  tasks = modified;
}

////////////////////////////////////////////////////////////////////////////////
// Determine a start date (due), an optional end date (until), and an increment
// period (recur).  Then generate a set of corresponding dates.
//
// Returns false if the parent recurring task is depleted.
bool generateDueDates (Task& parent, std::vector <Date>& allDue)
{
  // Determine due date, recur period and until date.
  Date due (atoi (parent.get ("due").c_str ()));
  std::string recur = parent.get ("recur");

  bool specificEnd = false;
  Date until;
  if (parent.get ("until") != "")
  {
    until = Date (atoi (parent.get ("until").c_str ()));
    specificEnd = true;
  }

  int recurrence_limit = context.config.getInteger ("recurrence.limit");
  int recurrence_counter = 0;
  Date now;
  for (Date i = due; ; i = getNextRecurrence (i, recur))
  {
    allDue.push_back (i);

    if (specificEnd && i > until)
    {
      // If i > until, it means there are no more tasks to generate, and if the
      // parent mask contains all + or X, then there never will be another task
      // to generate, and this parent task may be safely reaped.
      std::string mask = parent.get ("mask");
      if (mask.length () == allDue.size () &&
          mask.find ('-') == std::string::npos)
        return false;

      return true;
    }

    if (i > now)
      ++recurrence_counter;

    if (recurrence_counter >= recurrence_limit)
      return true;
  }

  return true;
}

////////////////////////////////////////////////////////////////////////////////
Date getNextRecurrence (Date& current, std::string& period)
{
  int m = current.month ();
  int d = current.day ();
  int y = current.year ();

  // Some periods are difficult, because they can be vague.
  if (period == "monthly")
  {
    if (++m > 12)
    {
       m -= 12;
       ++y;
    }

    while (! Date::valid (m, d, y))
      --d;

    return Date (m, d, y);
  }

  if (period == "weekdays")
  {
    int dow = current.dayOfWeek ();
    int days;

         if (dow == 5) days = 3;
    else if (dow == 6) days = 2;
    else               days = 1;

    return current + (days * 86400);
  }

  if (isdigit (period[0]) && period[period.length () - 1] == 'm')
  {
    std::string numeric = period.substr (0, period.length () - 1);
    int increment = atoi (numeric.c_str ());

    m += increment;
    while (m > 12)
    {
       m -= 12;
       ++y;
    }

    while (! Date::valid (m, d, y))
      --d;

    return Date (m, d, y);
  }

  else if (period == "quarterly")
  {
    m += 3;
    if (m > 12)
    {
       m -= 12;
       ++y;
    }

    while (! Date::valid (m, d, y))
      --d;

    return Date (m, d, y);
  }

  else if (isdigit (period[0]) && period[period.length () - 1] == 'q')
  {
    std::string numeric = period.substr (0, period.length () - 1);
    int increment = atoi (numeric.c_str ());

    m += 3 * increment;
    while (m > 12)
    {
       m -= 12;
       ++y;
    }

    while (! Date::valid (m, d, y))
      --d;

    return Date (m, d, y);
  }

  else if (period == "semiannual")
  {
    m += 6;
    if (m > 12)
    {
       m -= 12;
       ++y;
    }

    while (! Date::valid (m, d, y))
      --d;

    return Date (m, d, y);
  }

  else if (period == "bimonthly")
  {
    m += 2;
    if (m > 12)
    {
       m -= 12;
       ++y;
    }

    while (! Date::valid (m, d, y))
      --d;

    return Date (m, d, y);
  }

  else if (period == "biannual"    ||
           period == "biyearly")
  {
    y += 2;

    return Date (m, d, y);
  }

  else if (period == "annual" ||
           period == "yearly")
  {
    y += 1;

    // If the due data just happens to be 2/29 in a leap year, then simply
    // incrementing y is going to create an invalid date.
    if (m == 2 && d == 29)
      d = 28;

    return Date (m, d, y);
  }

  // If the period is an 'easy' one, add it to current, and we're done.
  int secs = 0;
  try { Duration du (period); secs = du; }
  catch (...) { secs = 0; }

  return current + secs;
}

////////////////////////////////////////////////////////////////////////////////
// When the status of a recurring child task changes, the parent task must
// update it's mask.
void updateRecurrenceMask (
  std::vector <Task>& all,
  Task& task)
{
  std::string parent = task.get ("parent");
  if (parent != "")
  {
    std::vector <Task>::iterator it;
    for (it = all.begin (); it != all.end (); ++it)
    {
      if (it->get ("uuid") == parent)
      {
        unsigned int index = atoi (task.get ("imask").c_str ());
        std::string mask = it->get ("mask");
        if (mask.length () > index)
        {
          mask[index] = (task.getStatus () == Task::pending)   ? '-'
                      : (task.getStatus () == Task::completed) ? '+'
                      : (task.getStatus () == Task::deleted)   ? 'X'
                      : (task.getStatus () == Task::waiting)   ? 'W'
                      :                                          '?';

          it->set ("mask", mask);
          context.tdb.update (*it);
        }
        else
        {
          std::string mask;
          for (unsigned int i = 0; i < index; ++i)
            mask += "?";

          mask += (task.getStatus () == Task::pending)   ? '-'
                : (task.getStatus () == Task::completed) ? '+'
                : (task.getStatus () == Task::deleted)   ? 'X'
                : (task.getStatus () == Task::waiting)   ? 'W'
                :                                          '?';
        }

        return;  // No point continuing the loop.
      }
    }
  }
}

////////////////////////////////////////////////////////////////////////////////
// Determines whether a task is overdue.  Returns
//   0 = not due at all
//   1 = imminent
//   2 = today
//   3 = overdue
int getDueState (const std::string& due)
{
  if (due.length ())
  {
    Date dt (::atoi (due.c_str ()));

    // rightNow is the current date + time.
    Date rightNow;
    Date thisDay (rightNow.month (), rightNow.day (), rightNow.year ());

    if (dt < rightNow)
      return 3;

    if (rightNow.sameDay (dt))
      return 2;

    int imminentperiod = context.config.getInteger ("due");

    if (imminentperiod == 0)
      return 1;

    Date imminentDay = thisDay + imminentperiod * 86400;
    if (dt < imminentDay)
      return 1;
  }

  return 0;
}

////////////////////////////////////////////////////////////////////////////////
// Returns a Boolean indicator as to whether a nag message was generated, so
// that commands can control the number of nag messages displayed (ie one is
// enough).
bool nag (Task& task)
{
  // Special tag overrides nagging.
  if (task.hasTag ("nonag"))
    return false;

  std::string nagMessage = context.config.get ("nag");
  if (nagMessage != "")
  {
    // Load all pending tasks.
    std::vector <Task> tasks;
    Filter filter;

    // Piggy-back on existing locked TDB.
    context.tdb.loadPending (tasks, filter);

    // Counters.
    int overdue    = 0;
    int high       = 0;
    int medium     = 0;
    int low        = 0;
    bool isOverdue = false;
    char pri       = ' ';

    // Scan all pending tasks.
    foreach (t, tasks)
    {
      if (t->id == task.id)
      {
        if (getDueState (t->get ("due")) == 3)
          isOverdue = true;

        std::string priority = t->get ("priority");
        if (priority.length ())
          pri = priority[0];
      }
      else if (t->getStatus () == Task::pending)
      {
        if (getDueState (t->get ("due")) == 3)
          overdue++;

        std::string priority = t->get ("priority");
        if (priority.length ())
        {
          switch (priority[0])
          {
          case 'H': high++;   break;
          case 'M': medium++; break;
          case 'L': low++;    break;
          }
        }
      }
    }

    // General form is "if there are no more deserving tasks", suppress the nag.
    if (isOverdue                                                 ) return false;
    if (pri == 'H' && !overdue                                    ) return false;
    if (pri == 'M' && !overdue && !high                           ) return false;
    if (pri == 'L' && !overdue && !high && !medium                ) return false;
    if (pri == ' ' && !overdue && !high && !medium && !low        ) return false;
    if (dependencyIsBlocking (task) && !dependencyIsBlocked (task)) return false;

    // All the excuses are made, all that remains is to nag the user.
    context.footnote (nagMessage);
    return true;
  }

  return false;
}

////////////////////////////////////////////////////////////////////////////////
<commit_msg>Code Cleanup<commit_after>////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
//     Free Software Foundation, Inc.,
//     51 Franklin Street, Fifth Floor,
//     Boston, MA
//     02110-1301
//     USA
//
////////////////////////////////////////////////////////////////////////////////

#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <pwd.h>
#include <time.h>

#include "Context.h"
#include "Date.h"
#include "Duration.h"
#include "text.h"
#include "util.h"
#include "main.h"

#ifdef HAVE_LIBNCURSES
#include <ncurses.h>
#endif

// Global context for use by all.
extern Context context;

////////////////////////////////////////////////////////////////////////////////
// Scans all tasks, and for any recurring tasks, determines whether any new
// child tasks need to be generated to fill gaps.
void handleRecurrence ()
{
  std::vector <Task> tasks;
  Filter filter;
  context.tdb.loadPending (tasks, filter);

  std::vector <Task> modified;

  // Look at all tasks and find any recurring ones.
  foreach (t, tasks)
  {
    if (t->getStatus () == Task::recurring)
    {
      // Generate a list of due dates for this recurring task, regardless of
      // the mask.
      std::vector <Date> due;
      if (!generateDueDates (*t, due))
      {
        std::cout << "Task ("
                  << trim (t->get ("description"))
                  << ") has past its 'until' date, and has been deleted.\n";

        // Determine the end date.
        char endTime[16];
        sprintf (endTime, "%u", (unsigned int) time (NULL));
        t->set ("end", endTime);
        t->setStatus (Task::deleted);
        context.tdb.update (*t);
        continue;
      }

      // Get the mask from the parent task.
      std::string mask = t->get ("mask");

      // Iterate over the due dates, and check each against the mask.
      bool changed = false;
      unsigned int i = 0;
      foreach (d, due)
      {
        if (mask.length () <= i)
        {
          changed = true;

          Task rec (*t);                         // Clone the parent.
          rec.set ("uuid", uuid ());             // New UUID.
          rec.set ("parent", t->get ("uuid"));   // Remember mom.

          char dueDate[16];
          sprintf (dueDate, "%u", (unsigned int) d->toEpoch ());
          rec.set ("due", dueDate);              // Store generated due date.

          if (t->has ("wait"))
          {
            Date old_wait (atoi (t->get ("wait").c_str ()));
            Date old_due (atoi (t->get ("due").c_str ()));
            Date due (*d);
            sprintf (dueDate, "%u", (unsigned int) (due + (old_wait - old_due)).toEpoch ());
            rec.set ("wait", dueDate);
            rec.setStatus (Task::waiting);
            mask += 'W';
          }
          else
          {
            mask += '-';
            rec.setStatus (Task::pending);
          }

          char indexMask[12];
          sprintf (indexMask, "%u", (unsigned int) i);
          rec.set ("imask", indexMask);          // Store index into mask.

          rec.remove ("mask");                   // Remove the mask of the parent.

          // Add the new task to the vector, for immediate use.
          modified.push_back (rec);

          // Add the new task to the DB.
          context.tdb.add (rec);
        }

        ++i;
      }

      // Only modify the parent if necessary.
      if (changed)
      {
        t->set ("mask", mask);
        context.tdb.update (*t);
      }
    }
    else
      modified.push_back (*t);
  }

  tasks = modified;
}

////////////////////////////////////////////////////////////////////////////////
// Determine a start date (due), an optional end date (until), and an increment
// period (recur).  Then generate a set of corresponding dates.
//
// Returns false if the parent recurring task is depleted.
bool generateDueDates (Task& parent, std::vector <Date>& allDue)
{
  // Determine due date, recur period and until date.
  Date due (atoi (parent.get ("due").c_str ()));
  std::string recur = parent.get ("recur");

  bool specificEnd = false;
  Date until;
  if (parent.get ("until") != "")
  {
    until = Date (atoi (parent.get ("until").c_str ()));
    specificEnd = true;
  }

  int recurrence_limit = context.config.getInteger ("recurrence.limit");
  int recurrence_counter = 0;
  Date now;
  for (Date i = due; ; i = getNextRecurrence (i, recur))
  {
    allDue.push_back (i);

    if (specificEnd && i > until)
    {
      // If i > until, it means there are no more tasks to generate, and if the
      // parent mask contains all + or X, then there never will be another task
      // to generate, and this parent task may be safely reaped.
      std::string mask = parent.get ("mask");
      if (mask.length () == allDue.size () &&
          mask.find ('-') == std::string::npos)
        return false;

      return true;
    }

    if (i > now)
      ++recurrence_counter;

    if (recurrence_counter >= recurrence_limit)
      return true;
  }

  return true;
}

////////////////////////////////////////////////////////////////////////////////
Date getNextRecurrence (Date& current, std::string& period)
{
  int m = current.month ();
  int d = current.day ();
  int y = current.year ();

  // Some periods are difficult, because they can be vague.
  if (period == "monthly")
  {
    if (++m > 12)
    {
       m -= 12;
       ++y;
    }

    while (! Date::valid (m, d, y))
      --d;

    return Date (m, d, y);
  }

  if (period == "weekdays")
  {
    int dow = current.dayOfWeek ();
    int days;

         if (dow == 5) days = 3;
    else if (dow == 6) days = 2;
    else               days = 1;

    return current + (days * 86400);
  }

  if (isdigit (period[0]) && period[period.length () - 1] == 'm')
  {
    std::string numeric = period.substr (0, period.length () - 1);
    int increment = atoi (numeric.c_str ());

    m += increment;
    while (m > 12)
    {
       m -= 12;
       ++y;
    }

    while (! Date::valid (m, d, y))
      --d;

    return Date (m, d, y);
  }

  else if (period == "quarterly")
  {
    m += 3;
    if (m > 12)
    {
       m -= 12;
       ++y;
    }

    while (! Date::valid (m, d, y))
      --d;

    return Date (m, d, y);
  }

  else if (isdigit (period[0]) && period[period.length () - 1] == 'q')
  {
    std::string numeric = period.substr (0, period.length () - 1);
    int increment = atoi (numeric.c_str ());

    m += 3 * increment;
    while (m > 12)
    {
       m -= 12;
       ++y;
    }

    while (! Date::valid (m, d, y))
      --d;

    return Date (m, d, y);
  }

  else if (period == "semiannual")
  {
    m += 6;
    if (m > 12)
    {
       m -= 12;
       ++y;
    }

    while (! Date::valid (m, d, y))
      --d;

    return Date (m, d, y);
  }

  else if (period == "bimonthly")
  {
    m += 2;
    if (m > 12)
    {
       m -= 12;
       ++y;
    }

    while (! Date::valid (m, d, y))
      --d;

    return Date (m, d, y);
  }

  else if (period == "biannual"    ||
           period == "biyearly")
  {
    y += 2;

    return Date (m, d, y);
  }

  else if (period == "annual" ||
           period == "yearly")
  {
    y += 1;

    // If the due data just happens to be 2/29 in a leap year, then simply
    // incrementing y is going to create an invalid date.
    if (m == 2 && d == 29)
      d = 28;

    return Date (m, d, y);
  }

  // If the period is an 'easy' one, add it to current, and we're done.
  int secs = 0;
  try { Duration du (period); secs = du; }
  catch (...) { secs = 0; }

  return current + secs;
}

////////////////////////////////////////////////////////////////////////////////
// When the status of a recurring child task changes, the parent task must
// update it's mask.
void updateRecurrenceMask (
  std::vector <Task>& all,
  Task& task)
{
  std::string parent = task.get ("parent");
  if (parent != "")
  {
    std::vector <Task>::iterator it;
    for (it = all.begin (); it != all.end (); ++it)
    {
      if (it->get ("uuid") == parent)
      {
        unsigned int index = atoi (task.get ("imask").c_str ());
        std::string mask = it->get ("mask");
        if (mask.length () > index)
        {
          mask[index] = (task.getStatus () == Task::pending)   ? '-'
                      : (task.getStatus () == Task::completed) ? '+'
                      : (task.getStatus () == Task::deleted)   ? 'X'
                      : (task.getStatus () == Task::waiting)   ? 'W'
                      :                                          '?';

          it->set ("mask", mask);
          context.tdb.update (*it);
        }
        else
        {
          std::string mask;
          for (unsigned int i = 0; i < index; ++i)
            mask += "?";

          mask += (task.getStatus () == Task::pending)   ? '-'
                : (task.getStatus () == Task::completed) ? '+'
                : (task.getStatus () == Task::deleted)   ? 'X'
                : (task.getStatus () == Task::waiting)   ? 'W'
                :                                          '?';
        }

        return;  // No point continuing the loop.
      }
    }
  }
}

////////////////////////////////////////////////////////////////////////////////
// Determines whether a task is overdue.  Returns
//   0 = not due at all
//   1 = imminent
//   2 = today
//   3 = overdue
int getDueState (const std::string& due)
{
  if (due.length ())
  {
    Date dt (::atoi (due.c_str ()));

    // rightNow is the current date + time.
    Date rightNow;
    Date thisDay (rightNow.month (), rightNow.day (), rightNow.year ());

    if (dt < rightNow)
      return 3;

    if (rightNow.sameDay (dt))
      return 2;

    int imminentperiod = context.config.getInteger ("due");

    if (imminentperiod == 0)
      return 1;

    Date imminentDay = thisDay + imminentperiod * 86400;
    if (dt < imminentDay)
      return 1;
  }

  return 0;
}

////////////////////////////////////////////////////////////////////////////////
// Returns a Boolean indicator as to whether a nag message was generated, so
// that commands can control the number of nag messages displayed (ie one is
// enough).
bool nag (Task& task)
{
  // Special tag overrides nagging.
  if (task.hasTag ("nonag"))
    return false;

  std::string nagMessage = context.config.get ("nag");
  if (nagMessage != "")
  {
    // Load all pending tasks.
    std::vector <Task> tasks;
    Filter filter;

    // Piggy-back on existing locked TDB.
    context.tdb.loadPending (tasks, filter);

    // Counters.
    int overdue    = 0;
    int high       = 0;
    int medium     = 0;
    int low        = 0;
    bool isOverdue = false;
    char pri       = ' ';

    // Scan all pending tasks.
    foreach (t, tasks)
    {
      if (t->id == task.id)
      {
        if (getDueState (t->get ("due")) == 3)
          isOverdue = true;

        std::string priority = t->get ("priority");
        if (priority.length ())
          pri = priority[0];
      }
      else if (t->getStatus () == Task::pending)
      {
        if (getDueState (t->get ("due")) == 3)
          overdue++;

        std::string priority = t->get ("priority");
        if (priority.length ())
        {
          switch (priority[0])
          {
          case 'H': high++;   break;
          case 'M': medium++; break;
          case 'L': low++;    break;
          }
        }
      }
    }

    // General form is "if there are no more deserving tasks", suppress the nag.
    if (isOverdue                                                 ) return false;
    if (pri == 'H' && !overdue                                    ) return false;
    if (pri == 'M' && !overdue && !high                           ) return false;
    if (pri == 'L' && !overdue && !high && !medium                ) return false;
    if (pri == ' ' && !overdue && !high && !medium && !low        ) return false;
    if (dependencyIsBlocking (task) && !dependencyIsBlocked (task)) return false;

    // All the excuses are made, all that remains is to nag the user.
    context.footnote (nagMessage);
    return true;
  }

  return false;
}

////////////////////////////////////////////////////////////////////////////////
<|endoftext|>


<commit_before>// Copyright[2015] <kangic21@gmail.com>

#include <cstdio>

#include "./runner.h"

namespace eznetpp {

runner::runner(void) {
}

runner::~runner(void) {
}

void runner::run(void) {
  printf("hello test!");
}

}  // namespace eznetpp
<commit_msg>updated runner class code<commit_after>// Copyright[2015] <kangic21@gmail.com>

#include <cstdio>

#include "./runner.h"

namespace eznetpp {

runner::runner(void) {
}

runner::~runner(void) {
}

void runner::run(void) {

}

}  // namespace eznetpp
<|endoftext|>


<commit_before>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright 2011-2018 Dominik Charousset                                     *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#include "caf/stream_manager.hpp"

#include "caf/actor_addr.hpp"
#include "caf/actor_cast.hpp"
#include "caf/actor_control_block.hpp"
#include "caf/error.hpp"
#include "caf/expected.hpp"
#include "caf/inbound_path.hpp"
#include "caf/scheduled_actor.hpp"
#include "caf/logger.hpp"
#include "caf/message.hpp"
#include "caf/outbound_path.hpp"
#include "caf/response_promise.hpp"
#include "caf/sec.hpp"

namespace caf {

stream_manager::stream_manager(scheduled_actor* selfptr, stream_priority prio)
    : self_(selfptr),
      pending_handshakes_(0),
      priority_(prio),
      continuous_(false) {
  // nop
}

stream_manager::~stream_manager() {
  // nop
}

void stream_manager::handle(inbound_path*, downstream_msg::batch&) {
  CAF_LOG_WARNING("unimplemented base handler for batches called");
}

void stream_manager::handle(inbound_path*, downstream_msg::close&) {
  // nop
}

void stream_manager::handle(inbound_path*, downstream_msg::forced_close& x) {
  abort(std::move(x.reason));
}

bool stream_manager::handle(stream_slots slots, upstream_msg::ack_open& x) {
  CAF_LOG_TRACE(CAF_ARG(slots) << CAF_ARG(x));
  auto ptr = out().path(slots.receiver);
  if (ptr == nullptr)
    return false;
  if (!ptr->pending()) {
    CAF_LOG_ERROR("received repeated ack_open");
    return false;
  }
  if (ptr->hdl != x.rebind_from) {
    CAF_LOG_ERROR("received ack_open with invalid rebind_from");
    return false;
  }
  if (x.rebind_from != x.rebind_to) {
    ptr->hdl = x.rebind_to;
  }
  ptr->slots.receiver = slots.sender;
  ptr->open_credit = x.initial_demand;
  ptr->desired_batch_size = x.desired_batch_size;
  --pending_handshakes_;
  push();
  return true;
}

void stream_manager::handle(stream_slots slots, upstream_msg::ack_batch& x) {
  CAF_LOG_TRACE(CAF_ARG(slots) << CAF_ARG(x));
  auto path = out().path(slots.receiver);
  if (path != nullptr) {
    path->open_credit += x.new_capacity;
    path->desired_batch_size = x.desired_batch_size;
    path->next_ack_id = x.acknowledged_id + 1;
    push();
  }
}

void stream_manager::handle(stream_slots slots, upstream_msg::drop&) {
  error tmp;
  out().remove_path(slots.receiver, std::move(tmp), true);
}

void stream_manager::handle(stream_slots slots, upstream_msg::forced_drop& x) {
  if (out().remove_path(slots.receiver, x.reason, true))
    abort(std::move(x.reason));
}

void stream_manager::stop() {
  CAF_LOG_TRACE("");
  out().close();
  error tmp;
  finalize(tmp);
  self_->erase_inbound_paths_later(this);
}

void stream_manager::abort(error reason) {
  CAF_LOG_TRACE(CAF_ARG(reason));
  out().abort(reason);
  finalize(reason);
  self_->erase_inbound_paths_later(this, std::move(reason));
}

void stream_manager::push() {
  CAF_LOG_TRACE("");
  do {
    out().emit_batches();
  } while (generate_messages());
}

bool stream_manager::congested() const noexcept {
  return false;
}

void stream_manager::deliver_handshake(response_promise& rp, stream_slot slot,
                                       message handshake) {
  CAF_LOG_TRACE(CAF_ARG(rp) << CAF_ARG(slot) << CAF_ARG(handshake));
  CAF_ASSERT(rp.pending());
  CAF_ASSERT(slot != invalid_stream_slot);
  ++pending_handshakes_;
  auto next = rp.next();
  rp.deliver(open_stream_msg{slot, std::move(handshake), self_->ctrl(), next,
                             priority_});
}

bool stream_manager::generate_messages() {
  return false;
}

void stream_manager::cycle_timeout(size_t) {
  // TODO: make pure virtual
}

void stream_manager::register_input_path(inbound_path* ptr) {
  CAF_ASSERT(ptr != nullptr);
  CAF_LOG_TRACE(CAF_ARG2("path", *ptr));
  inbound_paths_.emplace_back(ptr);
}

void stream_manager::deregister_input_path(inbound_path* ptr) noexcept {
  CAF_ASSERT(ptr != nullptr);
  CAF_LOG_TRACE(CAF_ARG2("path", *ptr));
  CAF_ASSERT(inbound_paths_.size() > 0);
  using std::swap;
  if (ptr != inbound_paths_.back()) {
    auto i = std::find(inbound_paths_.begin(), inbound_paths_.end(), ptr);
    CAF_ASSERT(i != inbound_paths_.end());
    swap(*i, inbound_paths_.back());
  }
  inbound_paths_.pop_back();
  CAF_LOG_DEBUG(inbound_paths_.size() << "paths remaining");
}

void stream_manager::remove_input_path(stream_slot slot, error reason,
                                       bool silent) {
  if (silent)
    self_->erase_inbound_path_later(slot);
  else
    self_->erase_inbound_path_later(slot, std::move(reason));
}

inbound_path* stream_manager::get_inbound_path(stream_slot x) const noexcept {
  auto pred = [=](inbound_path* ptr) {
    return ptr->slots.receiver == x;
  };
  auto e = inbound_paths_.end();
  auto i = std::find_if(inbound_paths_.begin(), e, pred);
  return i != e ? *i : nullptr;
}

bool stream_manager::inbound_paths_up_to_date() const noexcept {
  auto up_to_date = [](inbound_path* x) { return x->up_to_date(); };
  return std::all_of(inbound_paths_.begin(), inbound_paths_.end(), up_to_date);
}

stream_slot stream_manager::add_unchecked_outbound_path_impl(response_promise& rp,
                                                          message handshake) {
  CAF_LOG_TRACE(CAF_ARG(rp) << CAF_ARG(handshake));
  CAF_ASSERT(out().terminal() == false);
  if (!rp.pending()) {
    CAF_LOG_WARNING("add_outbound_path called with next == nullptr");
    rp.deliver(sec::no_downstream_stages_defined);
    return invalid_stream_slot;
  }
  auto slot = self_->assign_next_pending_slot_to(this);
  auto path = out().add_path(slot, rp.next());
  CAF_IGNORE_UNUSED(path);
  CAF_ASSERT(path != nullptr);
  // Build pipeline by forwarding handshake along the path.
  deliver_handshake(rp, slot, std::move(handshake));
  generate_messages();
  return slot;
}

stream_slot stream_manager::add_unchecked_outbound_path_impl(strong_actor_ptr next,
                                                          message handshake) {
  CAF_LOG_TRACE(CAF_ARG(next) << CAF_ARG(handshake));
  response_promise rp{self_->ctrl(), nullptr, {next}, make_message_id()};
  return add_unchecked_outbound_path_impl(rp, std::move(handshake));
}

stream_slot stream_manager::add_unchecked_outbound_path_impl(message handshake) {
  CAF_LOG_TRACE(CAF_ARG(handshake));
  auto rp = self_->make_response_promise();
  return add_unchecked_outbound_path_impl(rp, std::move(handshake));
}

stream_slot stream_manager::add_unchecked_inbound_path_impl() {
  CAF_LOG_TRACE("");
  auto x = self_->current_mailbox_element();
  if (x == nullptr || !x->content().match_elements<open_stream_msg>()) {
    CAF_LOG_ERROR("add_unchecked_inbound_path called, but current message "
                  "is not an open_stream_msg");
    return invalid_stream_slot;
  }
  auto& osm = x->content().get_mutable_as<open_stream_msg>(0);
  if (out().terminal() && !self_->current_forwarding_stack().empty()) {
    // Sinks must always terminate the stream.
    CAF_LOG_WARNING("add_unchecked_inbound_path called in a sink, but the "
                    "handshake has further stages");
    stream_slots path_id{osm.slot, 0};
    auto code = sec::cannot_add_downstream;
    inbound_path::emit_irregular_shutdown(self_, path_id,
                                          std::move(osm.prev_stage), code);
    auto rp = self_->make_response_promise();
    rp.deliver(code);
    return invalid_stream_slot;
  }
  auto slot = assign_next_slot();
  stream_slots path_id{osm.slot, slot};
  auto ptr = self_->make_inbound_path(this, path_id, std::move(osm.prev_stage));
  CAF_ASSERT(ptr != nullptr);
  ptr->emit_ack_open(self_, actor_cast<actor_addr>(osm.original_stage));
  return slot;
}

stream_slot stream_manager::assign_next_slot() {
  return self_->assign_next_slot_to(this);
}

stream_slot stream_manager::assign_next_pending_slot() {
  return self_->assign_next_pending_slot_to(this);
}

void stream_manager::finalize(const error&) {
  // nop
}

message stream_manager::make_final_result() {
  return none;
}

error stream_manager::process_batch(message&) {
  CAF_LOG_ERROR("stream_manager::process_batch called");
  return sec::invalid_stream_state;
}

void stream_manager::output_closed(error) {
  // nop
}

void stream_manager::downstream_demand(outbound_path*, long) {
  CAF_LOG_ERROR("stream_manager::downstream_demand called");
}

void stream_manager::input_closed(error) {
  // nop
}

} // namespace caf
<commit_msg>Add sanity checks<commit_after>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright 2011-2018 Dominik Charousset                                     *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#include "caf/stream_manager.hpp"

#include "caf/actor_addr.hpp"
#include "caf/actor_cast.hpp"
#include "caf/actor_control_block.hpp"
#include "caf/error.hpp"
#include "caf/expected.hpp"
#include "caf/inbound_path.hpp"
#include "caf/scheduled_actor.hpp"
#include "caf/logger.hpp"
#include "caf/message.hpp"
#include "caf/outbound_path.hpp"
#include "caf/response_promise.hpp"
#include "caf/sec.hpp"

namespace caf {

stream_manager::stream_manager(scheduled_actor* selfptr, stream_priority prio)
    : self_(selfptr),
      pending_handshakes_(0),
      priority_(prio),
      continuous_(false) {
  // nop
}

stream_manager::~stream_manager() {
  // nop
}

void stream_manager::handle(inbound_path*, downstream_msg::batch&) {
  CAF_LOG_WARNING("unimplemented base handler for batches called");
}

void stream_manager::handle(inbound_path*, downstream_msg::close&) {
  // nop
}

void stream_manager::handle(inbound_path*, downstream_msg::forced_close& x) {
  abort(std::move(x.reason));
}

bool stream_manager::handle(stream_slots slots, upstream_msg::ack_open& x) {
  CAF_LOG_TRACE(CAF_ARG(slots) << CAF_ARG(x));
  CAF_ASSERT(x.desired_batch_size > 0);
  auto ptr = out().path(slots.receiver);
  if (ptr == nullptr)
    return false;
  if (!ptr->pending()) {
    CAF_LOG_ERROR("received repeated ack_open");
    return false;
  }
  if (ptr->hdl != x.rebind_from) {
    CAF_LOG_ERROR("received ack_open with invalid rebind_from");
    return false;
  }
  if (x.rebind_from != x.rebind_to) {
    ptr->hdl = x.rebind_to;
  }
  ptr->slots.receiver = slots.sender;
  ptr->open_credit = x.initial_demand;
  ptr->desired_batch_size = x.desired_batch_size;
  --pending_handshakes_;
  push();
  return true;
}

void stream_manager::handle(stream_slots slots, upstream_msg::ack_batch& x) {
  CAF_LOG_TRACE(CAF_ARG(slots) << CAF_ARG(x));
  CAF_ASSERT(x.desired_batch_size > 0);
  auto path = out().path(slots.receiver);
  if (path != nullptr) {
    path->open_credit += x.new_capacity;
    path->desired_batch_size = x.desired_batch_size;
    path->next_ack_id = x.acknowledged_id + 1;
    push();
  }
}

void stream_manager::handle(stream_slots slots, upstream_msg::drop&) {
  error tmp;
  out().remove_path(slots.receiver, std::move(tmp), true);
}

void stream_manager::handle(stream_slots slots, upstream_msg::forced_drop& x) {
  if (out().remove_path(slots.receiver, x.reason, true))
    abort(std::move(x.reason));
}

void stream_manager::stop() {
  CAF_LOG_TRACE("");
  out().close();
  error tmp;
  finalize(tmp);
  self_->erase_inbound_paths_later(this);
}

void stream_manager::abort(error reason) {
  CAF_LOG_TRACE(CAF_ARG(reason));
  out().abort(reason);
  finalize(reason);
  self_->erase_inbound_paths_later(this, std::move(reason));
}

void stream_manager::push() {
  CAF_LOG_TRACE("");
  do {
    out().emit_batches();
  } while (generate_messages());
}

bool stream_manager::congested() const noexcept {
  return false;
}

void stream_manager::deliver_handshake(response_promise& rp, stream_slot slot,
                                       message handshake) {
  CAF_LOG_TRACE(CAF_ARG(rp) << CAF_ARG(slot) << CAF_ARG(handshake));
  CAF_ASSERT(rp.pending());
  CAF_ASSERT(slot != invalid_stream_slot);
  ++pending_handshakes_;
  auto next = rp.next();
  rp.deliver(open_stream_msg{slot, std::move(handshake), self_->ctrl(), next,
                             priority_});
}

bool stream_manager::generate_messages() {
  return false;
}

void stream_manager::cycle_timeout(size_t) {
  // TODO: make pure virtual
}

void stream_manager::register_input_path(inbound_path* ptr) {
  CAF_ASSERT(ptr != nullptr);
  CAF_LOG_TRACE(CAF_ARG2("path", *ptr));
  inbound_paths_.emplace_back(ptr);
}

void stream_manager::deregister_input_path(inbound_path* ptr) noexcept {
  CAF_ASSERT(ptr != nullptr);
  CAF_LOG_TRACE(CAF_ARG2("path", *ptr));
  CAF_ASSERT(inbound_paths_.size() > 0);
  using std::swap;
  if (ptr != inbound_paths_.back()) {
    auto i = std::find(inbound_paths_.begin(), inbound_paths_.end(), ptr);
    CAF_ASSERT(i != inbound_paths_.end());
    swap(*i, inbound_paths_.back());
  }
  inbound_paths_.pop_back();
  CAF_LOG_DEBUG(inbound_paths_.size() << "paths remaining");
}

void stream_manager::remove_input_path(stream_slot slot, error reason,
                                       bool silent) {
  if (silent)
    self_->erase_inbound_path_later(slot);
  else
    self_->erase_inbound_path_later(slot, std::move(reason));
}

inbound_path* stream_manager::get_inbound_path(stream_slot x) const noexcept {
  auto pred = [=](inbound_path* ptr) {
    return ptr->slots.receiver == x;
  };
  auto e = inbound_paths_.end();
  auto i = std::find_if(inbound_paths_.begin(), e, pred);
  return i != e ? *i : nullptr;
}

bool stream_manager::inbound_paths_up_to_date() const noexcept {
  auto up_to_date = [](inbound_path* x) { return x->up_to_date(); };
  return std::all_of(inbound_paths_.begin(), inbound_paths_.end(), up_to_date);
}

stream_slot stream_manager::add_unchecked_outbound_path_impl(response_promise& rp,
                                                          message handshake) {
  CAF_LOG_TRACE(CAF_ARG(rp) << CAF_ARG(handshake));
  CAF_ASSERT(out().terminal() == false);
  if (!rp.pending()) {
    CAF_LOG_WARNING("add_outbound_path called with next == nullptr");
    rp.deliver(sec::no_downstream_stages_defined);
    return invalid_stream_slot;
  }
  auto slot = self_->assign_next_pending_slot_to(this);
  auto path = out().add_path(slot, rp.next());
  CAF_IGNORE_UNUSED(path);
  CAF_ASSERT(path != nullptr);
  // Build pipeline by forwarding handshake along the path.
  deliver_handshake(rp, slot, std::move(handshake));
  generate_messages();
  return slot;
}

stream_slot stream_manager::add_unchecked_outbound_path_impl(strong_actor_ptr next,
                                                          message handshake) {
  CAF_LOG_TRACE(CAF_ARG(next) << CAF_ARG(handshake));
  response_promise rp{self_->ctrl(), nullptr, {next}, make_message_id()};
  return add_unchecked_outbound_path_impl(rp, std::move(handshake));
}

stream_slot stream_manager::add_unchecked_outbound_path_impl(message handshake) {
  CAF_LOG_TRACE(CAF_ARG(handshake));
  auto rp = self_->make_response_promise();
  return add_unchecked_outbound_path_impl(rp, std::move(handshake));
}

stream_slot stream_manager::add_unchecked_inbound_path_impl() {
  CAF_LOG_TRACE("");
  auto x = self_->current_mailbox_element();
  if (x == nullptr || !x->content().match_elements<open_stream_msg>()) {
    CAF_LOG_ERROR("add_unchecked_inbound_path called, but current message "
                  "is not an open_stream_msg");
    return invalid_stream_slot;
  }
  auto& osm = x->content().get_mutable_as<open_stream_msg>(0);
  if (out().terminal() && !self_->current_forwarding_stack().empty()) {
    // Sinks must always terminate the stream.
    CAF_LOG_WARNING("add_unchecked_inbound_path called in a sink, but the "
                    "handshake has further stages");
    stream_slots path_id{osm.slot, 0};
    auto code = sec::cannot_add_downstream;
    inbound_path::emit_irregular_shutdown(self_, path_id,
                                          std::move(osm.prev_stage), code);
    auto rp = self_->make_response_promise();
    rp.deliver(code);
    return invalid_stream_slot;
  }
  auto slot = assign_next_slot();
  stream_slots path_id{osm.slot, slot};
  auto ptr = self_->make_inbound_path(this, path_id, std::move(osm.prev_stage));
  CAF_ASSERT(ptr != nullptr);
  ptr->emit_ack_open(self_, actor_cast<actor_addr>(osm.original_stage));
  return slot;
}

stream_slot stream_manager::assign_next_slot() {
  return self_->assign_next_slot_to(this);
}

stream_slot stream_manager::assign_next_pending_slot() {
  return self_->assign_next_pending_slot_to(this);
}

void stream_manager::finalize(const error&) {
  // nop
}

message stream_manager::make_final_result() {
  return none;
}

error stream_manager::process_batch(message&) {
  CAF_LOG_ERROR("stream_manager::process_batch called");
  return sec::invalid_stream_state;
}

void stream_manager::output_closed(error) {
  // nop
}

void stream_manager::downstream_demand(outbound_path*, long) {
  CAF_LOG_ERROR("stream_manager::downstream_demand called");
}

void stream_manager::input_closed(error) {
  // nop
}

} // namespace caf
<|endoftext|>