<commit_before>#include <iostream>
#include <Halide.h>

#include "recfilter.h"

using namespace Halide;

using std::map;
using std::vector;
using std::string;
using std::cerr;
using std::cout;
using std::endl;

int main(int argc, char **argv) {
    Arguments args(argc, argv);

    bool  nocheck = args.nocheck;
    int   width   = args.width;
    int   height  = args.width;
    int   tile_width = args.block;
    int   iter    = args.iterations;

    Image<float> random_image = generate_random_image<float>(width,height);

    ImageParam image(type_of<float>(), 2);
    image.set(random_image);

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

    RecFilterDim x("x", width);
    RecFilterDim y("y", height);

    RecFilter F;

    F(x,y) = image(x,y);

    F.add_filter(+x, {1.0, 1.0});
    F.add_filter(+y, {1.0, 1.0});

    F.split(x, tile_width, y, tile_width);

///     F.transpose_dimensions ("SAT_Intra_Tail_y_1", "xi", "yi");
///     F.interleave_func("SAT_Intra_Tail_x_0", "SAT_Intra_Tail_y_1", "SAT_Intra_Tail", "xi", 1);
///
///     F.remove_pure_def("SAT_Intra_CTail_x_0_Sub");
///     F.remove_pure_def("SAT_Intra_CTail_y_1_Sub");
///
///     cerr << F << endl;
///
///    // ----------------------------------------------------------------------------------------------
///
///    {
///        Var t("t");
///
///        RecFilterDim x", channels)i("xi"), yi("yi"), rxi("rxi"), rxf("rxf"), rxt("rxt");
///        RecFilterDim x", channels)o("xo"), yo("yo"), ryi("ryi"), ryf("ryf"), ryt("ryt");
///
///        Var rxox("rxo.x$r"), rxoy("rxo.y$r");
///        Var ryox("ryo.x$r"), ryoy("ryo.y$r");
///
///        Func SAT             = F.func("SAT");
///        Func SAT_Final       = F.func("SAT_Final");
///        Func SAT_Intra       = F.func("SAT_Intra");
///        Func SAT_Tail        = F.func("SAT_Intra_Tail");
///        Func SAT_CTail_x     = F.func("SAT_Intra_CTail_x_0_Sub");
///        Func SAT_CTail_y     = F.func("SAT_Intra_CTail_y_1_Sub");
///        Func SAT_CTail_xy    = F.func("SAT_Intra_CTail_x_0_y_1");
///        Func SAT_CTail_xy_sub= F.func("SAT_Intra_CTail_x_0_y_1_Sub");
///        Func SAT_Deps_x      = F.func("SAT_Intra_Deps_x_0");
///        Func SAT_Deps_y      = F.func("SAT_Intra_Deps_y_1");
///
///        SAT_Intra.compute_at(SAT_Tail, Var::gpu_blocks());
///        SAT_Intra.update(0).split(ryi,yi,t,UNROLL).reorder(t,rxi,yi,xo,yo).gpu_threads(rxi,yi).unroll(t);
///        SAT_Intra.update(1).reorder(rxi,ryi,xo,yo).gpu_threads(ryi).unroll(rxi);
///        SAT_Intra.update(2).reorder(rxt,ryi,xo,yo).gpu_threads(ryi).unroll(rxt);
///        SAT_Intra.update(3).reorder(ryi,rxi,xo,yo).gpu_threads(rxi).unroll(ryi);
///        SAT_Intra.update(4).reorder(ryt,rxi,xo,yo).gpu_threads(rxi).unroll(ryt);
///
///        SAT_Tail.compute_root().reorder_storage(xi,yi,xo,yo);
///        SAT_Tail.split(yi,yi,t,UNROLL).reorder(t,xi,yi,xo,yo).gpu(xo,yo,xi,yi).unroll(t);
///
///        SAT_Tail.compile_to_simplified_lowered_stmt("stmt2.html", 2, width/tile, tile, width/tile, HTML, get_jit_target_from_environment());
///
///        //
///
///        SAT_CTail_x.compute_root().reorder_storage(yi,xi,yo,xo);
///        SAT_CTail_x.update().reorder(rxox,rxoy,yi,yo).fuse(yi,yo,y).gpu_tile(y,MAX_THREADS);
///
///        //
///
///        SAT_CTail_xy_sub.compute_at(SAT_CTail_xy, Var::gpu_blocks());
///        SAT_CTail_xy_sub.split(xo,xo,x,TILES_PER_WARP).reorder(yi,xi,x,xo,yo).gpu_threads(x,yi);
///        SAT_CTail_xy_sub.update().split(xo,xo,x,TILES_PER_WARP).reorder(ryi,rxt,x,xo,yo).gpu_threads(x).unroll(ryi);
///
///        SAT_CTail_xy.compute_root().reorder_storage(yi,xi,yo,xo);
///        SAT_CTail_xy.split(xo,xo,x,TILES_PER_WARP).reorder(yi,xi,x,xo,yo).gpu_threads(x,yi).gpu_blocks(xo,yo);
///
///
///        SAT_CTail_y.compute_root().reorder_storage(xi,yi,xo,yo);
///        SAT_CTail_y.update().reorder(ryox,ryoy,xi,xo).fuse(xi,xo,x).gpu_tile(x,MAX_THREADS);
///
///        //
///
///        SAT_Deps_x.compute_at(SAT, Var::gpu_blocks()).reorder(xi,yi,xo,yo).gpu_threads(yi);
///        SAT_Deps_y.compute_at(SAT, Var::gpu_blocks()).reorder(xi,yi,xo,yo).gpu_threads(xi);
///
///        SAT_Final.compute_at(SAT, Var::gpu_blocks());
///        SAT_Final.update(0).split(ryi,yi,t,UNROLL).reorder(t,rxi,yi,xo,yo).gpu_threads(rxi,yi).unroll(t);
///        SAT_Final.update(1).reorder(rxt,ryi,xo,yo).gpu_threads(ryi).unroll(rxt);
///        SAT_Final.update(2).reorder(rxf,ryi,xo,yo).gpu_threads(ryi).unroll(rxf);
///        SAT_Final.update(3).reorder(ryt,rxi,xo,yo).gpu_threads(rxi).unroll(ryt);
///        SAT_Final.update(4).reorder(ryf,rxi,xo,yo).gpu_threads(rxi).unroll(ryf);
///
///        SAT.compute_root();
///        SAT.split(x,xo,xi,tile).split(y,yo,yi,tile);
///        SAT.split(yi,yi,t,UNROLL).reorder(t,xi,yi,xo,yo).gpu(xo,yo,xi,yi).unroll(t);
///
///        SAT.bound(x,0,width).bound(y,0,height);
///    }

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

    // innermost: unrolled dimensions, then gpu_thread dimensions, then gpu_block dimensions

    int unroll_w = 4;
    int tiles_per_warp = 4;

    F.intra_schedule().compute_locally()
        .reorder_storage(F.tail(), F.inner(), F.outer())
        .unroll         (F.inner_scan());

    F.intra_schedule(1)
        .split          (F.inner(1), unroll_w)
        .unroll         (F.inner(1).split_var())
        .reorder        (F.inner_scan(), F.inner(1).split_var(), F.tail(), F.inner(), F.outer())
        .gpu_threads    (F.inner(0), F.inner(1))
        .gpu_blocks     (F.outer(0), F.outer(1));

    F.intra_schedule(2)
        .split          (F.outer(0), 4)
        .reorder        (F.inner_scan(), F.tail(), F.inner(), F.outer(0).split_var(), F.outer())
        .gpu_threads    (F.outer(0).split_var(), F.inner(0))
        .gpu_blocks     (F.outer(0), F.outer(1));

    F.inter_schedule().compute_globally()
        .reorder_storage(F.inner(), F.tail(), F.outer())
        .unroll         (F.outer_scan())
        .split          (F.outer(0), 2)
        .reorder        (F.outer_scan(), F.inner(), F.tail(), F.outer(0).split_var(), F.outer())
        .gpu_threads    (F.inner(0), F.outer(0).split_var())
        .gpu_blocks     (F.outer(0));

    cerr << F << endl;

    cerr << "\nJIT compilation ... " << endl;
    F.compile_jit("hl_stmt.html");

    cerr << "\nRunning ... " << endl;
    Buffer out(type_of<float>(), width, height);
    float time = F.realize(out, iter);
    cerr << width << "\t" << time << endl;

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

    if (!nocheck) {
        cerr << "\nChecking difference ... " << endl;
        Image<float> hl_out(out);
        Image<float> ref(width,height);

        for (int y=0; y<height; y++) {
            for (int x=0; x<width; x++) {
                ref(x,y) = random_image(x,y);
            }
        }
        for (int y=0; y<height; y++) {
            for (int x=1; x<width; x++) {
                ref(x,y) += ref(x-1,y);
            }
        }
        for (int y=1; y<height; y++) {
            for (int x=0; x<width; x++) {
                ref(x,y) += ref(x,y-1);
            }
        }

        cout << CheckResult<float>(ref,hl_out) << endl;
    }

    return 0;
}
<commit_msg>Schedule changes<commit_after>#include <iostream>
#include <Halide.h>

#include "recfilter.h"

using namespace Halide;

using std::map;
using std::vector;
using std::string;
using std::cerr;
using std::cout;
using std::endl;

