/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /*! * \file pipeline.hpp * \ingroup building_block high_level_patterns * * \brief This file implements the pipeline skeleton, both in the high-level pattern * syntax (\ref ff::ff_pipe) and low-level syntax (\ref ff::ff_pipeline) * */ /* *************************************************************************** * FastFlow is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3 as * published by the Free Software Foundation. * Starting from version 3.0.1 FastFlow is dual licensed under the GNU LGPLv3 * or MIT License (https://github.com/ParaGroup/WindFlow/blob/vers3.x/LICENSE.MIT) * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * **************************************************************************** */ #ifndef FF_PIPELINE_HPP #define FF_PIPELINE_HPP #include #include #include #include #include #ifdef FF_OPENCL #include #endif #if defined(MAMMUT) #include #endif namespace ff { static int nthreads = 8; // forward declarations class ff_pipeline; static inline int optimize_static(ff_pipeline&, const OptLevel&); template static inline int combine_with_firststage(ff_pipeline&,T*,bool=false); template static inline int combine_with_laststage(ff_pipeline&,T*,bool=false); static bool isfarm_withcollector(ff_node*); static bool isfarm_multimultioutput(ff_node*); static const svector& isa2a_getfirstset(ff_node*); static const svector& isa2a_getsecondset(ff_node*); static const svector& isfarm_getworkers(ff_node*); /** * \class ff_pipeline * \ingroup core_patterns * * \brief The Pipeline skeleton (low-level syntax) * */ class ff_pipeline: public ff_node { friend inline int optimize_static(ff_pipeline&, const OptLevel&); template friend inline int combine_with_firststage(ff_pipeline&,T*,bool); template friend inline int combine_with_laststage(ff_pipeline&,T*,bool); protected: int prepare_wraparound() { const int last = static_cast(nodes_list.size())-1; bool isa2a_first = get_node(0)->isAll2All(); bool isa2a_last = get_lastnode()->isAll2All(); bool isfarm_last = get_lastnode()->isFarm(); // possible cases: [captured by] // // the last stage is a standard node (the first stage can also be a multi-input node) [last_single_standard] // the first stage is a standard node (the last stage can also be a multi-output node) [first_single_standard] // // // the last stage is a multi-output node: // - it's a farm with a multi-output collector [last_single_multioutput] // - it's a farm without collector and workers are standard nodes [last_multi_standard] // - it's a farm without collector with multi-output workers [last_multi_multioutput] // - it's a all2all with standard nodes [last_multi_standard] // - it's a all2all with multi-output nodes [last_multi_multioutput] // - it's a singolo nodo (comp or pipeline) with the last stage multi-output [last_single_multioutput] // // // the first stage is multi-input // - it's a farm (the emettitore is multi-input by default) [first_single_multiinput] // - it's a single multi-input node [first_single_multiinput] // - it's all2all with standard nodes [first_multi_standard] // - its' all2all with multi-input nodes [first_multi_multiinput] // bool last_isfarm_nocollector = get_lastnode()->isFarm() && !isfarm_withcollector(get_lastnode()); bool last_isfarm_withcollector = get_lastnode()->isFarm() && !last_isfarm_nocollector; bool first_single_standard = (!nodes_list[0]->isMultiInput()); // the farm is considered single_multiinput bool first_single_multiinput = (nodes_list[0]->isMultiInput() && !isa2a_first); bool first_multi_standard = [&]() { if (!isa2a_first) return false; const svector& w1=isa2a_getfirstset(get_node(0)); assert(w1.size()>0); if (w1[0]->isMultiInput()) return false; // NOTE: we suppose homogeneous first set return true; }(); bool first_multi_multiinput = [&]() { if (!isa2a_first) return false; const svector& w1=isa2a_getfirstset(get_node(0)); assert(w1.size()>0); if (w1[0]->isMultiInput()) return true; // NOTE: we suppose homogeneous first set return false; } (); bool last_single_standard = (!nodes_list[last]->isMultiOutput()); bool last_single_multioutput = ((nodes_list[last]->isMultiOutput() && !isa2a_last && !isfarm_last) || (last_isfarm_withcollector && nodes_list[last]->isMultiOutput())); bool last_multi_standard = [&]() { if (last_isfarm_nocollector) { return !isfarm_multimultioutput(get_lastnode()); } if (isa2a_last) { const svector& w1=isa2a_getsecondset(nodes_list[last]); assert(w1.size()>0); if (!w1[0]->isMultiOutput()) return true; // NOTE: we suppose homogeneous second set } return false; } (); bool last_multi_multioutput = [&]() { if (last_isfarm_nocollector) { return isfarm_multimultioutput(get_lastnode()); } if (isa2a_last) { const svector& w1=isa2a_getsecondset(nodes_list[last]); assert(w1.size()>0); if (w1[0]->isMultiOutput()) return true; // NOTE: we suppose homogeneous second set } return false; } (); // first stage: standard node if (first_single_standard) { if (create_input_buffer(out_buffer_entries, false)<0) return -1; if (last_single_standard) { if (set_output_buffer(get_in_buffer())<0) return -1; // blocking stuff ------ pthread_mutex_t *m = NULL; pthread_cond_t *c = NULL; if (!nodes_list[last]->init_output_blocking(m,c)) return -1; if (!nodes_list[0]->init_input_blocking(m,c)) return -1; nodes_list[last]->set_output_blocking(m,c); // --------------------- } else { if (last_single_multioutput) { ff_node *t = new ff_buffernode(last, get_in_buffer(), get_in_buffer()); internalSupportNodes.push_back(t); assert(t); nodes_list[last]->set_output_feedback(t); // blocking stuff ------ pthread_mutex_t *m = NULL; pthread_cond_t *c = NULL; if (!nodes_list[last]->init_output_blocking(m,c)) return -1; if (!nodes_list[0]->init_input_blocking(m,c)) return -1; // NOTE: if the node0 is multi-input, then the init_input_blocking // method already calls set_output_blocking t->set_output_blocking(m,c); // --------------------- } else { error("PIPE, cannot connect stage %d with node %d\n", last, 0); return -1; } } } // first stage: multi-input if (first_single_multiinput) { if (last_single_standard) { if (nodes_list[last]->create_output_buffer(out_buffer_entries,false)<0) return -1; nodes_list[0]->set_input_feedback(nodes_list[last]); // blocking stuff ...... pthread_mutex_t *m = NULL; pthread_cond_t *c = NULL; if (!nodes_list[last]->init_output_blocking(m,c)) return -1; // multi-input nodes execute set_output_blocking if (!nodes_list[0]->init_input_blocking(m,c)) return -1; // --------------------- } else { if (last_single_multioutput) { ff_node *t = new ff_buffernode(out_buffer_entries,false, last); assert(t); internalSupportNodes.push_back(t); nodes_list[last]->set_output_feedback(t); nodes_list[0]->set_input_feedback(t); // blocking stuff ...... pthread_mutex_t *m = NULL; pthread_cond_t *c = NULL; if (!nodes_list[last]->init_output_blocking(m,c)) return -1; // multi-input nodes execute set_output_blocking if (!nodes_list[0]->init_input_blocking(m,c)) return -1; // --------------------- } else { if (last_multi_standard) { if (nodes_list[last]->create_output_buffer(out_buffer_entries, false) <0) return -1; svector w(MAX_NUM_THREADS); nodes_list[last]->get_out_nodes(w); assert(w.size()); for(size_t j=0;jset_input_feedback(w[j]); // blocking stuff ...... pthread_mutex_t *m = NULL; pthread_cond_t *c = NULL; if (!nodes_list[last]->init_output_blocking(m,c)) return -1; // multi-input nodes execute set_output_blocking if (!nodes_list[0]->init_input_blocking(m,c)) return -1; // --------------------- } else { if (last_multi_multioutput) { svector w; ff_node *lastbb = get_lastnode(); if (lastbb->isAll2All()) w = isa2a_getsecondset(lastbb); else { assert(lastbb->isFarm()); w = isfarm_getworkers(lastbb); } assert(w.size()); for(size_t j=0;jset_input_feedback(t); w[j]->set_output_feedback(t); } // blocking stuff ...... pthread_mutex_t *m = NULL; pthread_cond_t *c = NULL; if (!nodes_list[last]->init_output_blocking(m,c)) return -1; // multi-input nodes execute set_output_blocking if (!nodes_list[0]->init_input_blocking(m,c)) return -1; // --------------------- } else { error("PIPE, wrap_around invalid stage\n"); return -1; } } } } } // first stage: multi standard if (first_multi_standard) { // all-to-all assert(get_node(0)->isAll2All()); ff_node* a2a = nodes_list[0]; const svector& firstSet=isa2a_getfirstset(a2a); assert(firstSet.size()>0); if (last_single_standard) { error("PIPE, wrap_around, cannot connect last with first stage\n"); return -1; } if (last_single_multioutput) { for(size_t j=0;jset_input(t); nodes_list[last]->set_output_feedback(t); // blocking stuff ...... pthread_mutex_t *m = NULL; pthread_cond_t *c = NULL; if (!firstSet[j]->init_input_blocking(m,c)) return -1; t->set_output_blocking(m,c); // --------------------- } // blocking stuff ...... pthread_mutex_t *m = NULL; pthread_cond_t *c = NULL; nodes_list[last]->init_output_blocking(m,c); // --------------------- } if (last_multi_standard) { svector w(MAX_NUM_THREADS); nodes_list[last]->get_out_nodes(w); assert(w.size()); assert(w.size() == firstSet.size()); if (a2a->create_input_buffer(in_buffer_entries, false)<0) return -1; for(size_t j=0;jset_output_buffer(firstSet[j]->get_in_buffer()); // blocking stuff ...... pthread_mutex_t *m = NULL; pthread_cond_t *c = NULL; if (!a2a->init_output_blocking(m,c)) return -1; for(size_t j=0;jinit_input_blocking(m,c)) return -1; w[j]->set_output_blocking(m,c); } // --------------------- } if (last_multi_multioutput) { error("PIPE, wrap_around, cannot connect last stage with first stage\n"); return -1; } } // first stage: multi multi-input if (first_multi_multiinput) { assert(get_node(0)->isAll2All()); const svector& firstSet=isa2a_getfirstset(nodes_list[0]); assert(firstSet.size()>0); if (last_single_standard) { error("PIPE, wrap_around, cannot connect last with first stage\n"); return -1; } if (last_single_multioutput) { for(size_t j=0;jset_input_feedback(t); nodes_list[last]->set_output_feedback(t); } } if (last_multi_standard) { svector w(MAX_NUM_THREADS); nodes_list[last]->get_out_nodes(w); assert(w.size()); assert(w.size() == firstSet.size()); for(size_t i=0;iset_input_feedback(t); w[i]->set_output(t); } } if (last_multi_multioutput) { svector w; ff_node *lastbb = get_lastnode(); if (lastbb->isAll2All()) w = isa2a_getsecondset(lastbb); else { assert(lastbb->isFarm()); w = isfarm_getworkers(lastbb); } assert(w.size()); // here we have to create all connections for(size_t i=0;iset_input_feedback(t); w[j]->set_output_feedback(t); } } } // blocking stuff ...... pthread_mutex_t *m = NULL; pthread_cond_t *c = NULL; if (!nodes_list[last]->init_output_blocking(m,c)) return -1; if (!nodes_list[0]->init_input_blocking(m,c)) return -1; // --------------------- } return 0; } inline int prepare() { if (wraparound) { if (nodes_list.size()<2) { error("PIPE, too few pipeline nodes\n"); return -1; } if (prepare_wraparound()<0) { error("PIPE, prepare_wraparound failed\n"); return -1; } } const int nstages=static_cast(nodes_list.size()); // possible cases: [captured by] // // the current stage is a standard node (the previous stage can also be multi-output) [curr_single_standard] // // the current stage is multi-input // - it's a farm (the emettitore is multi-input by default) [curr_single_multiinput] // - it's a single multi-input node [curr_single_multiinput] // - it's all2all with standard nodes [curr_multi_standard] // - it's all2all with multi-input nodes [curr_multi_multiinput] // // the previous stage is a standard node [prev_single_standard] // // the previous stage is a multi-output node: // - it's a farm with a multi-output collector [prev_single_multioutput] // - it's a farm without collector and workers are standard node [prev_multi_standard] // - it's a farm without collector with multi-output workers [prev_multi_multioutput] // - it's a all2all with standard nodes [prev_multi_standard] // - it's a all2all with multi-output nodes [prev_multi_multioutput] // - it's a single node (comp or pipeline) with the last stage multi-output [prev_single_multioutput] // // for(int i=1;iisAll2All(); const bool curr_single_standard = (!nodes_list[i]->isMultiInput()); // the farm is considered single_multiinput const bool curr_single_multiinput = (!isa2a_curr && nodes_list[i]->isMultiInput()); const bool curr_multi_multiinput = [&]() { if (!isa2a_curr) return false; const svector& w1=isa2a_getfirstset(get_node(i)); assert(w1.size()>0); for(size_t k=0;k w2(1); w1[k]->get_in_nodes(w2); for(size_t j=0;jisMultiInput()) return true; } return false; } (); const bool curr_multi_standard = [&]() { if (!isa2a_curr) return false; return !curr_multi_multiinput; }(); const bool isa2a_prev = get_node_last(i-1)->isAll2All(); const bool isfarm_prev = get_node_last(i-1)->isFarm(); const bool prev_isfarm_nocollector = (isfarm_prev && !get_node_last(i-1)->isOFarm() && !(isfarm_withcollector(get_node_last(i-1)))); const bool prev_isfarm_withcollector = isfarm_withcollector(get_node_last(i-1)); const bool prev_single_standard = (!get_node_last(i-1)->isMultiOutput()); const bool prev_single_multioutput = ((get_node_last(i-1)->isMultiOutput() && !isa2a_prev && !isfarm_prev) || (prev_isfarm_withcollector && get_node_last(i-1)->isMultiOutput())); const bool prev_multi_standard = [&]() { if (prev_isfarm_nocollector) { svector w1; nodes_list[i-1]->get_out_nodes(w1); if (!w1[0]->isMultiOutput()) return true; // NOTE: we suppose homogeneous workers } if (isa2a_prev) { const svector& w1=isa2a_getsecondset(get_node_last(i-1)); assert(w1.size()>0); if (!w1[0]->isMultiOutput()) return true; // NOTE: we suppose homogeneous workers } return false; } (); const bool prev_multi_multioutput = [&]() { if (prev_isfarm_nocollector) { svector w1; nodes_list[i-1]->get_out_nodes(w1); if (w1[0]->isMultiOutput()) return true; // NOTE: we suppose homogeneous workers } if (isa2a_prev) { const svector& w1=isa2a_getsecondset(get_node_last(i-1)); assert(w1.size()>0); if (w1[0]->isMultiOutput()) return true; // NOTE: we suppose homogeneous workers } return false; } (); pthread_mutex_t *m = NULL; pthread_cond_t *c = NULL; if (curr_single_standard) { bool skip_set_output_blocking = false; if (nodes_list[i]->create_input_buffer(in_buffer_entries, fixedsizeIN)<0) return -1; if (prev_single_standard) { if (nodes_list[i-1]->set_output_buffer(nodes_list[i]->get_in_buffer())<0) return -1; } else { skip_set_output_blocking = true; // WARNING: here we add as output node of the previous stage the // current node and not a buffer-node. if (prev_multi_standard || prev_multi_multioutput) { svector w(1); nodes_list[i-1]->get_out_nodes(w); if (w.size()>1) { error("PIPE, cannot connect stage %d with stage %d\n", i-1, i); return -1; } nodes_list[i-1]->set_output(nodes_list[i]); } else { assert(prev_single_multioutput); nodes_list[i-1]->set_output(nodes_list[i]); } } // blocking stuff -------------------------------------------- if (!nodes_list[i]->init_input_blocking(m,c)) { error("PIPE, init input blocking mode for node %d\n", i); return -1; } if (!skip_set_output_blocking) // we do not want to overwrite previous setting nodes_list[i-1]->set_output_blocking(m,c); if (!nodes_list[i-1]->init_output_blocking(m,c,false)) { error("PIPE, init output blocking mode for node %d\n", i-1); return -1; } // ------------------------------------------------------------ } if (curr_single_multiinput) { if (prev_single_standard) { if (nodes_list[i-1]->create_output_buffer(in_buffer_entries, fixedsizeOUT)<0) return -1; if (nodes_list[i]->set_input(nodes_list[i-1])<0) return -1; // blocking stuff -------------------------------------------- if (!nodes_list[i]->init_input_blocking(m,c)) { error("PIPE, init input blocking mode for node %d\n", i); return -1; } // since the curr node is multi-input, the following op has been executed // by the previous one. //nodes_list[i-1]->set_output_blocking(m,c); if (!nodes_list[i-1]->init_output_blocking(m,c)) { error("PIPE, init output blocking mode for node %d\n", i-1); return -1; } // ------------------------------------------------------------ } else { if (prev_single_multioutput) { ff_node* t = new ff_buffernode(in_buffer_entries,fixedsizeIN|fixedsizeOUT, i); internalSupportNodes.push_back(t); nodes_list[i-1]->set_output(t); nodes_list[i]->set_input(t); } if (prev_multi_standard) { if (nodes_list[i-1]->create_output_buffer(out_buffer_entries, fixedsizeOUT)<0) return -1; svector w(MAX_NUM_THREADS); nodes_list[i-1]->get_out_nodes(w); assert(w.size()); nodes_list[i]->set_input(w); //if (w.size() == 0) nodes_list[i]->set_input(nodes_list[i-1]); } if (prev_multi_multioutput) { svector w(MAX_NUM_THREADS); nodes_list[i-1]->get_out_nodes(w); assert(w.size()); for(size_t j=0;jset_output(t); nodes_list[i]->set_input(t); } } // blocking stuff -------------------------------------------- if (!nodes_list[i]->init_input_blocking(m,c)) { error("PIPE, init input blocking mode for node %d\n", i); return -1; } svector w(MAX_NUM_THREADS); nodes_list[i-1]->get_out_nodes(w); assert(w.size()); for(size_t j=0;jset_output_blocking(m,c); } if (!nodes_list[i-1]->init_output_blocking(m,c)) { error("PIPE, init output blocking mode for node %d\n", i); return -1; } // ------------------------------------------------------------ } } if (curr_multi_standard) { ff_node* a2a = nodes_list[i]; const svector& W1 = isa2a_getfirstset(a2a); assert(W1.size()>0); if (prev_single_standard) { if (W1.size()>1) { error("PIPE, cannot connect stage %d with stage %d because of different cardinality\n", i-1, i); return -1; } if (a2a->create_input_buffer(in_buffer_entries, fixedsizeIN)<0) return -1; svector w(1); nodes_list[i]->get_in_nodes(w); assert(w.size() == 1); if (nodes_list[i-1]->set_output_buffer(w[0]->get_in_buffer())<0) return -1; // blocking stuff -------------------------------------------- if (!nodes_list[i]->init_input_blocking(m,c)) { error("PIPE, init input blocking mode for node %d\n", i); return -1; } nodes_list[i-1]->set_output_blocking(m,c); if (!nodes_list[i-1]->init_output_blocking(m,c)) { error("PIPE, init output blocking mode for node %d\n", i-1); return -1; } // ----------------------------------------------------------- } else { if (prev_single_multioutput) { if (a2a->create_input_buffer(in_buffer_entries, fixedsizeIN)<0) return -1; svector w(MAX_NUM_THREADS); nodes_list[i]->get_in_nodes(w); if (nodes_list[i-1]->set_output(w)<0) return -1; // blocking stuff -------------------------------------------- if (!a2a->init_input_blocking(m,c)) return -1; if (!nodes_list[i-1]->init_output_blocking(m,c)) { error("PIPE, init output blocking mode for node %d\n", i-1); return -1; } // ----------------------------------------------------------- } else { assert(prev_multi_multioutput || prev_multi_standard); svector w1(1); nodes_list[i-1]->get_out_nodes(w1); assert(w1.size()); // here we pretend to have point-to-point connections if (w1.size() != W1.size()) { error("PIPE, cannot connect stage %d with stage %d, because of different input/output cardinality (**)\n", i-1, i); return -1; } if (a2a->create_input_buffer(in_buffer_entries, fixedsizeIN)<0) return -1; // the previous node can be a farm or a all2all for(size_t i=0;iset_output(W1[i])<0) return -1; } else { if (w1[i]->set_output_buffer(W1[i]->get_in_buffer())<0) return -1; } } //if (nodes_list[i-1]->set_output(w1)<0) return -1; // blocking stuff -------------------------------------------- svector w(MAX_NUM_THREADS); nodes_list[i-1]->get_out_nodes(w); assert(w.size()); for(size_t i=0;iinit_input_blocking(m,c)) return -1; w[i]->set_output_blocking(m,c); if (!w[i]->init_output_blocking(m,c)) return -1; } // ----------------------------------------------------------- } } } // it could be an all-to-all or a pipeline containing as first stage an all-to-all if (curr_multi_multiinput) { ff_node* a2a = get_node(i); assert(a2a->isAll2All()); const svector& W1 = isa2a_getfirstset(a2a); assert(W1.size()>0); if (prev_single_standard) { if (W1.size()>1) { error("PIPE, cannot connect stage %d with stage %d\n", i-1, i); return -1; } if (a2a->create_input_buffer(in_buffer_entries, fixedsizeIN)<0) return -1; svector w(1); nodes_list[i]->get_in_nodes(w); assert(w.size() == 1); if (nodes_list[i-1]->set_output_buffer(w[0]->get_in_buffer())<0) return -1; } if (prev_single_multioutput) { for(size_t j=0;j w(MAX_NUM_THREADS); W1[j]->get_in_nodes(w); for(size_t k=0;kset_input(t); nodes_list[i-1]->set_output(t); } } } if (prev_multi_standard) { svector w(MAX_NUM_THREADS); nodes_list[i-1]->get_out_nodes(w); assert(w.size()); // here we pretend to have point-to-point connections if (w.size() != W1.size()) { error("PIPE, cannot connect stage %d with stage %d, because of different input/output cardinality (**)\n", i-1, i); return -1; } w.clear(); if (nodes_list[i-1]->create_output_buffer(in_buffer_entries, fixedsizeOUT)<0) return -1; nodes_list[i-1]->get_out_nodes(w); assert(w.size()== W1.size()); for(size_t j=0;jset_input(w[j]); } } if (prev_multi_multioutput) { svector w(MAX_NUM_THREADS); nodes_list[i-1]->get_out_nodes(w); assert(w.size()); for(size_t k=0;kset_input(t); w[j]->set_output(t); } } } // blocking stuff -------------------------------------------- if (prev_multi_standard) { svector w(MAX_NUM_THREADS); nodes_list[i-1]->get_out_nodes(w); assert(w.size()); for(size_t i=0;iinit_input_blocking(m,c)) return -1; w[i]->set_output_blocking(m,c); if (!w[i]->init_output_blocking(m,c)) return -1; } } else { if (!a2a->init_input_blocking(m,c)) { error("PIPE, init input blocking mode for node %d\n", i); return -1; } if (!nodes_list[i-1]->init_output_blocking(m,c)) { error("PIPE, init output blocking mode for node %d\n", i-1); return -1; } } // ----------------------------------------------------------- } } // Preparation of buffers for the accelerator int ret = 0; if (has_input_channel) { if (create_input_buffer(in_buffer_entries, fixedsizeIN)<0) { error("PIPE, creating input buffer for the accelerator\n"); ret=-1; } else { if (get_out_buffer()) { error("PIPE, output buffer already present for the accelerator\n"); ret=-1; } else { // NOTE: the last buffer is forced to be unbounded | if (create_output_buffer(out_buffer_entries, false)<0) { error("PIPE, creating output buffer for the accelerator\n"); ret = -1; } } } pthread_mutex_t *m = NULL; pthread_cond_t *c = NULL; // set blocking input for the first stage (cons_m,...) if (!init_input_blocking(m,c)) { error("PIPE, init input blocking mode for accelerator\n"); } // set my pointers to the first stage input blocking stuff ff_node::set_output_blocking(m,c); m=NULL,c=NULL; // set my blocking output (prod_m, ....) if (!ff_node::init_output_blocking(m,c)) { error("PIPE, init output blocking mode for accelerator\n"); } m=NULL,c=NULL; // pipeline's first stage blocking output stuff (prod_m, ....) if (!init_output_blocking(m,c)) { error("FARM, add_collector, init input blocking mode for accelerator\n"); } m=NULL,c=NULL; // set my blocking input (cons_m, ....) if (!ff_node::init_input_blocking(m,c)) { error("PIPE, init input blocking mode for accelerator\n"); } // give pointers to my blocking input to the last pipeline stage (p_cons_m,...) set_output_blocking(m,c); } prepared=true; return ret; } int freeze_and_run(bool skip_init=false) { int nstages=static_cast(nodes_list.size()); if (!skip_init) { #if defined(FF_INITIAL_BARRIER) if (initial_barrier) { // set the initial value for the barrier if (!barrier) barrier = new BARRIER_T; //const int nthreads = cardinality(barrier); if (nthreads > MAX_NUM_THREADS) { error("PIPE, too much threads, increase MAX_NUM_THREADS !\n"); return -1; } barrier->barrierSetup(nthreads); } #endif // only the first stage has to skip the first pop skipfirstpop(!has_input_channel); } if (!prepared) if (prepare()<0) return -1; ssize_t startid = (get_my_id()>0)?get_my_id():0; for(ssize_t i=0;iset_id(i+startid); assert(blocking_in == blocking_out); nodes_list[i]->blocking_mode(blocking_in); if (nodes_list[i]->freeze_and_run(true)<0) { error("ERROR: PIPE, (frbbeezing and) running stage %d\n", i); return -1; } } return 0; } public: /** * \brief Constructor * * \param input_ch \p true set accelerator mode * \param in_buffer_entries input queue length * \param out_buffer_entries output queue length * \param fixedsize \p true uses bound channels (SPSC queue) */ explicit ff_pipeline(bool input_ch=false, int in_buffer_entries=DEFAULT_BUFFER_CAPACITY, int out_buffer_entries=DEFAULT_BUFFER_CAPACITY, bool fixedsize=FF_FIXED_SIZE): has_input_channel(input_ch), node_cleanup(false),fixedsizeIN(fixedsize),fixedsizeOUT(fixedsize), in_buffer_entries(in_buffer_entries), out_buffer_entries(out_buffer_entries) { } ff_pipeline(const ff_pipeline& p) : ff_node(p) { if (p.prepared) { error("ff_pipeline, copy constructor, the input pipeline is already prepared\n"); return; } has_input_channel = p.has_input_channel; node_cleanup = p.node_cleanup; fixedsizeIN = p.fixedsizeIN; fixedsizeOUT = p.fixedsizeOUT; wraparound = p.wraparound; in_buffer_entries = p.in_buffer_entries; out_buffer_entries = p.out_buffer_entries; nodes_list = p.nodes_list; internalSupportNodes = p.internalSupportNodes; dontcleanup = p.dontcleanup; // this is a dirty part, we modify a const object..... ff_pipeline *dirty= const_cast(&p); dirty->node_cleanup = false; dirty->internalSupportNodes.resize(0); dirty->dontcleanup.resize(0); } /** * \brief Destructor */ virtual ~ff_pipeline() { if (barrier) delete barrier; if (node_cleanup) { while(nodes_list.size()>0) { ff_node *n = nodes_list.back(); bool found = false; for(size_t i=0;i int add_stage(T *s, bool cleanup=false) { nodes_list.push_back(s); if (cleanup) internalSupportNodes.push_back(s); return 0; } template int add_stage(const T& s) { ff_node *newstage = new T(s); if (!newstage) { error("add_stage not enough memory\n"); return -1; } return add_stage(newstage, true); } /* remove one stage already inserted into the pipeline. * NOTE: The node is not deleted if the pipeline cleanup flag is set. * NOTE: If remove_from_cleanuplist is true the node is removed (if it is present) * from the list of internalSupportNodes. * */ void remove_stage(int pos, bool remove_from_cleanuplist=false) { if (prepared) { error("PIPE, remove_stage, stage %d cannot be removed because the PIPE has already been prepared\n"); return; } if (pos<0 || pos>static_cast(nodes_list.size())) { error("PIPE, remove_stage, stage number %d does not exist\n", pos); return; } svector::iterator it=nodes_list.begin(); assert(it+pos < nodes_list.end()); if (remove_from_cleanuplist) { ff_node* node = nodes_list[pos]; int pos2=-1; for(size_t i=0;i=0) internalSupportNodes.erase(internalSupportNodes.begin()+pos2); } nodes_list.erase(it+pos); } void insert_stage(int pos, ff_node* node, bool cleanup=false) { if (prepared) { error("PIPE, insert_stage, stage %d cannot be added because the PIPE has already been prepared\n"); return; } if (pos<0 || pos>static_cast(nodes_list.size())) { error("PIPE, insert_stage, invalid position\n"); return; } svector::iterator it=nodes_list.begin(); assert(it+pos <= nodes_list.end()); nodes_list.insert(it+pos, node); if (cleanup) internalSupportNodes.push_back(node); } ssize_t get_stageindex(const ff_node* stage){ if (!stage) return -1; for(ssize_t i=0; i<(ssize_t)nodes_list.size(); ++i) if(nodes_list[i] == stage) return i; return -1; } // returns true if the old node has been changed with the new one // false in case of error of if the old node has not been found bool change_node(ff_node* old, ff_node* n, bool cleanup=false, bool remove_from_cleanuplist=false) { if (prepared) { error("PIPE, change_node cannot be called because the PIPE has already been prepared\n"); return false; } for(size_t i=0; i> get_and_remove_nodes() { int nstages=static_cast(this->nodes_list.size()); svector> newvector; for(int i=0;iisPipe()) { ff_pipeline * p = reinterpret_cast(nodes_list[i]); const svector>& W = p->get_and_remove_nodes(); newvector+=W; if (node_cleanup) { bool found=false; for(size_t i=0;iremove_stage(0); return newvector; } /** * \brief Feedback channel (pattern modifier) * * The last stage output stream will be connected to the first stage * input stream in a cycle (feedback channel) */ int wrap_around() { wraparound=true; return 0; }; bool isset_wraparound() { return wraparound; } bool isset_cleanup_nodes() const { return node_cleanup; } inline void cleanup_nodes(bool onoff=true) { node_cleanup = onoff; } /** * returns the stages added to the pipeline */ const svector& getStages() const { return nodes_list; } /** * \brief returns all nodes of the pipeline, where stages are not pipeline. * In the list returned, no single stage is a pipeline. */ const svector get_pipeline_nodes() const { const int nstages=static_cast(nodes_list.size()); svector newvector; for (int i=0;iisPipe()) { ff_pipeline * p = reinterpret_cast(nodes_list[i]); const svector& tmp = p->get_pipeline_nodes(); newvector+=tmp; } else newvector.push_back(nodes_list[i]); } return newvector; }; /** * \brief returns the stage i of the pipeline. If the stage is a pipeline * the function is called recursively extracting its first stage. */ ff_node* get_node(int i) const { if (!nodes_list.size()) return nullptr; if (i<0 || i>=(int)nodes_list.size()) return nullptr; if (nodes_list[i]->isPipe()) { ff_pipeline * p = reinterpret_cast(nodes_list[i]); return p->get_node(0); } return nodes_list[i]; } /** * \brief returns the stage i of the pipeline. If the stage is a pipeline * the function is called recursively extracting its last stage. */ ff_node* get_node_last(int i) const { if (!nodes_list.size()) return nullptr; if (i<0 || i>=(int)nodes_list.size()) return nullptr; if (nodes_list[i]->isPipe()) { ff_pipeline * p = reinterpret_cast(nodes_list[i]); return p->get_lastnode(); } return nodes_list[i]; } /** * \brief returns the last stage of the pipeline recursively. */ ff_node* get_lastnode() const { if (!nodes_list.size()) return nullptr; const int last = static_cast(nodes_list.size())-1; if (nodes_list[last]->isPipe()) { ff_pipeline * p = reinterpret_cast(nodes_list[last]); return p->get_lastnode(); } return nodes_list[last]; } /** * \brief returns the last stage of the pipeline. */ ff_node* get_laststage() const { if (!nodes_list.size()) return nullptr; const int last = static_cast(nodes_list.size())-1; return nodes_list[last]; } /** * \brief returns the first stage of the pipeline. */ ff_node* get_firststage() const { if (!nodes_list.size()) return nullptr; return nodes_list[0]; } ff_node* get_nextstage(const ff_node* s){ ssize_t index = get_stageindex(s); if (index == -1 || (index+1) == (ssize_t)nodes_list.size()) return nullptr; return nodes_list[index+1]; } ff_node* get_prevstage(const ff_node* s){ ssize_t index = get_stageindex(s); if (index <= 0) return nullptr; return nodes_list[index-1]; } inline void get_out_nodes(svector&w) { assert(nodes_list.size()>0); int last = static_cast(nodes_list.size())-1; nodes_list[last]->get_out_nodes(w); } inline void get_out_nodes_feedback(svector&w) { assert(nodes_list.size()>0); int last = static_cast(nodes_list.size())-1; nodes_list[last]->get_out_nodes_feedback(w); } inline void get_in_nodes(svector&w) { assert(nodes_list.size()>0); nodes_list[0]->get_in_nodes(w); } void skipfirstpop(bool sk) { get_node(0)->skipfirstpop(sk); for(size_t i=1;iskipfirstpop(false); } #ifdef DFF_ENABLED void skipallpop(bool sk) { get_node(0)->skipallpop(sk); } #endif /* WARNING: these methods must be called before the run() method */ void blocking_mode(bool blk=true) { blocking_in = blocking_out = blk; } void no_barrier() { initial_barrier = false; } void no_mapping() { default_mapping = false; } /** * \brief Run the pipeline skeleton asynchronously * * Run the pipeline, the method call return immediately. To be coupled with * \ref ff_pipeline::wait() */ int run(bool skip_init=false) { int nstages=static_cast(nodes_list.size()); if (!skip_init) { #if defined(MAMMUT) mammut::energy::Energy *e = mammut.getInstanceEnergy(); mammutcounter = e->getCounter(); if (mammutcounter) { mammutcounter->reset(); printf("Starting Joules = %g\n", mammutcounter->getJoules()); } #endif #if defined(FF_INITIAL_BARRIER) if (initial_barrier) { // set the initial value for the barrier if (!barrier) barrier = new BARRIER_T; //const int nthreads = cardinality(barrier); printf("nthreads = %d\n", nthreads); if (nthreads > MAX_NUM_THREADS) { error("PIPE, too much threads, increase MAX_NUM_THREADS !\n"); return -1; } barrier->barrierSetup(nthreads); } #endif #ifdef FF_OPENCL // TODO: check if the pipeline has at least 1 oclNode. // setup openCL environment clEnvironment::instance(); #if 0 // REMOVE THIS ? // check if we have to setup the OpenCL environment ! if (fftree_ptr->hasOpenCLNode()) { // setup openCL environment clEnvironment::instance(); } #endif #endif // only the first stage has to skip the first pop skipfirstpop(!has_input_channel); } if (!prepared) if (prepare()<0) return -1; ssize_t startid = (get_my_id()>0)?get_my_id():0; for(int i=0;i0) startid += nodes_list[i-1]->cardinality(); nodes_list[i]->set_id(startid); assert(blocking_in == blocking_out); nodes_list[i]->blocking_mode(blocking_in); if (!default_mapping) nodes_list[i]->no_mapping(); if (nodes_list[i]->run(true)<0) { error("ERROR: PIPE, running stage %d\n", i); return -1; } } return 0; } int dryrun() { if (!prepared) if (prepare()<0) return -1; return 0; } /** * \relates ff_pipe * \brief run the pipeline, waits that all stages received the End-Of-Stream (EOS), * and destroy the pipeline run-time * * Blocking behaviour w.r.t. main thread to be clarified */ #ifdef DFF_ENABLED int run_and_wait_end(); #else int run_and_wait_end() { if (isfrozen()) { // TODO error("PIPE: Error: feature not yet supported\n"); return -1; } stop(); if (run()<0) return -1; if (wait()<0) return -1; return 0; } #endif /** * \related ff_pipe * \brief run the pipeline, waits that all stages received the End-Of-Stream (EOS), * and suspend the pipeline run-time * * Run-time threads are suspended by way of a distrubuted protocol. * The same pipeline can be re-started by calling again run_then_freeze */ int run_then_freeze(ssize_t nw=-1) { if (isfrozen()) { // true means that next time threads are frozen again thaw(true, nw); return 0; } if (!prepared) if (prepare()<0) return -1; //freeze(); //return run(); /* freeze_and_run is required because in the pipeline * there isn't no manager thread, which allows to freeze other * threads before starting the computation */ return freeze_and_run(false); } /** * \brief wait for pipeline termination (all stages received EOS) */ int wait(/* timeval */ ) { int ret=0; for(unsigned int i=0;iwait()<0) { error("PIPE, waiting stage thread, id = %d\n",nodes_list[i]->get_my_id()); ret = -1; } #if defined(MAMMUT) if (mammutcounter) joules = mammutcounter->getJoules(); #endif return ret; } int wait_last() { int ret=-1; const int last = static_cast(nodes_list.size())-1; if (last<0) return ret; if (nodes_list[0]->wait()<0) return ret; return nodes_list[last]->wait(); } /** * \brief wait for pipeline to complete and suspend (all stages received EOS) * * */ inline int wait_freezing(/* timeval */ ) { int ret=0; for(unsigned int i=0;iwait_freezing()<0) { error("PIPE, waiting freezing of stage thread, id = %d\n", nodes_list[i]->get_my_id()); ret = -1; } return ret; } inline void stop() { for(unsigned int i=0;istop(); } inline void freeze() { for(unsigned int i=0;ifreeze(); } /** * \brief checks if the pipeline is still running or not * */ inline bool done() const { int nstages=static_cast(nodes_list.size()); for(int i=0;idone()) return false; return true; } inline bool isPipe() const { return true; } inline bool isPrepared() const { return prepared;} inline bool isMultiInput() const { if (nodes_list.size()==0) return false; return nodes_list[0]->isMultiInput(); } inline bool isMultiOutput() const { if (nodes_list.size()==0) return false; int last = static_cast(nodes_list.size())-1; return nodes_list[last]->isMultiOutput(); } // remove internal pipeline // WARNING: if there are feedback channels calling this method is dangerous! void flatten() { const svector>& W = this->get_and_remove_nodes(); assert(get_pipeline_nodes().size() == 0); for(size_t i=0;iadd_stage(W[i].first); if (node_cleanup) { for(size_t i=0;ipush(task)) { pthread_cond_signal(p_cons_c); return true; } struct timespec tv; timedwait_timeout(tv); pthread_mutex_lock(prod_m); pthread_cond_timedwait(prod_c, prod_m, &tv); pthread_mutex_unlock(prod_m); goto _retry; } for(unsigned long i=0;ipush(task)) return true; losetime_out(ticks); } return false; } /** * \brief gets a result from a task to the pipeline from the main thread * (accelator mode) * * Total call: return when a result is available. To be used in accelerator mode only * * \param[out] task * \param retry number of attempts to get a result before failing * (related to nonblocking get from channel - expert use only) * \param ticks number of clock cycles between successive attempts * (related to nonblocking get from channel - expert use only) * \return \p true is a task is returned, \p false if End-Of-Stream (EOS) */ inline bool load_result(void ** task, unsigned long retry=((unsigned long)-1), unsigned long ticks=ff_node::TICKS2WAIT) { FFBUFFER * outbuffer = get_out_buffer(); if (!outbuffer) { if (!has_input_channel) error("PIPE: accelerator is not set, offload not available"); else error("PIPE: output buffer not created"); return false; } if (ff_node::blocking_in) { _retry: if (outbuffer->pop(task)) { if ((*task != (void *)FF_EOS)) return true; else return false; } struct timespec tv; timedwait_timeout(tv); pthread_mutex_lock(cons_m); pthread_cond_timedwait(cons_c, cons_m, &tv); pthread_mutex_unlock(cons_m); goto _retry; } for(unsigned long i=0;ipop(task)) { if ((*task != (void *)FF_EOS)) return true; else return false; } losetime_in(ticks); } return false; } /** * \brief try to get a result from a task to the pipeline from the main thread * (accelator mode) * * Partial call: can return no result. To be used in accelerator mode only * * \param[out] task * \return \p true is a task is returned (including EOS), * \p false if no task is returned */ inline bool load_result_nb(void ** task) { FFBUFFER * outbuffer = get_out_buffer(); if (outbuffer) { if (outbuffer->pop(task)) return true; else return false; } if (!has_input_channel) error("PIPE: accelerator is not set, offload not available"); else error("PIPE: output buffer not created"); return false; } int cardinality() const { int card=0; for(unsigned int i=0;icardinality(); return card; } /* * \brief Misure execution time (including init and finalise) * * \return pipeline execution time (including init and finalise) */ double ffTime() { return diffmsec(nodes_list[nodes_list.size()-1]->getstoptime(), nodes_list[0]->getstarttime()); } /* * \brief Misure execution time (excluding init and finalise) * * \return pipeline execution time (excluding runtime setup) */ double ffwTime() { return diffmsec(nodes_list[nodes_list.size()-1]->getwstoptime(), nodes_list[0]->getwstartime()); } #if defined(TRACE_FASTFLOW) void ffStats(std::ostream & out) { out << "--- pipeline:\n"; for(unsigned int i=0;iffStats(out); #if defined(MAMMUT) out << "Joules: " << joules << "\n"; #endif } #else void ffStats(std::ostream & out) { out << "FastFlow trace not enabled\n"; } #endif #ifdef DFF_ENABLED virtual bool isSerializable(){ svector outputs; this->get_out_nodes(outputs); for(ff_node* output: outputs) if (!output->isSerializable()) return false; return true; } virtual bool isDeserializable(){ svector inputs; this->get_in_nodes(inputs); for(ff_node* input: inputs) if(!input->isDeserializable()) return false; return true; } #endif protected: int cardinality(BARRIER_T * const barrier) { int card=0; for(unsigned int i=0;icardinality(barrier); return card; } /* The pipeline has not been flattened and its first stage is a multi-input node used as * a standard node. */ inline bool put(void * ptr) { return nodes_list[0]->put(ptr); } inline FFBUFFER * get_in_buffer() const { return nodes_list[0]->get_in_buffer(); } // returns the pipeline starting time const struct timeval startTime() { return nodes_list[0]->getstarttime(); } void* svc(void *) { return NULL; } int svc_init() { return -1; }; void svc_end() {} void setAffinity(int) { error("PIPE, setAffinity: cannot set affinity for the pipeline\n"); } int getCPUId() const { return -1;} inline void thaw(bool _freeze=false, ssize_t nw=-1) { for(unsigned int i=0;ithaw(_freeze, nw); } inline bool isfrozen() const { int nstages=static_cast(nodes_list.size()); for(int i=0;iisfrozen()) return false; return true; } // consumer virtual inline bool init_input_blocking(pthread_mutex_t *&m, pthread_cond_t *&c, bool /*feedback*/=true) { return nodes_list[0]->init_input_blocking(m,c); } // producer virtual inline bool init_output_blocking(pthread_mutex_t *&m, pthread_cond_t *&c, bool /*feedback*/=true) { const int last = static_cast(nodes_list.size())-1; if (last<0) return false; return nodes_list[last]->init_output_blocking(m,c); } virtual inline void set_output_blocking(pthread_mutex_t *&m, pthread_cond_t *&c, bool canoverwrite=false) { const int last = static_cast(nodes_list.size())-1; if (last<0) return; nodes_list[last]->set_output_blocking(m,c, canoverwrite); } virtual inline pthread_cond_t &get_cons_c() { return nodes_list[0]->get_cons_c();} int create_input_buffer(int nentries, bool fixedsize) { if (in) return -1; if (nodes_list[0]->create_input_buffer(nentries, fixedsize)<0) { error("PIPE, creating input buffer for node 0\n"); return -1; } if (!nodes_list[0]->isMultiInput() || get_node(0)->isFarm()) ff_node::set_input_buffer(get_node(0)->get_in_buffer()); return 0; } int create_output_buffer(int nentries, bool fixedsize=false) { int last = static_cast(nodes_list.size())-1; if (last<0) return -1; if (nodes_list[last]->create_output_buffer(nentries, fixedsize)<0) { error("PIPE, creating output buffer for node %d\n",last); return -1; } ff_node::set_output_buffer(nodes_list[last]->get_out_buffer()); return 0; } int set_output_buffer(FFBUFFER * const o) { int last = static_cast(nodes_list.size())-1; if (last<0) return -1; if (nodes_list[last]->set_output_buffer(o)<0) { error("PIPE, setting output buffer for node %d\n",last); return -1; } return 0; } inline int set_input(ff_node *node) { return nodes_list[0]->set_input(node); } inline int set_input(const svector & w) { return nodes_list[0]->set_input(w); } inline int set_input_feedback(ff_node *node) { return nodes_list[0]->set_input_feedback(node); } inline int set_output(ff_node *node) { int last = static_cast(nodes_list.size())-1; return nodes_list[last]->set_output(node); } inline int set_output(const svector &w) { int last = static_cast(nodes_list.size())-1; return nodes_list[last]->set_output(w); } inline int set_output_feedback(ff_node *node) { int last = static_cast(nodes_list.size())-1; if (nodes_list[last]->isMultiOutput()) return nodes_list[last]->set_output_feedback(node); assert(node->get_out_buffer()); return nodes_list[last]->set_output_buffer(node->get_out_buffer()); } inline int ondemand_buffer() const { int last = static_cast(nodes_list.size())-1; svector w; nodes_list[last]->get_out_nodes(w); return w[0]->ondemand_buffer(); // NOTE: we suppose that all others are the same !!!!! } private: bool has_input_channel; // for accelerator bool node_cleanup; bool fixedsizeIN, fixedsizeOUT; bool wraparound=false; int in_buffer_entries; int out_buffer_entries; svector nodes_list; svector internalSupportNodes; svector dontcleanup; // used by the flatten method #if defined(MAMMUT) mammut::Mammut mammut; mammut::energy::Joules joules = -1; mammut::energy::Counter* mammutcounter = nullptr; #endif }; //#ifndef WIN32 //VS12 // ------------------------ high-level (simpler) pipeline ------------------ #if ((__cplusplus >= 201103L) || (defined __GXX_EXPERIMENTAL_CXX0X__)) || (defined(HAS_CXX11_VARIADIC_TEMPLATES)) #include /*! * \class ff_Pipe * \ingroup high_level_patterns * * \brief Pipeline pattern (high-level pattern syntax) * * Set up a parallel for pipeline pattern run-time support object. * Run with \p run_and_wait_end or \p run_the_freeze. See related functions. * * \note Don't use to model a workflow of tasks, stages are nonblocking threads * and * require one core per stage. If you need to model a workflow use \ref ff::ff_mdf * * \example pipe_basic.cpp */ template class ff_Pipe: public ff_pipeline { private: #if !defined(__CUDACC__) && !defined(WIN32) && !defined(__ICC) // // Thanks to Suter Toni (HSR) for suggesting the following code for checking // correct input-output types ordering. // template struct valid_stage_types : std::true_type {}; template struct valid_stage_types : std::integral_constant{} && valid_stage_types{}> {}; template struct valid_stage_types&&, std::unique_ptr&&, Bs &&...> : std::integral_constant{} && valid_stage_types, Bs...>{}> {}; template struct valid_stage_types&&, B&&, Bs &&...> : std::integral_constant{} && valid_stage_types{}> {}; template struct valid_stage_types&&, Bs &&...> : std::integral_constant{} && valid_stage_types, Bs...>{}> {}; //struct valid_stage_types : std::integral_constant{} && valid_stage_types{}> {}; #endif // // Thanks to Peter Sommerlad for suggesting the following simpler code // void add2pipeall(){} // base case // need to see this before add2pipeall variadic template function inline void add2pipe(ff_node &node) { ff_pipeline::add_stage(&node); } // need to see this before add2pipeall variadic template function inline void add2pipe(ff_node *node) { ff_pipeline::add_stage(node); } template void add2pipeall(FIRST &stage,ARGS&...args){ add2pipe(stage); add2pipeall(args...); // recurse } template void add2pipeall(const FIRST &stage,ARGS&...args){ FIRST *f = new FIRST(stage); add2pipe(f); add2pipeall(args...); // recurse } template void add2pipeall(std::unique_ptr & stage,ARGS&...args){ ff_node* node = stage.release(); add2pipe(*node); //stage.release()); cleanup_stages.push_back(node); add2pipeall(args...); // recurse } protected: svector cleanup_stages; public: // NOTE: The ff_Pipe accepts as stages either l-value references or std::unique_ptr l-value references. // The ownership of the (unique) pointer stage is transferred to the pipeline !!!! typedef IN_t in_type; typedef OUT_t out_type; /** * \brief Create a stand-alone pipeline (no input/output streams). Run with \p run_and_wait_end or \p run_the_freeze. * * Identifies an stream parallel construct in which stages are executed * in parallel. * It does require a stream of tasks, either external of created by the * first stage. * \param stages pipeline stages * * Example: \ref pipe_basic.cpp */ template ff_Pipe(STAGES &&...stages) { // forwarding reference (aka universal reference) #if !defined(__CUDACC__) && !defined(WIN32) && !defined(__ICC) static_assert(valid_stage_types{}, "Input & output types of the pipe's stages don't match"); #endif this->add2pipeall(stages...); //this->add2pipeall(std::forward(stages)...); } /** * \brief Create a pipeline (with input stream). Run with \p run_and_wait_end or \p run_the_freeze. * * Identifies an stream parallel construct in which stages are executed * in parallel. * It does require a stream of tasks, either external of created by the * first stage. * \param input_ch \p true to enable first stage input stream * \param stages pipeline stages * * Example: \ref pipe_basic.cpp */ template explicit ff_Pipe(bool input_ch, STAGES &&...stages):ff_pipeline(input_ch) { #if !defined(__CUDACC__) && !defined(WIN32) && !defined(__ICC) static_assert(valid_stage_types{}, "Input & output types of the pipe's stages don't match"); #endif this->add2pipeall(stages...); } ~ff_Pipe() { for (auto s: cleanup_stages) delete s; } operator ff_node* () { return this;} bool load_result(OUT_t *&task, unsigned long retry=((unsigned long)-1), unsigned long ticks=ff_node::TICKS2WAIT) { return ff_pipeline::load_result((void**)&task, retry,ticks); } // deleted members bool load_result(void ** task, unsigned long retry=((unsigned long)-1), unsigned long ticks=ff_node::TICKS2WAIT) = delete; /* * using the following two add_stage method, no static check on the input/output * types is executed */ int add_stage(ff_node &s) { add2pipe(s); return 0; } int add_stage(std::unique_ptr &&s) { add2pipe(s.release()); return 0; } // deleted functions int add_stage(ff_node * s) = delete; void cleanup_nodes() = delete; }; #endif /* HAS_CXX11_VARIADIC_TEMPLATES */ //#endif //VS12 } // namespace ff // to avoid warning when optimize_static is not used #include #endif /* FF_PIPELINE_HPP */