int main(int argc, char **argv) {
    Arguments args(argc, argv);

    bool  nocheck = args.nocheck;
    int   width   = args.width;
    int   height  = args.width;
    int   tile_width = args.block;
    int   iter    = args.iterations;

    Image<float> random_image = generate_random_image<float>(width,height);

    ImageParam image(type_of<float>(), 2);
    image.set(random_image);

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

    RecFilterDim x("x", width);
    RecFilterDim y("y", height);

    RecFilter F;

    F(x,y) = image(x,y);

    F.add_filter(+x, {1.0, 1.0});
    F.add_filter(+y, {1.0, 1.0});

    F.split(x, tile_width, y, tile_width);

///     F.transpose_dimensions ("SAT_Intra_Tail_y_1", "xi", "yi");
///     F.interleave_func("SAT_Intra_Tail_x_0", "SAT_Intra_Tail_y_1", "SAT_Intra_Tail", "xi", 1);
///
///     F.remove_pure_def("SAT_Intra_CTail_x_0_Sub");
///     F.remove_pure_def("SAT_Intra_CTail_y_1_Sub");
///
///     cerr << F << endl;
///
///    // ----------------------------------------------------------------------------------------------
///
///    {
///        Var t("t");
///
///        RecFilterDim x", channels)i("xi"), yi("yi"), rxi("rxi"), rxf("rxf"), rxt("rxt");
///        RecFilterDim x", channels)o("xo"), yo("yo"), ryi("ryi"), ryf("ryf"), ryt("ryt");
///
///        Var rxox("rxo.x$r"), rxoy("rxo.y$r");
///        Var ryox("ryo.x$r"), ryoy("ryo.y$r");
///
///        Func SAT             = F.func("SAT");
///        Func SAT_Final       = F.func("SAT_Final");
///        Func SAT_Intra       = F.func("SAT_Intra");
///        Func SAT_Tail        = F.func("SAT_Intra_Tail");
///        Func SAT_CTail_x     = F.func("SAT_Intra_CTail_x_0_Sub");
///        Func SAT_CTail_y     = F.func("SAT_Intra_CTail_y_1_Sub");
///        Func SAT_CTail_xy    = F.func("SAT_Intra_CTail_x_0_y_1");
///        Func SAT_CTail_xy_sub= F.func("SAT_Intra_CTail_x_0_y_1_Sub");
///        Func SAT_Deps_x      = F.func("SAT_Intra_Deps_x_0");
///        Func SAT_Deps_y      = F.func("SAT_Intra_Deps_y_1");
///
///        SAT_Intra.compute_at(SAT_Tail, Var::gpu_blocks());
///        SAT_Intra.update(0).split(ryi,yi,t,UNROLL).reorder(t,rxi,yi,xo,yo).gpu_threads(rxi,yi).unroll(t);
///        SAT_Intra.update(1).reorder(rxi,ryi,xo,yo).gpu_threads(ryi).unroll(rxi);
///        SAT_Intra.update(2).reorder(rxt,ryi,xo,yo).gpu_threads(ryi).unroll(rxt);
///        SAT_Intra.update(3).reorder(ryi,rxi,xo,yo).gpu_threads(rxi).unroll(ryi);
///        SAT_Intra.update(4).reorder(ryt,rxi,xo,yo).gpu_threads(rxi).unroll(ryt);
///
///        SAT_Tail.compute_root().reorder_storage(xi,yi,xo,yo);
///        SAT_Tail.split(yi,yi,t,UNROLL).reorder(t,xi,yi,xo,yo).gpu(xo,yo,xi,yi).unroll(t);
///
///        SAT_Tail.compile_to_simplified_lowered_stmt("stmt2.html", 2, width/tile, tile, width/tile, HTML, get_jit_target_from_environment());
///
///        //
///
///        SAT_CTail_x.compute_root().reorder_storage(yi,xi,yo,xo);
///        SAT_CTail_x.update().reorder(rxox,rxoy,yi,yo).fuse(yi,yo,y).gpu_tile(y,MAX_THREADS);
///
///        //
///
///        SAT_CTail_xy_sub.compute_at(SAT_CTail_xy, Var::gpu_blocks());
///        SAT_CTail_xy_sub.split(xo,xo,x,TILES_PER_WARP).reorder(yi,xi,x,xo,yo).gpu_threads(x,yi);
///        SAT_CTail_xy_sub.update().split(xo,xo,x,TILES_PER_WARP).reorder(ryi,rxt,x,xo,yo).gpu_threads(x).unroll(ryi);
///
///        SAT_CTail_xy.compute_root().reorder_storage(yi,xi,yo,xo);
///        SAT_CTail_xy.split(xo,xo,x,TILES_PER_WARP).reorder(yi,xi,x,xo,yo).gpu_threads(x,yi).gpu_blocks(xo,yo);
///
///
///        SAT_CTail_y.compute_root().reorder_storage(xi,yi,xo,yo);
///        SAT_CTail_y.update().reorder(ryox,ryoy,xi,xo).fuse(xi,xo,x).gpu_tile(x,MAX_THREADS);
///
///        //
///
///        SAT_Deps_x.compute_at(SAT, Var::gpu_blocks()).reorder(xi,yi,xo,yo).gpu_threads(yi);
///        SAT_Deps_y.compute_at(SAT, Var::gpu_blocks()).reorder(xi,yi,xo,yo).gpu_threads(xi);
///
///        SAT_Final.compute_at(SAT, Var::gpu_blocks());
///        SAT_Final.update(0).split(ryi,yi,t,UNROLL).reorder(t,rxi,yi,xo,yo).gpu_threads(rxi,yi).unroll(t);
///        SAT_Final.update(1).reorder(rxt,ryi,xo,yo).gpu_threads(ryi).unroll(rxt);
///        SAT_Final.update(2).reorder(rxf,ryi,xo,yo).gpu_threads(ryi).unroll(rxf);
///        SAT_Final.update(3).reorder(ryt,rxi,xo,yo).gpu_threads(rxi).unroll(ryt);
///        SAT_Final.update(4).reorder(ryf,rxi,xo,yo).gpu_threads(rxi).unroll(ryf);
///
///        SAT.compute_root();
///        SAT.split(x,xo,xi,tile).split(y,yo,yi,tile);
///        SAT.split(yi,yi,t,UNROLL).reorder(t,xi,yi,xo,yo).gpu(xo,yo,xi,yi).unroll(t);
///
///        SAT.bound(x,0,width).bound(y,0,height);
///    }

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

    // innermost: unrolled dimensions, then gpu_thread dimensions, then gpu_block dimensions

    int unroll_w = 8;
    int tiles_per_warp = 4;
    int vectorize = 4;

    F.intra_schedule(1)
        .compute_locally()
        .reorder_storage(F.inner(), F.outer())
        .unroll         (F.inner_scan())
        .split          (F.inner(1), unroll_w)
        .unroll         (F.inner(1).split_var())
        .reorder        (F.inner_scan(), F.inner(1).split_var(), F.inner(), F.outer())
        .gpu_threads    (F.inner(0), F.inner(1))
        .gpu_blocks     (F.outer(0), F.outer(1));

    F.intra_schedule(2)
        .compute_locally()
        .reorder_storage(F.inner(), F.tail(), F.outer())
        .unroll         (F.inner_scan())
        .split          (F.outer(0), tiles_per_warp)
        .reorder        (F.inner_scan(), F.inner(), F.outer(0).split_var(), F.outer())
        .gpu_threads    (F.outer(0).split_var(), F.inner(0))
        .gpu_blocks     (F.outer(0), F.outer(1));

    F.inter_schedule().compute_globally()
        .reorder_storage(F.inner(), F.tail(), F.outer())
        .unroll         (F.outer_scan())
        .split          (F.outer(0), tiles_per_warp)
        .reorder        (F.outer_scan(), F.inner(), F.tail(), F.outer(0).split_var(), F.outer())
        .gpu_threads    (F.inner(0), F.outer(0).split_var())
        .gpu_blocks     (F.outer(0));

    cerr << F << endl;

    cerr << "\nJIT compilation ... " << endl;
    F.compile_jit("stmt.html");

    cerr << "\nRunning ... " << endl;
    Buffer out(type_of<float>(), width, height);
    float time = F.realize(out, iter);
    cerr << width << "\t" << time << endl;

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

    if (!nocheck) {
        cerr << "\nChecking difference ... " << endl;
        Image<float> hl_out(out);
        Image<float> ref(width,height);

        for (int y=0; y<height; y++) {
            for (int x=0; x<width; x++) {
                ref(x,y) = random_image(x,y);
            }
        }
        for (int y=0; y<height; y++) {
            for (int x=1; x<width; x++) {
                ref(x,y) += ref(x-1,y);
            }
        }
        for (int y=1; y<height; y++) {
            for (int x=0; x<width; x++) {
                ref(x,y) += ref(x,y-1);
            }
        }

        cout << CheckResult<float>(ref,hl_out) << endl;
    }

    return 0;
}
<|endoftext|>


<commit_before>#include <google/profiler.h>

#include <vector>

#include "thread.h"
#include "io_interface.h"
#include "container.h"
#include "concurrency.h"

#include "vertex_index.h"
#include "graph_engine.h"
#include "graph_config.h"
#include "worker_thread.h"

std::vector<int> thread_delays;

class count_message: public vertex_message
{
	int count;
public:
	count_message(): vertex_message(sizeof(count_message), false) {
		count = 1;
	}

	int get_count() const {
		return count;
	}
};

class test_vertex: public compute_vertex
{
	size_t num_edges;
	size_t add1_count;
public:
	test_vertex() {
		num_edges = 0;
		add1_count = 0;
	}

	test_vertex(vertex_id_t id, const vertex_index *index): compute_vertex(
			id, index) {
		num_edges = 0;
		add1_count = 0;
	}

	void init() {
		add1_count = 0;
	}

	void verify_result() const {
		assert(add1_count == num_edges * 2);
	}

	void run(vertex_program &prog) {
		vertex_id_t id = get_id();
		request_vertices(&id, 1);
	}

	void run(vertex_program &prog, const page_vertex &vertex);

	virtual void run_on_message(vertex_program &, const vertex_message &msg1) {
		const count_message &msg = (const count_message &) msg1;
		add1_count += msg.get_count();
	}
};

void test_vertex::run(vertex_program &prog, const page_vertex &vertex)
{
	worker_thread *t = (worker_thread *) thread::get_curr_thread();
	int worker_id = t->get_worker_id();
	if ((size_t) worker_id < thread_delays.size() / 2) {
		if (thread_delays[worker_id] == 0) {
			int slp_time = random() % 10;
			printf("worker %d sleeps %d seconds\n", worker_id, slp_time);
			sleep(slp_time);
			thread_delays[worker_id] = 1;
		}
	}
	// We need to add the neighbors of the vertex to the queue of
	// the next level.
	page_byte_array::const_iterator<vertex_id_t> end_it
		= vertex.get_neigh_end(BOTH_EDGES);
	num_edges = vertex.get_num_edges(BOTH_EDGES);
	stack_array<vertex_id_t, 1024> dest_buf(num_edges);
	int num_dests = 0;
	for (page_byte_array::const_iterator<vertex_id_t> it
			= vertex.get_neigh_begin(BOTH_EDGES); it != end_it; ++it) {
		vertex_id_t id = *it;
		dest_buf[num_dests++] = id;
		add1_count++;
	}
	count_message msg;
	prog.multicast_msg(dest_buf.data(), num_dests, msg);
}

int main(int argc, char *argv[])
{
	if (argc < 4) {
		fprintf(stderr, "test conf_file graph_file index_file");
		exit(-1);
	}

	std::string conf_file = argv[1];
	std::string graph_file = argv[2];
	std::string index_file = argv[3];

	config_map configs(conf_file);
	graph_conf.init(configs);
	graph_conf.print();

	init_io_system(configs);

	graph_index *index = NUMA_graph_index<test_vertex>::create(index_file,
			graph_conf.get_num_threads(), params.get_num_nodes());

	graph_engine *graph = graph_engine::create(graph_conf.get_num_threads(),
			params.get_num_nodes(), graph_file, index);
	printf("prof_file: %s\n", graph_conf.get_prof_file().c_str());
	if (!graph_conf.get_prof_file().empty())
		ProfilerStart(graph_conf.get_prof_file().c_str());
	thread_delays.resize(graph->get_num_threads());

	for (int i = 0; i < 1000; i++) {
		printf("test %d\n", i);
		struct timeval start, end;
		gettimeofday(&start, NULL);
		graph->start_all();
		graph->wait4complete();
		gettimeofday(&end, NULL);

		NUMA_graph_index<test_vertex>::const_iterator it
			= ((NUMA_graph_index<test_vertex> *) index)->begin();
		NUMA_graph_index<test_vertex>::const_iterator end_it
			= ((NUMA_graph_index<test_vertex> *) index)->end();
		for (; it != end_it; ++it) {
			const test_vertex &v = (const test_vertex &) *it;
			v.verify_result();
		}
		printf("It takes %f seconds\n", time_diff(start, end));
	}

	if (!graph_conf.get_prof_file().empty())
		ProfilerStop();
	if (graph_conf.get_print_io_stat())
		print_io_thread_stat();
	graph_engine::destroy(graph);
	destroy_io_system();
}
<commit_msg>[Graph]: use vertex_query for verify test_load_balancer.<commit_after>#include <google/profiler.h>

#include <vector>

#include "thread.h"
#include "io_interface.h"
#include "container.h"
#include "concurrency.h"

#include "vertex_index.h"
#include "graph_engine.h"
#include "graph_config.h"
#include "worker_thread.h"

std::vector<int> thread_delays;

class count_message: public vertex_message
{
	int count;
public:
	count_message(): vertex_message(sizeof(count_message), false) {
		count = 1;
	}

	int get_count() const {
		return count;
	}
};

class test_vertex: public compute_vertex
{
	size_t num_edges;
	size_t add1_count;
public:
	test_vertex() {
		num_edges = 0;
		add1_count = 0;
	}

	test_vertex(vertex_id_t id, const vertex_index *index): compute_vertex(
			id, index) {
		num_edges = 0;
		add1_count = 0;
	}

	void init() {
		add1_count = 0;
	}

	void verify_result() const {
		assert(add1_count == num_edges * 2);
	}

	void run(vertex_program &prog) {
		vertex_id_t id = get_id();
		request_vertices(&id, 1);
	}

	void run(vertex_program &prog, const page_vertex &vertex);

	virtual void run_on_message(vertex_program &, const vertex_message &msg1) {
		const count_message &msg = (const count_message &) msg1;
		add1_count += msg.get_count();
	}
};

void test_vertex::run(vertex_program &prog, const page_vertex &vertex)
{
	worker_thread *t = (worker_thread *) thread::get_curr_thread();
	int worker_id = t->get_worker_id();
	if ((size_t) worker_id < thread_delays.size() / 2) {
		if (thread_delays[worker_id] == 0) {
			int slp_time = random() % 10;
			printf("worker %d sleeps %d seconds\n", worker_id, slp_time);
			sleep(slp_time);
			thread_delays[worker_id] = 1;
		}
	}
	// We need to add the neighbors of the vertex to the queue of
	// the next level.
	page_byte_array::const_iterator<vertex_id_t> end_it
		= vertex.get_neigh_end(BOTH_EDGES);
	num_edges = vertex.get_num_edges(BOTH_EDGES);
	stack_array<vertex_id_t, 1024> dest_buf(num_edges);
	int num_dests = 0;
	for (page_byte_array::const_iterator<vertex_id_t> it
			= vertex.get_neigh_begin(BOTH_EDGES); it != end_it; ++it) {
		vertex_id_t id = *it;
		dest_buf[num_dests++] = id;
		add1_count++;
	}
	count_message msg;
	prog.multicast_msg(dest_buf.data(), num_dests, msg);
}

class verify_vertex_query: public vertex_query
{
public:
	virtual void run(graph_engine &graph, compute_vertex &v) {
		const test_vertex &test_v = (const test_vertex &) v;
		test_v.verify_result();
	}

	virtual void merge(graph_engine &graph, vertex_query::ptr q) {
	}

	virtual ptr clone() {
		return vertex_query::ptr(new verify_vertex_query());
	}
};

int main(int argc, char *argv[])
{
	if (argc < 4) {
		fprintf(stderr, "test conf_file graph_file index_file");
		exit(-1);
	}

	std::string conf_file = argv[1];
	std::string graph_file = argv[2];
	std::string index_file = argv[3];

	config_map configs(conf_file);
	graph_conf.init(configs);
	graph_conf.print();

	init_io_system(configs);

	graph_index *index = NUMA_graph_index<test_vertex>::create(index_file,
			graph_conf.get_num_threads(), params.get_num_nodes());

	graph_engine *graph = graph_engine::create(graph_conf.get_num_threads(),
			params.get_num_nodes(), graph_file, index);
	printf("prof_file: %s\n", graph_conf.get_prof_file().c_str());
	if (!graph_conf.get_prof_file().empty())
		ProfilerStart(graph_conf.get_prof_file().c_str());
	thread_delays.resize(graph->get_num_threads());

	for (int i = 0; i < 1000; i++) {
		printf("test %d\n", i);
		struct timeval start, end;
		gettimeofday(&start, NULL);
		graph->start_all();
		graph->wait4complete();
		gettimeofday(&end, NULL);
		printf("It takes %f seconds\n", time_diff(start, end));

		vertex_query::ptr vvq(new verify_vertex_query());
		graph->query_on_all(vvq);
	}

	if (!graph_conf.get_prof_file().empty())
		ProfilerStop();
	if (graph_conf.get_print_io_stat())
		print_io_thread_stat();
	graph_engine::destroy(graph);
	destroy_io_system();
}
<|endoftext|>


<commit_before>// csabbg_funcalpha.cpp                                               -*-C++-*-
// ----------------------------------------------------------------------------

#include <csabase_analyser.h>
#include <csabase_debug.h>
#include <csabase_location.h>
#include <csabase_ppobserver.h>
#include <csabase_registercheck.h>
#include <csabase_util.h>
#include <csabase_visitor.h>
#include <llvm/Support/Regex.h>
#include <clang/AST/TypeLoc.h>
#include <map>
#include <string>
#include <sstream>
#include <vector>
#include <ctype.h>

#ident "$Id$"

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

static std::string const check_name("alphabetical-functions");

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

using clang::Decl;
using clang::DeclarationName;
using clang::FunctionDecl;
using clang::FunctionTemplateDecl;
using clang::FunctionTypeLoc;
using clang::SourceManager;
using clang::SourceRange;
using clang::TypeLoc;
using bde_verify::csabase::Analyser;
using bde_verify::csabase::Location;
using bde_verify::csabase::PPObserver;
using bde_verify::csabase::Visitor;

namespace
{

struct data
    // Data attached to analyzer for this check.
{
    typedef std::set<unsigned> Lines;
    typedef std::map<std::string, Lines> Comments;
    Comments d_comments;  // Comment lines per file.

    typedef std::set<const FunctionDecl *> Functions;
    Functions d_functions;
};

struct comments
    // Callback object for inspecting comments.
{
    Analyser& d_analyser;         // Analyser object.
    SourceManager& d_manager;     // SourceManager within Analyser.
    data::Comments& d_comments;   // Analyser's comment data.

    comments(Analyser& analyser);
        // Create a 'comments' object, accessing the specified 'analyser'.

    static bool isReset(llvm::StringRef comment);
        // Return wehether the specified 'comment' acts as a "reset" marker for
        // alphabetical ordering.

    void operator()(SourceRange range);
        // The specified 'range', representing a comment, is added to the
        // comments list.
};

comments::comments(Analyser& analyser)
: d_analyser(analyser)
, d_manager(analyser.manager())
, d_comments(analyser.attachment<data>().d_comments)
{
}

bool comments::isReset(llvm::StringRef comment)
{
    // Look for "aspect" directives in comments.
    static llvm::Regex re("^(//|/[*])" "[[:space:]]*"
                          "("
                             "CLASS"     "|"
                             "FREE"      "|"
                             "NOT"       "|"
                             "PRIVATE"   "|"
                             "PROTECTED" "|"
                             "PUBLIC"    "|"
                             "."         "|"  // Try allowing everything here.
                             "[[:space:]]"
                          ")*"
                          "("
                             "ACCESSORS?"    "|"
                             "Aspects?"      "|"
                             "CREATORS?"     "|"
                             "DATA"          "|"
                             "FUNCTIONS?"    "|"
                             "IMPLEMENTED"   "|"
                             "MANIPULATORS?" "|"
                             "METHODS?"      "|"
                             "OPERATORS?"    "|"
                             "TRAITS?"       "|"
                             "[-=_]+" "(" "[[:space:]]*" "[-=_]+" ")*"
                          ")"
                          "[:;.[:space:]]*" "([*]/)?" "[[:space:]]*" "$",
                          llvm::Regex::IgnoreCase);
    std::string error;
    return re.match(comment);
}

void comments::operator()(SourceRange range)
{
    Location location(d_analyser.get_location(range.getBegin()));
    if (   d_analyser.is_component(location.file())
        && isReset(d_analyser.get_source(range))) {
        d_comments[location.file()].insert(location.line());
    }
}

struct report
    // Callback object invoked upon completion.
{
    Analyser& d_analyser;       // Analyser object.
    SourceManager& d_manager;   // SourceManager within Analyser.
    data& d;                    // Analyser's data for this module.

    report(Analyser& analyser);
        // Create a 'report' object, accessing the specified 'analyser'.

    void operator()();
        // Invoked at end;

    void operator()(const FunctionDecl *decl);
        // Invoked to process function declarations.

    void operator()(const FunctionTemplateDecl *decl);
        // Invoked to process function template declarations.

    void check_order(const FunctionDecl *decl);
        // Check if function is in alphanumeric order.
};

report::report(Analyser& analyser)
: d_analyser(analyser)
, d_manager(analyser.manager())
, d(analyser.attachment<data>())
{
}

void report::operator()()
{
    data::Functions::iterator b = d.d_functions.begin();
    data::Functions::iterator e = d.d_functions.end();
    for (data::Functions::iterator i = b; i != e; ++i) {
        check_order(*i);
    }
}

void report::operator()(const FunctionTemplateDecl *decl)
{
    (*this)(decl->getTemplatedDecl());
}

void report::operator()(const FunctionDecl *decl)
{
    d.d_functions.insert(decl);
}

void report::check_order(const FunctionDecl *decl)
{
    DeclarationName name = decl->getDeclName();
    const Decl *next = decl->getNextDeclInContext();

    if (   d_analyser.is_component(decl)
        && !decl->isTemplateInstantiation()
        && next
        && name.isIdentifier()
        && !name.isEmpty()) {
        const FunctionDecl *nextf = llvm::dyn_cast<FunctionDecl>(next);
        const FunctionTemplateDecl* nextt =
            llvm::dyn_cast<FunctionTemplateDecl>(next);
        if (nextt) {
            nextf = nextt->getTemplatedDecl();
        }
        if (nextf && !nextf->isTemplateInstantiation()) {
            DeclarationName next_name = nextf->getDeclName();
            if (next_name.isIdentifier() &&
                !next_name.isEmpty() &&
                llvm::StringRef(next_name.getAsString())
                        .compare_numeric(name.getAsString()) < 0) {
                std::string q1 = decl->getQualifiedNameAsString();
                std::string q2 = nextf->getQualifiedNameAsString();
                q1 = q1.substr(0, q1.rfind(':'));
                q2 = q2.substr(0, q2.rfind(':'));
                bool reset = q1 != q2;
                if (!reset) {
                    Location l1 = d_analyser.get_location(decl);
                    Location l2 = d_analyser.get_location(nextf);
                    const data::Lines &lines = d.d_comments[l1.file()];
                    for (unsigned i = l1.line(); i <= l2.line(); ++i) {
                        if ((reset = lines.count(i)) == true) {
                            break;
                        }
                    }
                }
                if (!reset && q1 == q2) {
                    d_analyser.report(
                            decl->getLocation(),
                            check_name, "FABC01",
                            "Function '%0' not in alphanumeric order")
                        << name.getAsString()
                        << decl->getNameInfo().getSourceRange();
                    d_analyser.report(nextf->getLocation(),
                                      check_name, "FABC01",
                                      "Next function is '%0'",
                                      false, clang::DiagnosticsEngine::Note)
                        << next_name.getAsString()
                        << nextf->getNameInfo().getSourceRange();
                }
            }
        }
    }
}

void subscribe(Analyser& analyser, Visitor& visitor, PPObserver& observer)
    // Hook up the callback functions.
{
    observer.onComment             += comments(analyser);
    visitor.onFunctionDecl         += report(analyser);
    visitor.onFunctionTemplateDecl += report(analyser);
    analyser.onTranslationUnitDone += report(analyser);
}

}  // close anonymous namespace

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

static bde_verify::csabase::RegisterCheck c3(check_name, &subscribe);
<commit_msg>Fix error whereby misordered function templates were not detected.<commit_after>// csabbg_funcalpha.cpp                                               -*-C++-*-
// ----------------------------------------------------------------------------

#include <csabase_analyser.h>
#include <csabase_debug.h>
#include <csabase_location.h>
#include <csabase_ppobserver.h>
#include <csabase_registercheck.h>
#include <csabase_util.h>
#include <csabase_visitor.h>
#include <llvm/Support/Regex.h>
#include <clang/AST/TypeLoc.h>
#include <map>
#include <string>
#include <sstream>
#include <vector>
#include <ctype.h>

#ident "$Id$"

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

static std::string const check_name("alphabetical-functions");

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

using clang::Decl;
using clang::DeclarationName;
using clang::FunctionDecl;
using clang::FunctionTemplateDecl;
using clang::FunctionTypeLoc;
using clang::SourceManager;
using clang::SourceRange;
using clang::TypeLoc;
using bde_verify::csabase::Analyser;
using bde_verify::csabase::Location;
using bde_verify::csabase::PPObserver;
using bde_verify::csabase::Visitor;

namespace
{

struct data
    // Data attached to analyzer for this check.
{
    typedef std::set<unsigned> Lines;
    typedef std::map<std::string, Lines> Comments;
    Comments d_comments;  // Comment lines per file.

    typedef std::set<std::pair<const FunctionDecl *, const Decl *> > Functions;
    Functions d_functions;
};

struct comments
    // Callback object for inspecting comments.
{
    Analyser& d_analyser;         // Analyser object.
    SourceManager& d_manager;     // SourceManager within Analyser.
    data::Comments& d_comments;   // Analyser's comment data.

    comments(Analyser& analyser);
        // Create a 'comments' object, accessing the specified 'analyser'.

    static bool isReset(llvm::StringRef comment);
        // Return wehether the specified 'comment' acts as a "reset" marker for
        // alphabetical ordering.

    void operator()(SourceRange range);
        // The specified 'range', representing a comment, is added to the
        // comments list.
};

comments::comments(Analyser& analyser)
: d_analyser(analyser)
, d_manager(analyser.manager())
, d_comments(analyser.attachment<data>().d_comments)
{
}

bool comments::isReset(llvm::StringRef comment)
{
    // Look for "aspect" directives in comments.
    static llvm::Regex re("^(//|/[*])" "[[:space:]]*"
                          "("
                             "CLASS"     "|"
                             "FREE"      "|"
                             "NOT"       "|"
                             "PRIVATE"   "|"
                             "PROTECTED" "|"
                             "PUBLIC"    "|"
                             "."         "|"  // Try allowing everything here.
                             "[[:space:]]"
                          ")*"
                          "("
                             "ACCESSORS?"    "|"
                             "Aspects?"      "|"
                             "CREATORS?"     "|"
                             "DATA"          "|"
                             "FUNCTIONS?"    "|"
                             "IMPLEMENTED"   "|"
                             "MANIPULATORS?" "|"
                             "METHODS?"      "|"
                             "OPERATORS?"    "|"
                             "TRAITS?"       "|"
                             "[-=_]+" "(" "[[:space:]]*" "[-=_]+" ")*"
                          ")"
                          "[:;.[:space:]]*" "([*]/)?" "[[:space:]]*" "$",
                          llvm::Regex::IgnoreCase);
    std::string error;
    return re.match(comment);
}

void comments::operator()(SourceRange range)
{
    Location location(d_analyser.get_location(range.getBegin()));
    if (   d_analyser.is_component(location.file())
        && isReset(d_analyser.get_source(range))) {
        d_comments[location.file()].insert(location.line());
    }
}

struct report
    // Callback object invoked upon completion.
{
    Analyser& d_analyser;       // Analyser object.
    SourceManager& d_manager;   // SourceManager within Analyser.
    data& d;                    // Analyser's data for this module.

    report(Analyser& analyser);
        // Create a 'report' object, accessing the specified 'analyser'.

    void operator()();
        // Invoked at end;

    void operator()(const FunctionDecl *decl);
        // Invoked to process function declarations.

    void operator()(const FunctionTemplateDecl *decl);
        // Invoked to process function template declarations.

    void check_order(std::pair<const FunctionDecl *, const Decl *> p);
        // Check if function is in alphanumeric order.
};

report::report(Analyser& analyser)
: d_analyser(analyser)
, d_manager(analyser.manager())
, d(analyser.attachment<data>())
{
}

void report::operator()()
{
    data::Functions::iterator b = d.d_functions.begin();
    data::Functions::iterator e = d.d_functions.end();
    for (data::Functions::iterator i = b; i != e; ++i) {
        check_order(*i);
    }
}

void report::operator()(const FunctionTemplateDecl *decl)
{
    d.d_functions.insert(std::make_pair(decl->getTemplatedDecl(), decl));
}

void report::operator()(const FunctionDecl *decl)
{
    d.d_functions.insert(std::make_pair(decl, decl));
}

void report::check_order(std::pair<const FunctionDecl *, const Decl *> p)
{
    const FunctionDecl *decl = p.first;
    DeclarationName name = decl->getDeclName();
    const Decl *next = p.second->getNextDeclInContext();

    if (   d_analyser.is_component(decl)
        && !decl->isTemplateInstantiation()
        && next
        && name.isIdentifier()
        && !name.isEmpty()) {
        const FunctionDecl *nextf = llvm::dyn_cast<FunctionDecl>(next);
        const FunctionTemplateDecl* nextt =
            llvm::dyn_cast<FunctionTemplateDecl>(next);
        if (nextt) {
            nextf = nextt->getTemplatedDecl();
        }
        if (nextf && !nextf->isTemplateInstantiation()) {
            DeclarationName next_name = nextf->getDeclName();
            if (next_name.isIdentifier() &&
                !next_name.isEmpty() &&
                llvm::StringRef(next_name.getAsString())
                        .compare_numeric(name.getAsString()) < 0) {
                std::string q1 = decl->getQualifiedNameAsString();
                std::string q2 = nextf->getQualifiedNameAsString();
                q1 = q1.substr(0, q1.rfind(':'));
                q2 = q2.substr(0, q2.rfind(':'));
                bool reset = q1 != q2;
                if (!reset) {
                    Location l1 = d_analyser.get_location(decl);
                    Location l2 = d_analyser.get_location(nextf);
                    const data::Lines &lines = d.d_comments[l1.file()];
                    for (unsigned i = l1.line(); i <= l2.line(); ++i) {
                        if ((reset = lines.count(i)) == true) {
                            break;
                        }
                    }
                }
                if (!reset && q1 == q2) {
                    d_analyser.report(
                            decl->getLocation(),
                            check_name, "FABC01",
                            "Function '%0' not in alphanumeric order")
                        << name.getAsString()
                        << decl->getNameInfo().getSourceRange();
                    d_analyser.report(nextf->getLocation(),
                                      check_name, "FABC01",
                                      "Next function is '%0'",
                                      false, clang::DiagnosticsEngine::Note)
                        << next_name.getAsString()
                        << nextf->getNameInfo().getSourceRange();
                }
            }
        }
    }
}

void subscribe(Analyser& analyser, Visitor& visitor, PPObserver& observer)
    // Hook up the callback functions.
{
    observer.onComment             += comments(analyser);
    visitor.onFunctionDecl         += report(analyser);
    visitor.onFunctionTemplateDecl += report(analyser);
    analyser.onTranslationUnitDone += report(analyser);
}

}  // close anonymous namespace

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

static bde_verify::csabase::RegisterCheck c3(check_name, &subscribe);
<|endoftext|>


<commit_before>/// \file THistPainter.cxx
/// \ingroup HistPainter ROOT7
/// \author Axel Naumann <axel@cern.ch>
/// \date 2015-07-09
/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback is welcome!

/*************************************************************************
 * Copyright (C) 1995-2016, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//#include "ROOT/THistPainter.hxx" see ROOT/THistDrawable.h

#include "ROOT/THistDrawable.hxx"
#include "ROOT/TVirtualCanvasPainter.hxx"
#include "ROOT/TDisplayItem.hxx"
#include "TH1.h"

#include <iostream>
#include <cassert>

using namespace ROOT::Experimental;
using namespace ROOT::Experimental::Internal;

namespace {
class THistPainter1D: public THistPainterBase<1> {
public:
  void Paint(TDrawable& hist, THistDrawOptions<1> /*opts*/,
             TVirtualCanvasPainter& /*canv*/) final {
    // TODO: paint!
    std::cout << "Painting histogram @" << &hist << '\n';
  }
  virtual ~THistPainter1D() final {}
};

class THistPainter2D: public THistPainterBase<2> {
public:
  void Paint(TDrawable& drw, THistDrawOptions<2> /*opts*/,
             TVirtualCanvasPainter& canv) final {
    std::cout << "Painting histogram @" << &drw << '\n';
    assert(dynamic_cast<THistDrawable<2>*>(&drw) && "Wrong drawable type");
    THistDrawable<2>& hd = static_cast<THistDrawable<2>&>(drw);

    ROOT::Experimental::TDisplayItem *res = new TOrdinaryDisplayItem<TH1>(hd.GetOldHist());
    res->SetOption("col");

    canv.AddDisplayItem(res);

    //hd.GetOldHist()->Paint("BOX");
  }
  virtual ~THistPainter2D() final {}
};

class THistPainter3D: public THistPainterBase<3> {
public:
  void Paint(TDrawable& hist, THistDrawOptions<3> /*opts*/,
             TVirtualCanvasPainter& /*canv*/) final {
    // TODO: paint!
    std::cout << "Painting histogram @" << &hist << '\n';
  }
  virtual ~THistPainter3D() final {}
};

struct HistPainterReg {
  THistPainter1D fPainter1D;
  THistPainter2D fPainter2D;
  THistPainter3D fPainter3D;
} histPainterReg;
} // unnamed namespace
<commit_msg>Mostly-clang-format hist/histpainter/v7/.<commit_after>/// \file THistPainter.cxx
/// \ingroup HistPainter ROOT7
/// \author Axel Naumann <axel@cern.ch>
/// \date 2015-07-09
/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
/// is welcome!

/*************************************************************************
 * Copyright (C) 1995-2016, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//#include "ROOT/THistPainter.hxx" see ROOT/THistDrawable.h

#include "ROOT/THistDrawable.hxx"
#include "ROOT/TVirtualCanvasPainter.hxx"
#include "ROOT/TDisplayItem.hxx"
#include "TH1.h"

#include <iostream>
#include <cassert>

using namespace ROOT::Experimental;
using namespace ROOT::Experimental::Internal;

namespace {
class THistPainter1D: public THistPainterBase<1> {
public:
   void Paint(TDrawable &hist, THistDrawOptions<1> /*opts*/, TVirtualCanvasPainter & /*canv*/) final
   {
      // TODO: paint!
      std::cout << "Painting histogram @" << &hist << '\n';
   }
   virtual ~THistPainter1D() final {}
};

class THistPainter2D: public THistPainterBase<2> {
public:
   void Paint(TDrawable &drw, THistDrawOptions<2> /*opts*/, TVirtualCanvasPainter &canv) final
   {
      std::cout << "Painting histogram @" << &drw << '\n';
      assert(dynamic_cast<THistDrawable<2> *>(&drw) && "Wrong drawable type");
      THistDrawable<2> &hd = static_cast<THistDrawable<2> &>(drw);

      ROOT::Experimental::TDisplayItem *res = new TOrdinaryDisplayItem<TH1>(hd.GetOldHist());
      res->SetOption("col");

      canv.AddDisplayItem(res);

      // hd.GetOldHist()->Paint("BOX");
   }
   virtual ~THistPainter2D() final {}
};

class THistPainter3D: public THistPainterBase<3> {
public:
   void Paint(TDrawable &hist, THistDrawOptions<3> /*opts*/, TVirtualCanvasPainter & /*canv*/) final
   {
      // TODO: paint!
      std::cout << "Painting histogram @" << &hist << '\n';
   }
   virtual ~THistPainter3D() final {}
};

struct HistPainterReg {
   THistPainter1D fPainter1D;
   THistPainter2D fPainter2D;
   THistPainter3D fPainter3D;
} histPainterReg;
} // unnamed namespace
<|endoftext|>


<commit_before>#include <gtest/gtest.h>

#include <ros/ros.h>
#include <igvc_utils/NodeUtils.hpp>

class TestAssertions : public testing::Test
{
public:
  TestAssertions() : handle_{} {};

protected:
  virtual void SetUp()
  {
    ros::start();
    handle_.deleteParam(parameter1);
    handle_.deleteParam(parameter2);
    handle_.deleteParam(parameter3);
  }

  virtual void TearDown()
  {
    if (ros::ok())
    {
      ros::shutdown();
    }
  }

  const std::string parameter1 = "first_param";
  const std::string parameter2 = "second_param";
  const std::string parameter3 = "third_param";
  ros::NodeHandle handle_;
};

//========== Basic Functionality ==========
TEST_F(TestAssertions, getParamGetsParam)
{
  {
    int param;
    int set_param = 1;
    handle_.setParam(parameter1, set_param);
    igvc::getParam(handle_, parameter1, param);
    EXPECT_EQ(param, set_param);
  }
  {
    std::string param;
    std::string set_param = "Vim>Emacs";
    handle_.setParam(parameter2, set_param);
    igvc::getParam(handle_, parameter2, param);
    EXPECT_STREQ(param.c_str(), set_param.c_str());
  }
  {
    std::vector<double> param;
    std::vector<double> set_param{ 3.0, 1.0, 4.0, 1.0, 5.0 };
    handle_.setParam(parameter3, set_param);
    igvc::getParam(handle_, parameter3, param);
    EXPECT_EQ(param.size(), set_param.size());
    for (size_t i = 0; i < set_param.size(); i++)
    {
      EXPECT_DOUBLE_EQ(param[i], set_param[i]);
    }
  }
}

TEST_F(TestAssertions, paramGetsParam)
{
  {
    int param;
    int set_param = 1;
    handle_.setParam(parameter1, set_param);
    igvc::param(handle_, parameter1, param, 0);
    EXPECT_EQ(param, set_param);
  }
  {
    std::string param;
    std::string set_param = "Vim>Emacs";
    handle_.setParam(parameter2, set_param);
    igvc::param(handle_, parameter2, param, std::string{ "" });
    EXPECT_STREQ(param.c_str(), set_param.c_str());
  }
  {
    std::vector<double> param;
    std::vector<double> set_param{ 3.0, 1.0, 4.0, 1.0, 5.0 };
    handle_.setParam(parameter3, set_param);
    igvc::param(handle_, parameter3, param, std::vector<double>{});
    EXPECT_EQ(param.size(), set_param.size());
    for (size_t i = 0; i < set_param.size(); i++)
    {
      EXPECT_DOUBLE_EQ(param[i], set_param[i]);
    }
  }
}

//========== Guarantees that parameter is set ==========
TEST_F(TestAssertions, getParamEnsuresParamIsSet)
{
  {
    int param;
    igvc::getParam(handle_, parameter1, param);
    EXPECT_TRUE(ros::isShuttingDown());
  }
}

TEST_F(TestAssertions, paramUsesDefaultValue)
{
  {
    int param;
    int set_param = 1;
    igvc::param(handle_, parameter1, param, set_param);
    EXPECT_EQ(param, set_param);
    EXPECT_FALSE(ros::isShuttingDown());
  }
  {
    std::string param;
    std::string set_param = "Vim>Emacs";
    igvc::param(handle_, parameter2, param, set_param);
    EXPECT_STREQ(param.c_str(), set_param.c_str());
    EXPECT_FALSE(ros::isShuttingDown());
  }
  {
    std::vector<double> param;
    std::vector<double> set_param{ 3.0, 1.0, 4.0, 1.0, 5.0 };
    igvc::param(handle_, parameter3, param, set_param);
    EXPECT_EQ(param.size(), set_param.size());
    for (size_t i = 0; i < set_param.size(); i++)
    {
      EXPECT_DOUBLE_EQ(param[i], set_param[i]);
    }
    EXPECT_FALSE(ros::isShuttingDown());
  }
}

//========== Assertions ==========
TEST_F(TestAssertions, getParamCanAssertTrueUsingIGVCAssertion)
{
  {
    int param;
    int set_param = 1;
    handle_.setParam(parameter1, set_param);
    igvc::getParam(handle_, parameter1, param, igvc::Assertion::POSITIVE);
    EXPECT_FALSE(ros::isShuttingDown());
  }
  {
    int param;
    int set_param = -1;
    handle_.setParam(parameter1, set_param);
    igvc::getParam(handle_, parameter1, param, igvc::Assertion::NEGATIVE);
    EXPECT_FALSE(ros::isShuttingDown());
  }
  {
    std::vector<double> param;
    std::vector<double> set_param{ 3.0, 1.0, 4.0, 1.0, 5.0 };
    handle_.setParam(parameter3, set_param);
    igvc::getParam(handle_, parameter3, param, igvc::Assertion::POSITIVE);
    EXPECT_EQ(param.size(), set_param.size());
    for (size_t i = 0; i < set_param.size(); i++)
    {
      EXPECT_DOUBLE_EQ(param[i], set_param[i]);
    }
    EXPECT_FALSE(ros::isShuttingDown());
  }
}

TEST_F(TestAssertions, getParamCanAssertFalseUsingIGVCAssertionNegativeInt)
{
  int param;
  int set_param = 1;
  handle_.setParam(parameter1, set_param);
  igvc::getParam(handle_, parameter1, param, igvc::Assertion::NEGATIVE);
  EXPECT_TRUE(ros::isShuttingDown());
}
TEST_F(TestAssertions, getParamCanAssertFalseUsingIGVCAssertionPositiveInt)
{
  int param;
  int set_param = -1;
  handle_.setParam(parameter1, set_param);
  igvc::getParam(handle_, parameter1, param, igvc::Assertion::POSITIVE);
  EXPECT_TRUE(ros::isShuttingDown());
}
TEST_F(TestAssertions, getParamCanAssertFalseUsingIGVCAssertionNegativeVector)
{
  std::vector<double> param;
  std::vector<double> set_param{ -3.0, -1.0, -4.0, 1.0, -5.0 };
  handle_.setParam(parameter3, set_param);
  igvc::getParam(handle_, parameter3, param, igvc::Assertion::NEGATIVE);
  EXPECT_TRUE(ros::isShuttingDown());
}
TEST_F(TestAssertions, getParamCanAssertFalseUsingIGVCAssertionPostitiveVector)
{
  std::vector<double> param;
  std::vector<double> set_param{ 3.0, -1.0, 4.0, 1.0, 5.0 };
  handle_.setParam(parameter3, set_param);
  igvc::getParam(handle_, parameter3, param, igvc::Assertion::POSITIVE);
  EXPECT_TRUE(ros::isShuttingDown());
}
// ========== getParam lambdas ==========
TEST_F(TestAssertions, getParamCanAssertTrueUsingLambda)
{
  int param;
  int set_param = -1;
  handle_.setParam(parameter1, set_param);
  igvc::getParam(handle_, parameter1, param, [](const int& x){ return x < -0.5; });
  EXPECT_EQ(param, set_param);
  EXPECT_FALSE(ros::isShuttingDown());
}
TEST_F(TestAssertions, getParamCanAssertFalseUsingLambda)
{
  std::string param;
  std::string set_param = "The cake";
  handle_.setParam(parameter1, set_param);
  igvc::getParam(handle_, parameter1, param, [](const std::string& x){ return x == "is a lie"; });
  EXPECT_TRUE(ros::isShuttingDown());
}
TEST_F(TestAssertions, getParamCanAssertTrueUsingLambdaVector)
{
  std::vector<double> param;
  std::vector<double> set_param{ 0.3, -0.1, 0.4, 0.1, 0.5 };
  handle_.setParam(parameter3, set_param);
  igvc::getParam(handle_, parameter3, param, [](const double& x){ return -1 <= x && x <= 1;});
  EXPECT_EQ(param.size(), set_param.size());
  for (size_t i = 0; i < set_param.size(); i++)
  {
    EXPECT_DOUBLE_EQ(param[i], set_param[i]);
  }
  EXPECT_FALSE(ros::isShuttingDown());
}
TEST_F(TestAssertions, getParamCanAssertFalseUsingLambdaVector)
{
  std::vector<double> param;
  std::vector<double> set_param{ 0.3, -1.1, 0.4, 0.1, 0.5 };
  handle_.setParam(parameter3, set_param);
  igvc::getParam(handle_, parameter3, param, [](const double& x){ return -1 <= x && x <= 1;});
  EXPECT_TRUE(ros::isShuttingDown());
}

// ========== param ==========
TEST_F(TestAssertions, paramCanAssertTrueUsingIGVCAssertion)
{
  {
    int param;
    int set_param = 1;
    handle_.setParam(parameter1, set_param);
    igvc::param(handle_, parameter1, param, -1, igvc::Assertion::POSITIVE);
    EXPECT_FALSE(ros::isShuttingDown());
  }
  {
    int param;
    int set_param = -1;
    handle_.setParam(parameter1, set_param);
    igvc::param(handle_, parameter1, param, 1, igvc::Assertion::NEGATIVE);
    EXPECT_FALSE(ros::isShuttingDown());
  }
  {
    std::vector<double> param;
    std::vector<double> set_param{ 3.0, 1.0, 4.0, 1.0, 5.0 };
    handle_.setParam(parameter3, set_param);
    igvc::param(handle_, parameter3, param, std::vector<double>{}, igvc::Assertion::POSITIVE);
    EXPECT_EQ(param.size(), set_param.size());
    for (size_t i = 0; i < set_param.size(); i++)
    {
      EXPECT_DOUBLE_EQ(param[i], set_param[i]);
    }
    EXPECT_FALSE(ros::isShuttingDown());
  }
}

TEST_F(TestAssertions, paramCanAssertTrueUsingIGVCAssertionDefault)
{
  {
    int param;
    int set_param = 1;
    igvc::param(handle_, parameter1, param, set_param, igvc::Assertion::POSITIVE);
    EXPECT_FALSE(ros::isShuttingDown());
  }
  {
    int param;
    int set_param = -1;
    igvc::param(handle_, parameter1, param, set_param, igvc::Assertion::NEGATIVE);
    EXPECT_FALSE(ros::isShuttingDown());
  }
  {
    std::vector<double> param;
    std::vector<double> set_param{ 3.0, 1.0, 4.0, 1.0, 5.0 };
    igvc::param(handle_, parameter3, param, set_param, igvc::Assertion::POSITIVE);
    EXPECT_EQ(param.size(), set_param.size());
    for (size_t i = 0; i < set_param.size(); i++)
    {
      EXPECT_DOUBLE_EQ(param[i], set_param[i]);
    }
    EXPECT_FALSE(ros::isShuttingDown());
  }
}

TEST_F(TestAssertions, paramCanAssertFalseUsingIGVCAssertionNegativeInt)
{
  int param;
  int set_param = 1;
  handle_.setParam(parameter1, set_param);
  igvc::param(handle_, parameter1, param, 2, igvc::Assertion::NEGATIVE);
  EXPECT_TRUE(ros::isShuttingDown());
}

// =========== param lambdas ===========

TEST_F(TestAssertions, paramCanAssertTrueUsingLambda)
{
  double param;
  double set_param = 1.5;
  handle_.setParam(parameter1, set_param);
  igvc::param(handle_, parameter1, param, 3, [](const double& x){ return x <= 2; });
  EXPECT_DOUBLE_EQ(param, set_param);
  EXPECT_FALSE(ros::isShuttingDown());
}
TEST_F(TestAssertions, paramCanAssertFalseUsingLambda)
{
  double param;
  double set_param = 3;
  handle_.setParam(parameter1, set_param);
  igvc::param(handle_, parameter1, param, 4, [](const double& x){ return x <= 2; });
  EXPECT_TRUE(ros::isShuttingDown());
}
TEST_F(TestAssertions, paramCanAssertTrueUsingLambdaDefault)
{
  double param;
  igvc::param(handle_, parameter1, param, 0, [](const double& x){ return x <= 2; });
  EXPECT_DOUBLE_EQ(param, param);
  EXPECT_FALSE(ros::isShuttingDown());
}
TEST_F(TestAssertions, paramCanAssertTrueUsingLambdaDefaultVector)
{
  std::vector<double> param;
  std::vector<double> set_param{ 0.3, -0.1, 0.4, 0.1, 0.5 };
  igvc::param(handle_, parameter3, param, set_param, [](const double& x){ return -1 <= x && x <= 1;});
  EXPECT_EQ(param.size(), set_param.size());
  for (size_t i = 0; i < set_param.size(); i++)
  {
    EXPECT_DOUBLE_EQ(param[i], set_param[i]);
  }
  EXPECT_FALSE(ros::isShuttingDown());
}

int main(int argc, char** argv)
{
  ros::init(argc, argv, "test_assertions");
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
<commit_msg>added one more test case, clang-format<commit_after>#include <gtest/gtest.h>

#include <ros/ros.h>
#include <igvc_utils/NodeUtils.hpp>

class TestAssertions : public testing::Test
{
public:
  TestAssertions() : handle_{} {};

protected:
  virtual void SetUp()
  {
    ros::start();
    handle_.deleteParam(parameter1);
    handle_.deleteParam(parameter2);
    handle_.deleteParam(parameter3);
  }

  virtual void TearDown()
  {
    if (ros::ok())
    {
      ros::shutdown();
    }
  }

  const std::string parameter1 = "first_param";
  const std::string parameter2 = "second_param";
  const std::string parameter3 = "third_param";
  ros::NodeHandle handle_;
};

//========== Basic Functionality ==========
TEST_F(TestAssertions, getParamGetsParam)
{
  {
    int param;
    int set_param = 1;
    handle_.setParam(parameter1, set_param);
    igvc::getParam(handle_, parameter1, param);
    EXPECT_EQ(param, set_param);
  }
  {
    std::string param;
    std::string set_param = "Vim>Emacs";
    handle_.setParam(parameter2, set_param);
    igvc::getParam(handle_, parameter2, param);
    EXPECT_STREQ(param.c_str(), set_param.c_str());
  }
  {
    std::vector<double> param;
    std::vector<double> set_param{ 3.0, 1.0, 4.0, 1.0, 5.0 };
    handle_.setParam(parameter3, set_param);
    igvc::getParam(handle_, parameter3, param);
    EXPECT_EQ(param.size(), set_param.size());
    for (size_t i = 0; i < set_param.size(); i++)
    {
      EXPECT_DOUBLE_EQ(param[i], set_param[i]);
    }
  }
}

TEST_F(TestAssertions, paramGetsParam)
{
  {
    int param;
    int set_param = 1;
    handle_.setParam(parameter1, set_param);
    igvc::param(handle_, parameter1, param, 0);
    EXPECT_EQ(param, set_param);
  }
  {
    std::string param;
    std::string set_param = "Vim>Emacs";
    handle_.setParam(parameter2, set_param);
    igvc::param(handle_, parameter2, param, std::string{ "" });
    EXPECT_STREQ(param.c_str(), set_param.c_str());
  }
  {
    std::vector<double> param;
    std::vector<double> set_param{ 3.0, 1.0, 4.0, 1.0, 5.0 };
    handle_.setParam(parameter3, set_param);
    igvc::param(handle_, parameter3, param, std::vector<double>{});
    EXPECT_EQ(param.size(), set_param.size());
    for (size_t i = 0; i < set_param.size(); i++)
    {
      EXPECT_DOUBLE_EQ(param[i], set_param[i]);
    }
  }
}

//========== Guarantees that parameter is set ==========
TEST_F(TestAssertions, getParamEnsuresParamIsSet)
{
  {
    int param;
    igvc::getParam(handle_, parameter1, param);
    EXPECT_TRUE(ros::isShuttingDown());
  }
}

TEST_F(TestAssertions, paramUsesDefaultValue)
{
  {
    int param;
    int set_param = 1;
    igvc::param(handle_, parameter1, param, set_param);
    EXPECT_EQ(param, set_param);
    EXPECT_FALSE(ros::isShuttingDown());
  }
  {
    std::string param;
    std::string set_param = "Vim>Emacs";
    igvc::param(handle_, parameter2, param, set_param);
    EXPECT_STREQ(param.c_str(), set_param.c_str());
    EXPECT_FALSE(ros::isShuttingDown());
  }
  {
    std::vector<double> param;
    std::vector<double> set_param{ 3.0, 1.0, 4.0, 1.0, 5.0 };
    igvc::param(handle_, parameter3, param, set_param);
    EXPECT_EQ(param.size(), set_param.size());
    for (size_t i = 0; i < set_param.size(); i++)
    {
      EXPECT_DOUBLE_EQ(param[i], set_param[i]);
    }
    EXPECT_FALSE(ros::isShuttingDown());
  }
}

//========== Assertions ==========
TEST_F(TestAssertions, getParamCanAssertTrueUsingIGVCAssertion)
{
  {
    int param;
    int set_param = 1;
    handle_.setParam(parameter1, set_param);
    igvc::getParam(handle_, parameter1, param, igvc::Assertion::POSITIVE);
    EXPECT_FALSE(ros::isShuttingDown());
  }
  {
    int param;
    int set_param = -1;
    handle_.setParam(parameter1, set_param);
    igvc::getParam(handle_, parameter1, param, igvc::Assertion::NEGATIVE);
    EXPECT_FALSE(ros::isShuttingDown());
  }
  {
    std::vector<double> param;
    std::vector<double> set_param{ 3.0, 1.0, 4.0, 1.0, 5.0 };
    handle_.setParam(parameter3, set_param);
    igvc::getParam(handle_, parameter3, param, igvc::Assertion::POSITIVE);
    EXPECT_EQ(param.size(), set_param.size());
    for (size_t i = 0; i < set_param.size(); i++)
    {
      EXPECT_DOUBLE_EQ(param[i], set_param[i]);
    }
    EXPECT_FALSE(ros::isShuttingDown());
  }
}

TEST_F(TestAssertions, getParamCanAssertFalseUsingIGVCAssertionNegativeInt)
{
  int param;
  int set_param = 1;
  handle_.setParam(parameter1, set_param);
  igvc::getParam(handle_, parameter1, param, igvc::Assertion::NEGATIVE);
  EXPECT_TRUE(ros::isShuttingDown());
}
TEST_F(TestAssertions, getParamCanAssertFalseUsingIGVCAssertionPositiveInt)
{
  int param;
  int set_param = -1;
  handle_.setParam(parameter1, set_param);
  igvc::getParam(handle_, parameter1, param, igvc::Assertion::POSITIVE);
  EXPECT_TRUE(ros::isShuttingDown());
}
TEST_F(TestAssertions, getParamCanAssertFalseUsingIGVCAssertionNegativeVector)
{
  std::vector<double> param;
  std::vector<double> set_param{ -3.0, -1.0, -4.0, 1.0, -5.0 };
  handle_.setParam(parameter3, set_param);
  igvc::getParam(handle_, parameter3, param, igvc::Assertion::NEGATIVE);
  EXPECT_TRUE(ros::isShuttingDown());
}
TEST_F(TestAssertions, getParamCanAssertFalseUsingIGVCAssertionPostitiveVector)
{
  std::vector<double> param;
  std::vector<double> set_param{ 3.0, -1.0, 4.0, 1.0, 5.0 };
  handle_.setParam(parameter3, set_param);
  igvc::getParam(handle_, parameter3, param, igvc::Assertion::POSITIVE);
  EXPECT_TRUE(ros::isShuttingDown());
}
// ========== getParam lambdas ==========
TEST_F(TestAssertions, getParamCanAssertTrueUsingLambda)
{
  int param;
  int set_param = -1;
  handle_.setParam(parameter1, set_param);
  igvc::getParam(handle_, parameter1, param, [](const int& x) { return x < -0.5; });
  EXPECT_EQ(param, set_param);
  EXPECT_FALSE(ros::isShuttingDown());
}
TEST_F(TestAssertions, getParamCanAssertFalseUsingLambda)
{
  std::string param;
  std::string set_param = "The cake";
  handle_.setParam(parameter1, set_param);
  igvc::getParam(handle_, parameter1, param, [](const std::string& x) { return x == "is a lie"; });
  EXPECT_TRUE(ros::isShuttingDown());
}
TEST_F(TestAssertions, getParamCanAssertTrueUsingLambdaVector)
{
  std::vector<double> param;
  std::vector<double> set_param{ 0.3, -0.1, 0.4, 0.1, 0.5 };
  handle_.setParam(parameter3, set_param);
  igvc::getParam(handle_, parameter3, param, [](const double& x) { return -1 <= x && x <= 1; });
  EXPECT_EQ(param.size(), set_param.size());
  for (size_t i = 0; i < set_param.size(); i++)
  {
    EXPECT_DOUBLE_EQ(param[i], set_param[i]);
  }
  EXPECT_FALSE(ros::isShuttingDown());
}
TEST_F(TestAssertions, getParamCanAssertFalseUsingLambdaVector)
{
  std::vector<double> param;
  std::vector<double> set_param{ 0.3, -1.1, 0.4, 0.1, 0.5 };
  handle_.setParam(parameter3, set_param);
  igvc::getParam(handle_, parameter3, param, [](const double& x) { return -1 <= x && x <= 1; });
  EXPECT_TRUE(ros::isShuttingDown());
}

// ========== param ==========
TEST_F(TestAssertions, paramCanAssertTrueUsingIGVCAssertion)
{
  {
    int param;
    int set_param = 1;
    handle_.setParam(parameter1, set_param);
    igvc::param(handle_, parameter1, param, -1, igvc::Assertion::POSITIVE);
    EXPECT_FALSE(ros::isShuttingDown());
  }
  {
    int param;
    int set_param = -1;
    handle_.setParam(parameter1, set_param);
    igvc::param(handle_, parameter1, param, 1, igvc::Assertion::NEGATIVE);
    EXPECT_FALSE(ros::isShuttingDown());
  }
  {
    std::vector<double> param;
    std::vector<double> set_param{ 3.0, 1.0, 4.0, 1.0, 5.0 };
    handle_.setParam(parameter3, set_param);
    igvc::param(handle_, parameter3, param, std::vector<double>{}, igvc::Assertion::POSITIVE);
    EXPECT_EQ(param.size(), set_param.size());
    for (size_t i = 0; i < set_param.size(); i++)
    {
      EXPECT_DOUBLE_EQ(param[i], set_param[i]);
    }
    EXPECT_FALSE(ros::isShuttingDown());
  }
}

TEST_F(TestAssertions, paramCanAssertTrueUsingIGVCAssertionDefault)
{
  {
    int param;
    int set_param = 1;
    igvc::param(handle_, parameter1, param, set_param, igvc::Assertion::POSITIVE);
    EXPECT_FALSE(ros::isShuttingDown());
  }
  {
    int param;
    int set_param = -1;
    igvc::param(handle_, parameter1, param, set_param, igvc::Assertion::NEGATIVE);
    EXPECT_FALSE(ros::isShuttingDown());
  }
  {
    std::vector<double> param;
    std::vector<double> set_param{ 3.0, 1.0, 4.0, 1.0, 5.0 };
    igvc::param(handle_, parameter3, param, set_param, igvc::Assertion::POSITIVE);
    EXPECT_EQ(param.size(), set_param.size());
    for (size_t i = 0; i < set_param.size(); i++)
    {
      EXPECT_DOUBLE_EQ(param[i], set_param[i]);
    }
    EXPECT_FALSE(ros::isShuttingDown());
  }
}

TEST_F(TestAssertions, paramCanAssertFalseUsingIGVCAssertionNegativeInt)
{
  int param;
  int set_param = 1;
  handle_.setParam(parameter1, set_param);
  igvc::param(handle_, parameter1, param, 2, igvc::Assertion::NEGATIVE);
  EXPECT_TRUE(ros::isShuttingDown());
}
TEST_F(TestAssertions, paramCanAssertFalseUsingIGVCAssertionNegativeDefault)
{
  double param;
  igvc::param(handle_, parameter1, param, 1.0, igvc::Assertion::NEGATIVE);
  EXPECT_TRUE(ros::isShuttingDown());
}

// =========== param lambdas ===========

TEST_F(TestAssertions, paramCanAssertTrueUsingLambda)
{
  double param;
  double set_param = 1.5;
  handle_.setParam(parameter1, set_param);
  igvc::param(handle_, parameter1, param, 3, [](const double& x) { return x <= 2; });
  EXPECT_DOUBLE_EQ(param, set_param);
  EXPECT_FALSE(ros::isShuttingDown());
}
TEST_F(TestAssertions, paramCanAssertFalseUsingLambda)
{
  double param;
  double set_param = 3;
  handle_.setParam(parameter1, set_param);
  igvc::param(handle_, parameter1, param, 4, [](const double& x) { return x <= 2; });
  EXPECT_TRUE(ros::isShuttingDown());
}
TEST_F(TestAssertions, paramCanAssertTrueUsingLambdaDefault)
{
  double param;
  igvc::param(handle_, parameter1, param, 0, [](const double& x) { return x <= 2; });
  EXPECT_DOUBLE_EQ(param, param);
  EXPECT_FALSE(ros::isShuttingDown());
}
TEST_F(TestAssertions, paramCanAssertTrueUsingLambdaDefaultVector)
{
  std::vector<double> param;
  std::vector<double> set_param{ 0.3, -0.1, 0.4, 0.1, 0.5 };
  igvc::param(handle_, parameter3, param, set_param, [](const double& x) { return -1 <= x && x <= 1; });
  EXPECT_EQ(param.size(), set_param.size());
  for (size_t i = 0; i < set_param.size(); i++)
  {
    EXPECT_DOUBLE_EQ(param[i], set_param[i]);
  }
  EXPECT_FALSE(ros::isShuttingDown());
}

int main(int argc, char** argv)
{
  ros::init(argc, argv, "test_assertions");
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
///
/// \file generic.hpp
/// -----------------
///
/// (c) Copyright Domagoj Saric 2016.
///
///  Use, modification and distribution are subject to the
///  Boost Software License, Version 1.0. (See accompanying file
///  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
///
///  See http://www.boost.org for most recent version.
///
////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------------
#ifndef generic_hpp__99FE2034_248F_4C7D_8CD2_EB2BB8247377
#define generic_hpp__99FE2034_248F_4C7D_8CD2_EB2BB8247377
#pragma once
//------------------------------------------------------------------------------
#include <boost/sweater/queues/mpmc_moodycamel.hpp>

#include <boost/config_ex.hpp>
#include <boost/container/small_vector.hpp>
#include <boost/container/static_vector.hpp>
#include <boost/functionoid/functionoid.hpp>
#include <boost/range/algorithm/count_if.hpp>
#include <boost/range/iterator_range_core.hpp>

#include <atomic>
#include <cstdint>
#include <exception>
#include <future>
#include <iterator>
#ifdef _MSC_VER
#include <malloc.h>
#else
#include <alloca.h>
#endif // _MSC_VER
#include <memory>
#include <mutex>
#include <thread>
//------------------------------------------------------------------------------
namespace boost
{
//------------------------------------------------------------------------------
namespace sweater
{
//------------------------------------------------------------------------------

#ifndef BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY
#	define BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY 0
#endif // BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY

namespace queues { template <typename Work> class mpmc_moodycamel; }

BOOST_OVERRIDABLE_SYMBOL
auto const hardware_concurrency( static_cast<std::uint8_t>( std::thread::hardware_concurrency() ) );

class impl
{
private:
#ifdef __ANROID__
    // https://petewarden.com/2015/10/11/one-weird-trick-for-faster-android-multithreading
    static auto constexpr spin_count = 30 * 1000 * 1000;
#else
    static auto constexpr spin_count =                1;
#endif // __ANROID__

    struct worker_traits : functionoid::std_traits
    {
        static constexpr auto copyable    = functionoid::support_level::na     ;
        static constexpr auto moveable    = functionoid::support_level::nofail ;
        static constexpr auto destructor  = functionoid::support_level::trivial;
        static constexpr auto is_noexcept = true;
        static constexpr auto rtti        = false;

        using empty_handler = functionoid::assert_on_empty;
    }; // struct worker_traits

    using worker_counter = std::atomic<std::uint16_t>;

    class batch_semaphore
    {
    public:
        batch_semaphore( std::uint16_t const initial_value ) : counter_( initial_value ) {}

        void release() noexcept
        {
            std::unique_lock<std::mutex> lock( mutex_ );
            if ( counter_.fetch_sub( 1, std::memory_order_relaxed ) == 1 )
                event_.notify_one();
        }

        BOOST_NOINLINE
        void wait() noexcept
        {
            for ( auto try_count( 0 ); try_count < spin_count; ++try_count )
            {
                bool const all_workers_done( counter_.load( std::memory_order_acquire ) == 0 );
                if ( BOOST_LIKELY( all_workers_done ) )
                    return;
            }
            std::unique_lock<std::mutex> lock( mutex_ );
            while ( BOOST_UNLIKELY( counter_.load( std::memory_order_relaxed ) != 0 ) )
                event_.wait( lock );
        }

        void reset() noexcept { counter_.store( 0, std::memory_order_release ); }

    private:
                worker_counter          counter_;
        mutable std::condition_variable event_  ;
                std::mutex              mutex_  ;
    }; // struct batch_semaphore

    using work_t = functionoid::callable<void(), worker_traits>;

    using my_queue = queues::mpmc_moodycamel<work_t>;

public:
	impl()
#if BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY
    : pool_( BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY - 1 )
#endif
	{
    #if BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY
        BOOST_ASSUME( hardware_concurrency <= BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY );
    #else
        auto const number_of_worker_threads( hardware_concurrency - 1 );
        auto p_workers( std::make_unique<std::thread[]>( number_of_worker_threads ) );
        pool_ = make_iterator_range_n( p_workers.get(), number_of_worker_threads );
    #endif // !BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY
		for ( auto & worker : pool_ )
		{
			auto const worker_loop
			(
				[this]() noexcept
				{
                    auto token( queue_.consumer_token() );

                    work_t work;

                    for ( ; ; )
                    {
                        for ( auto try_count( 0 ); try_count < spin_count; ++try_count )
                        {
                            if ( BOOST_LIKELY( queue_.dequeue( work, token ) ) )
                            {
                                work();
                                if ( spin_count > 1 ) // restart the spin-wait
                                    try_count = 0;
                            }
                        }

                        if ( BOOST_UNLIKELY( brexit_.load( std::memory_order_relaxed ) ) )
							return;

                        {
                            std::unique_lock<std::mutex> lock( mutex_ );
                            /// \note No need for a another loop here as a
                            /// spurious-wakeup would be handled by the check in
                            /// the loop above.
                            ///               (08.11.2016.) (Domagoj Saric)
                            if ( !BOOST_LIKELY( queue_.dequeue( work, token ) ) )
                            {
                                work.clear();
                                work_event_.wait( lock );
                            }
                        }
                        if ( BOOST_LIKELY( static_cast<bool>( work ) ) )
                            work();
                    }
				}
			); // worker_loop
            worker = std::thread( worker_loop );
		}
    #if !BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY
        p_workers.release();
    #endif // !BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY
	}

	~impl() noexcept
	{
		brexit_.store( true, std::memory_order_relaxed );
        work_event_.notify_all();
		for ( auto & worker : pool_ )
			worker.join();
    #if !BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY
        delete[] pool_.begin();
    #endif // BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY
	}

	auto number_of_workers() const
    {
        auto const result( static_cast<std::uint16_t>( pool_.size() + 1 ) );
    #if BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY
        BOOST_ASSUME( result <= BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY );
    #endif
        return result;
    }

    /// For GCD dispatch_apply/OMP-like parallel loops.
    /// \details Guarantees that <VAR>work</VAR> will not be called more than
    /// <VAR>iterations</VAR> times (even if number_of_workers() > iterations).
	template <typename F>
	bool spread_the_sweat( std::uint16_t const iterations, F && __restrict work ) noexcept
	{
		static_assert( noexcept( work( iterations, iterations ) ), "F must be noexcept" );

        if ( BOOST_UNLIKELY( iterations == 0 ) )
            return true;

        auto const number_of_workers               ( this->number_of_workers()      );
		auto const iterations_per_worker           ( iterations / number_of_workers );
        auto const leave_one_for_the_calling_thread( iterations_per_worker == 0     ); // If iterations < workers prefer using the caller thread instead of waking up a worker thread...
		auto const threads_with_extra_iteration    ( iterations % number_of_workers - leave_one_for_the_calling_thread );
        BOOST_ASSERT( !leave_one_for_the_calling_thread || iterations < number_of_workers );

        auto const number_of_work_parts( std::min<std::uint16_t>( number_of_workers, iterations ) );
        std::uint16_t const number_of_dispatched_work_parts( number_of_work_parts - 1 );

        batch_semaphore semaphore( number_of_dispatched_work_parts );

#   if BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY
        BOOST_ASSUME( number_of_dispatched_work_parts < BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY );
#   else
        BOOST_ASSUME( number_of_dispatched_work_parts < 512 );
#   endif
        /// \note MSVC does not support VLAs but has an alloca that returns
        /// (16 byte) aligned memory. Clang's alloca is unaligned and it does
        /// not support VLAs of non-POD types.
        /// The code below is safe with noexcept enqueue_bulk() and trivially
        /// destructible and noexcept constructible work_ts.
        ///                                   (21.01.2017.) (Domagoj Saric)
#   ifdef BOOST_MSVC
        auto const dispatched_work_parts( static_cast<work_t *>( alloca( ( number_of_dispatched_work_parts ) * sizeof( work_t ) ) ) );
#   else
        alignas( work_t ) char dispatched_work_parts_storage[ number_of_dispatched_work_parts * sizeof( work_t ) ];
        auto * const BOOST_MAY_ALIAS dispatched_work_parts( reinterpret_cast<work_t *>( dispatched_work_parts_storage ) );
#   endif // _MSC_VER

        std::uint16_t iteration( 0 );
        if ( BOOST_LIKELY( iterations > 1 ) )
        {
            for ( std::uint8_t work_part( 0 ); work_part < number_of_dispatched_work_parts; ++work_part )
            {
                auto const start_iteration( iteration );
                auto const extra_iteration( work_part < threads_with_extra_iteration );
                auto const end_iteration  ( start_iteration + iterations_per_worker + extra_iteration );
                new ( &dispatched_work_parts[ work_part ] ) work_t
                (
                    [&work, &semaphore, start_iteration = iteration, end_iteration]() noexcept
                    {
                        work( start_iteration, end_iteration );
                        semaphore.release();
                    }
                );
                iteration = end_iteration;
            }

            auto const enqueue_succeeded( queue_.enqueue_bulk( std::make_move_iterator( dispatched_work_parts ), number_of_dispatched_work_parts ) );
            for ( std::uint8_t work_part( 0 ); work_part < number_of_dispatched_work_parts; ++work_part )
                dispatched_work_parts[ work_part ].~work_t();
            if ( BOOST_LIKELY( enqueue_succeeded ) )
            {
                std::unique_lock<std::mutex> lock( mutex_ );
                work_event_.notify_all();
            }
            else
            {
                /// \note If enqueue failed perform everything on the caller's
                /// thread.
                ///                           (21.01.2017.) (Domagoj Saric)
                iteration = 0;
                semaphore.reset();
            }
        }

		auto const caller_thread_start_iteration( iteration );
		BOOST_ASSERT( caller_thread_start_iteration < iterations );
		work( caller_thread_start_iteration, iterations );

        if ( BOOST_LIKELY( iterations > 1 ) )
        {
            semaphore.wait();
        }

        return true;
	}

	template <typename F>
	bool fire_and_forget( F && work )
	{
        struct self_destructed_work
        {
            self_destructed_work( F       && work ) noexcept( std::is_nothrow_move_constructible<F>::value ) { new ( storage ) F( std::move( work ) ); }
            self_destructed_work( F const &  work ) noexcept( std::is_nothrow_copy_constructible<F>::value ) { new ( storage ) F(            work   ); }
            void operator()() noexcept
            {
                auto & work( reinterpret_cast<F &>( storage ) );
                work();
                work.~F();
            }
            alignas( work ) char storage[ sizeof( work ) ];
        };
        auto const enqueue_succeeded( queue_.enqueue( self_destructed_work( std::forward<F>( work ) ) ) );
        /// No need for a branch here as the worker thread has to handle
        /// spurious wakeups anyway.
        std::unique_lock<std::mutex> lock( mutex_ );
        work_event_.notify_one();
        return BOOST_LIKELY( enqueue_succeeded );
	}

    template <typename F>
    auto dispatch( F && work )
    {
        // http://scottmeyers.blogspot.hr/2013/03/stdfutures-from-stdasync-arent-special.html
        using result_t = typename std::result_of<F()>::type;
        std::promise<result_t> promise;
        std::future<result_t> future( promise.get_future() );
        auto const dispatch_succeeded
        (
            fire_and_forget
            (
                [promise = std::move( promise ), work = std::forward<F>( work )]
                () mutable { promise.set_value( work() ); }
            )
        );
        if ( BOOST_UNLIKELY( !dispatch_succeeded ) )
            future.set_exception( std::make_exception_ptr( std::bad_alloc() ) );
        return future;
    }

private:
            std::atomic<bool>       brexit_ = ATOMIC_FLAG_INIT;
	        std::mutex              mutex_;
    mutable std::condition_variable work_event_;

#if BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY
	using pool_threads_t = container::static_vector<std::thread, BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY - 1>; // also sweat on the calling thread
#else
    using pool_threads_t = iterator_range<std::thread *>;
#endif
	pool_threads_t pool_;

    /// \todo Further queue refinements.
    /// https://en.wikipedia.org/wiki/Work_stealing
    /// http://www.drdobbs.com/parallel/writing-lock-free-code-a-corrected-queue/210604448
    /// https://github.com/cameron314/readerwriterqueue
    /// http://moodycamel.com/blog/2013/a-fast-lock-free-queue-for-c++
    /// http://stackoverflow.com/questions/1164023/is-there-a-production-ready-lock-free-queue-or-hash-implementation-in-c#14936831
    /// https://github.com/facebook/folly/blob/master/folly/docs/ProducerConsumerQueue.md
    /// https://github.com/facebook/folly/blob/master/folly/MPMCQueue.h
    /// http://landenlabs.com/code/ring/ring.html
    /// https://github.com/Qarterd/Honeycomb/blob/master/src/common/Honey/Thread/Pool.cpp
    ///                                       (12.10.2016.) (Domagoj Saric)
    my_queue queue_;
}; // class impl

//------------------------------------------------------------------------------
} // namespace sweater
//------------------------------------------------------------------------------
} // namespace boost
//------------------------------------------------------------------------------
#endif // generic_hpp<commit_msg>Minor optimization.<commit_after>////////////////////////////////////////////////////////////////////////////////
///
/// \file generic.hpp
/// -----------------
///
/// (c) Copyright Domagoj Saric 2016.
///
///  Use, modification and distribution are subject to the
///  Boost Software License, Version 1.0. (See accompanying file
///  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
///
///  See http://www.boost.org for most recent version.
///
////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------------
#ifndef generic_hpp__99FE2034_248F_4C7D_8CD2_EB2BB8247377
#define generic_hpp__99FE2034_248F_4C7D_8CD2_EB2BB8247377
#pragma once
//------------------------------------------------------------------------------
#include <boost/sweater/queues/mpmc_moodycamel.hpp>

#include <boost/config_ex.hpp>
#include <boost/container/small_vector.hpp>
#include <boost/container/static_vector.hpp>
#include <boost/functionoid/functionoid.hpp>
#include <boost/range/algorithm/count_if.hpp>
#include <boost/range/iterator_range_core.hpp>

#include <atomic>
#include <cstdint>
#include <exception>
#include <future>
#include <iterator>
#ifdef _MSC_VER
#include <malloc.h>
#else
#include <alloca.h>
#endif // _MSC_VER
#include <memory>
#include <mutex>
#include <thread>
//------------------------------------------------------------------------------
namespace boost
{
//------------------------------------------------------------------------------
namespace sweater
{
//------------------------------------------------------------------------------

#ifndef BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY
#	define BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY 0
#endif // BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY

namespace queues { template <typename Work> class mpmc_moodycamel; }

BOOST_OVERRIDABLE_SYMBOL
auto const hardware_concurrency( static_cast<std::uint8_t>( std::thread::hardware_concurrency() ) );

class impl
{
private:
#ifdef __ANROID__
    // https://petewarden.com/2015/10/11/one-weird-trick-for-faster-android-multithreading
    static auto constexpr spin_count = 30 * 1000 * 1000;
#else
    static auto constexpr spin_count =                1;
#endif // __ANROID__

    struct worker_traits : functionoid::std_traits
    {
        static constexpr auto copyable    = functionoid::support_level::na     ;
        static constexpr auto moveable    = functionoid::support_level::nofail ;
        static constexpr auto destructor  = functionoid::support_level::trivial;
        static constexpr auto is_noexcept = true;
        static constexpr auto rtti        = false;

        using empty_handler = functionoid::assert_on_empty;
    }; // struct worker_traits

    using worker_counter = std::atomic<std::uint16_t>;

    class batch_semaphore
    {
    public:
        batch_semaphore( std::uint16_t const initial_value ) : counter_( initial_value ) {}

        void release() noexcept
        {
            std::unique_lock<std::mutex> lock( mutex_ );
            if ( counter_.fetch_sub( 1, std::memory_order_relaxed ) == 1 )
                event_.notify_one();
        }

        BOOST_NOINLINE
        void wait() noexcept
        {
            for ( auto try_count( 0 ); try_count < spin_count; ++try_count )
            {
                bool const all_workers_done( counter_.load( std::memory_order_acquire ) == 0 );
                if ( BOOST_LIKELY( all_workers_done ) )
                    return;
            }
            std::unique_lock<std::mutex> lock( mutex_ );
            while ( BOOST_UNLIKELY( counter_.load( std::memory_order_relaxed ) != 0 ) )
                event_.wait( lock );
        }

        void reset() noexcept { counter_.store( 0, std::memory_order_release ); }

    private:
                worker_counter          counter_;
        mutable std::condition_variable event_  ;
                std::mutex              mutex_  ;
    }; // struct batch_semaphore

    using work_t = functionoid::callable<void(), worker_traits>;

    using my_queue = queues::mpmc_moodycamel<work_t>;

public:
	impl()
#if BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY
    : pool_( BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY - 1 )
#endif
	{
    #if BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY
        BOOST_ASSUME( hardware_concurrency <= BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY );
    #else
        auto const number_of_worker_threads( hardware_concurrency - 1 );
        auto p_workers( std::make_unique<std::thread[]>( number_of_worker_threads ) );
        pool_ = make_iterator_range_n( p_workers.get(), number_of_worker_threads );
    #endif // !BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY
		for ( auto & worker : pool_ )
		{
			auto const worker_loop
			(
				[this]() noexcept
				{
                    auto token( queue_.consumer_token() );

                    work_t work;

                    for ( ; ; )
                    {
                        for ( auto try_count( 0 ); try_count < spin_count; ++try_count )
                        {
                            if ( BOOST_LIKELY( queue_.dequeue( work, token ) ) )
                            {
                                work();
                                if ( spin_count > 1 ) // restart the spin-wait
                                    try_count = 0;
                            }
                        }

                        if ( BOOST_UNLIKELY( brexit_.load( std::memory_order_relaxed ) ) )
							return;

                        {
                            std::unique_lock<std::mutex> lock( mutex_ );
                            /// \note No need for a another loop here as a
                            /// spurious-wakeup would be handled by the check in
                            /// the loop above.
                            ///               (08.11.2016.) (Domagoj Saric)
                            if ( !BOOST_LIKELY( queue_.dequeue( work, token ) ) )
                            {
                                work.clear();
                                work_event_.wait( lock );
                            }
                        }
                        if ( BOOST_LIKELY( static_cast<bool>( work ) ) )
                            work();
                    }
				}
			); // worker_loop
            worker = std::thread( worker_loop );
		}
    #if !BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY
        p_workers.release();
    #endif // !BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY
	}

	~impl() noexcept
	{
		brexit_.store( true, std::memory_order_relaxed );
        work_event_.notify_all();
		for ( auto & worker : pool_ )
			worker.join();
    #if !BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY
        delete[] pool_.begin();
    #endif // BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY
	}

	auto number_of_workers() const
    {
        auto const result( static_cast<std::uint16_t>( pool_.size() + 1 ) );
    #if BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY
        BOOST_ASSUME( result <= BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY );
    #endif
        return result;
    }

    /// For GCD dispatch_apply/OMP-like parallel loops.
    /// \details Guarantees that <VAR>work</VAR> will not be called more than
    /// <VAR>iterations</VAR> times (even if number_of_workers() > iterations).
	template <typename F>
	bool spread_the_sweat( std::uint16_t const iterations, F && __restrict work ) noexcept
	{
		static_assert( noexcept( work( iterations, iterations ) ), "F must be noexcept" );

        if ( BOOST_UNLIKELY( iterations == 0 ) )
            return true;

        auto const number_of_workers               ( this->number_of_workers()      );
		auto const iterations_per_worker           ( iterations / number_of_workers );
        auto const leave_one_for_the_calling_thread( iterations_per_worker == 0     ); // If iterations < workers prefer using the caller thread instead of waking up a worker thread...
		auto const threads_with_extra_iteration    ( iterations % number_of_workers - leave_one_for_the_calling_thread );
        BOOST_ASSERT( !leave_one_for_the_calling_thread || iterations < number_of_workers );

        auto const number_of_work_parts( std::min<std::uint16_t>( number_of_workers, iterations ) );
        std::uint16_t const number_of_dispatched_work_parts( number_of_work_parts - 1 );

        batch_semaphore semaphore( number_of_dispatched_work_parts );

#   if BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY
        BOOST_ASSUME( number_of_dispatched_work_parts < BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY );
#   else
        BOOST_ASSUME( number_of_dispatched_work_parts < 512 );
#   endif
        /// \note MSVC does not support VLAs but has an alloca that returns
        /// (16 byte) aligned memory. Clang's alloca is unaligned and it does
        /// not support VLAs of non-POD types.
        /// The code below is safe with noexcept enqueue_bulk() and trivially
        /// destructible and noexcept constructible work_ts.
        ///                                   (21.01.2017.) (Domagoj Saric)
#   ifdef BOOST_MSVC
        auto const dispatched_work_parts( static_cast<work_t *>( alloca( ( number_of_dispatched_work_parts ) * sizeof( work_t ) ) ) );
#   else
        alignas( work_t ) char dispatched_work_parts_storage[ number_of_dispatched_work_parts * sizeof( work_t ) ];
        auto * const BOOST_MAY_ALIAS dispatched_work_parts( reinterpret_cast<work_t *>( dispatched_work_parts_storage ) );
#   endif // _MSC_VER

        std::uint16_t iteration( 0 );
        if ( BOOST_LIKELY( iterations > 1 ) )
        {
            for ( std::uint8_t work_part( 0 ); work_part < number_of_dispatched_work_parts; ++work_part )
            {
                auto          const start_iteration( iteration );
                auto          const extra_iteration( work_part < threads_with_extra_iteration );
                std::uint16_t const end_iteration  ( start_iteration + iterations_per_worker + extra_iteration );
                auto const placeholder( &dispatched_work_parts[ work_part ] );
#          ifdef _MSC_VER
                // MSVC14u3 still generates a branch w/o this (GCC issues a warning that it knows that placeholder cannot be null so we have to ifdef guard this).
                BOOST_ASSUME( placeholder );
#          endif // _MSC_VER
                new ( placeholder ) work_t
                (
                    [&work, &semaphore, start_iteration = iteration, end_iteration]() noexcept
                    {
                        work( start_iteration, end_iteration );
                        semaphore.release();
                    }
                );
                iteration = end_iteration;
            }

            auto const enqueue_succeeded( queue_.enqueue_bulk( std::make_move_iterator( dispatched_work_parts ), number_of_dispatched_work_parts ) );
            for ( std::uint8_t work_part( 0 ); work_part < number_of_dispatched_work_parts; ++work_part )
                dispatched_work_parts[ work_part ].~work_t();
            if ( BOOST_LIKELY( enqueue_succeeded ) )
            {
                std::unique_lock<std::mutex> lock( mutex_ );
                work_event_.notify_all();
            }
            else
            {
                /// \note If enqueue failed perform everything on the caller's
                /// thread.
                ///                           (21.01.2017.) (Domagoj Saric)
                iteration = 0;
                semaphore.reset();
            }
        }

		auto const caller_thread_start_iteration( iteration );
		BOOST_ASSERT( caller_thread_start_iteration < iterations );
		work( caller_thread_start_iteration, iterations );

        if ( BOOST_LIKELY( iterations > 1 ) )
        {
            semaphore.wait();
        }

        return true;
	}

	template <typename F>
	bool fire_and_forget( F && work )
	{
        struct self_destructed_work
        {
            self_destructed_work( F       && work ) noexcept( std::is_nothrow_move_constructible<F>::value ) { new ( storage ) F( std::move( work ) ); }
            self_destructed_work( F const &  work ) noexcept( std::is_nothrow_copy_constructible<F>::value ) { new ( storage ) F(            work   ); }
            void operator()() noexcept
            {
                auto & work( reinterpret_cast<F &>( storage ) );
                work();
                work.~F();
            }
            alignas( work ) char storage[ sizeof( work ) ];
        };
        auto const enqueue_succeeded( queue_.enqueue( self_destructed_work( std::forward<F>( work ) ) ) );
        /// No need for a branch here as the worker thread has to handle
        /// spurious wakeups anyway.
        std::unique_lock<std::mutex> lock( mutex_ );
        work_event_.notify_one();
        return BOOST_LIKELY( enqueue_succeeded );
	}

    template <typename F>
    auto dispatch( F && work )
    {
        // http://scottmeyers.blogspot.hr/2013/03/stdfutures-from-stdasync-arent-special.html
        using result_t = typename std::result_of<F()>::type;
        std::promise<result_t> promise;
        std::future<result_t> future( promise.get_future() );
        auto const dispatch_succeeded
        (
            fire_and_forget
            (
                [promise = std::move( promise ), work = std::forward<F>( work )]
                () mutable { promise.set_value( work() ); }
            )
        );
        if ( BOOST_UNLIKELY( !dispatch_succeeded ) )
            future.set_exception( std::make_exception_ptr( std::bad_alloc() ) );
        return future;
    }

private:
            std::atomic<bool>       brexit_ = ATOMIC_FLAG_INIT;
	        std::mutex              mutex_;
    mutable std::condition_variable work_event_;

#if BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY
	using pool_threads_t = container::static_vector<std::thread, BOOST_SWEATER_MAX_HARDWARE_CONCURRENCY - 1>; // also sweat on the calling thread
#else
    using pool_threads_t = iterator_range<std::thread *>;
#endif
	pool_threads_t pool_;

    /// \todo Further queue refinements.
    /// https://en.wikipedia.org/wiki/Work_stealing
    /// http://www.drdobbs.com/parallel/writing-lock-free-code-a-corrected-queue/210604448
    /// https://github.com/cameron314/readerwriterqueue
    /// http://moodycamel.com/blog/2013/a-fast-lock-free-queue-for-c++
    /// http://stackoverflow.com/questions/1164023/is-there-a-production-ready-lock-free-queue-or-hash-implementation-in-c#14936831
    /// https://github.com/facebook/folly/blob/master/folly/docs/ProducerConsumerQueue.md
    /// https://github.com/facebook/folly/blob/master/folly/MPMCQueue.h
    /// http://landenlabs.com/code/ring/ring.html
    /// https://github.com/Qarterd/Honeycomb/blob/master/src/common/Honey/Thread/Pool.cpp
    ///                                       (12.10.2016.) (Domagoj Saric)
    my_queue queue_;
}; // class impl

//------------------------------------------------------------------------------
} // namespace sweater
//------------------------------------------------------------------------------
} // namespace boost
//------------------------------------------------------------------------------
#endif // generic_hpp<|endoftext|>