/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /*! * \link * \file multinode.hpp * \ingroup building_blocks * * \brief FastFlow ff_minode ff_monode and typed versions. * * @detail FastFlow multi-input and multi-output nodes. * */ #ifndef FF_MULTINODE_HPP #define FF_MULTINODE_HPP /* *************************************************************************** * * 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. * **************************************************************************** */ #include #include #include namespace ff { /* This file provides the following classes: * ff_minode * ff_monode * ff_minode_t (typed version of the ff_minode -- requires c++11) * ff_monode_t (typed version of the ff_monode -- requires c++11) * */ /* ************************* Multi-Input node ************************* */ /*! * \ingroup building_blocks * * \brief Multiple input ff_node (the SPMC mediator) * * The ff_node with many input channels. * * This class is defined in \ref farm.hpp */ class ff_minode: public ff_node { friend class ff_farm; friend class ff_comb; protected: /** * \brief Gets the number of input channels */ inline int cardinality(BARRIER_T * const barrier) { this->set_barrier(barrier); return 1; } /** * \brief Creates the input channels * * This function may be called because the multi-input node is * used just as a standard node (for example as a farm's worker). * * \return >=0 if successful, otherwise -1 is returned. */ int create_input_buffer(int nentries, bool fixedsize=FF_FIXED_SIZE) { assert(inputNodes.size() == 0); ff_node* t = new ff_buffernode(nentries,fixedsize); t->set_id(-1); internalSupportNodes.push_back(t); set_input(t); return ff_node::set_input_buffer(t->get_in_buffer()); } /* The multi-input node is used as a standard node. */ inline bool put(void * ptr) { assert(inputNodes.size() == 1); return inputNodes[0]->put(ptr); } inline FFBUFFER *get_in_buffer() const { if (inputNodes.size() == 0) return nullptr; assert(inputNodes.size() == 1); return inputNodes[0]->get_in_buffer(); } int dryrun() { if (prepared) return 0; for(size_t i=0;iregister_worker(inputNodesFeedback[i]); if (inputNodesFeedback.size()>0) gt->set_feedbackid_threshold(inputNodesFeedback.size()); for(size_t i=0;iregister_worker(inputNodes[i]); if (gt->dryrun()<0) return -1; return 0; } int prepare() { if (prepared) return 0; if (ff_minode::dryrun()<0) return -1; prepared=true; return 0; } void blocking_mode(bool blk=true) { blocking_in = blocking_out = blk; gt->blocking_mode(blk); } template int all_gather(T* in, T** V) { return gt->all_gather(in,(void**)V); } void registerAllGatherCallback(int (*cb)(void *,void **,void*), void * arg) { gt->registerAllGatherCallback(cb,arg); } // consumer virtual inline bool init_input_blocking(pthread_mutex_t *&m, pthread_cond_t *&c, bool feedback=true) { bool r = gt->init_input_blocking(m,c, feedback); if (!r) return false; // NOTE: for all registered input node (or buffernode) we have to set the // blocking stuff both for input nodes as well as "feedback nodes" for(size_t i=0;iset_output_blocking(m,c); if (feedback) { for(size_t i=0;iset_output_blocking(m,c); } return true; } // producer virtual inline bool init_output_blocking(pthread_mutex_t *&m, pthread_cond_t *&c, bool feedback=true) { // This is a multi-input node, so it has only one output channel // unless it is a combine node whose right part is a multi-output // node. If this is not the case, we have to initialize the // local-node output blocking ff_node* filter = gt->get_filter(); if (filter && ( (filter->get_out_buffer()!=nullptr) || filter->isMultiOutput() ) ) { // see gt.hpp return filter->init_output_blocking(m, c, feedback); } return ff_node::init_output_blocking(m,c, feedback); //return gt->init_output_blocking(m,c); } virtual inline void set_output_blocking(pthread_mutex_t *&m, pthread_cond_t *&c, bool canoverwrite=false) { ff_node* filter = gt->get_filter(); if (filter && ( (filter->get_out_buffer()!=nullptr) || filter->isMultiOutput() ) ) { // see gt.hpp filter->set_output_blocking(m, c, canoverwrite); } //gt->set_output_blocking(m,c); ff_node::set_output_blocking(m,c, canoverwrite); } inline pthread_cond_t &get_cons_c() { return gt->get_cons_c(); } virtual inline void get_in_nodes(svector&w) { size_t len=w.size(); // it is possible that the multi-input node is register // as collector of farm if (inputNodes.size() == 0 && gt->getNWorkers()>0) { w += gt->getWorkers(); } w += inputNodes; if (len == w.size()) w.push_back(this); } virtual void get_in_nodes_feedback(svector&w) { w += inputNodesFeedback; } virtual inline void get_out_nodes(svector&w) { w.push_back(this); } public: /** * \brief Constructor */ ff_minode(int max_num_workers=DEF_MAX_NUM_WORKERS): ff_node(), gt(new ff_gatherer(max_num_workers)),myowngt(true) { } ff_minode(ff_node *filter, int max_num_workers=DEF_MAX_NUM_WORKERS): ff_node(), gt(new ff_gatherer(max_num_workers)),myowngt(true) { if (filter == nullptr) { delete gt; gt = nullptr; return; } gt->set_filter(filter); } ff_minode(const ff_minode& n) : ff_node(n) { // here we re-initialize a new gatherer gt = new ff_gatherer(n.gt->max_nworkers); if (!gt) { error("ff_minode, not enough memory\n"); return; } if (n.gt->get_filter()) gt->set_filter(n.gt->get_filter()); myowngt=true; inputNodes=n.inputNodes; inputNodesFeedback=n.inputNodesFeedback; internalSupportNodes = n.internalSupportNodes; // this is a dirty part, we modify a const object..... ff_minode *dirty= const_cast(&n); dirty->internalSupportNodes.resize(0); } /** * \brief Destructor */ virtual ~ff_minode() { if (gt && myowngt) delete gt; for(size_t i=0;iset_filter(filter); } // used when a multi-input node is a filter of a collector or of a comp // it can also be used to set a particular gathering policy like for // example the ones pre-defined in the file ordering_policies.hpp void setgt(ff_gatherer *external_gt, bool cleanup=false) { if (myowngt) { delete gt; myowngt=false; } gt = external_gt; myowngt = cleanup; } inline void set_barrier(BARRIER_T * const barrier) { gt->set_barrier(barrier); } inline BARRIER_T* get_barrier() const { return gt->get_barrier(); } /** * \brief Assembly input channels * * Assembly input channelnames to ff_node channels */ virtual inline int set_input(const svector & w) { inputNodes += w; return 0; } void set_input_channelid(ssize_t id, bool fromin=true) { gt->set_input_channelid(id, fromin); } /** * \brief Assembly a input channel * * Assembly a input channelname to a ff_node channel */ virtual inline int set_input(ff_node *node) { inputNodes.push_back(node); return 0; } virtual inline int set_input_feedback(ff_node *node) { inputNodesFeedback.push_back(node); return 0; } virtual bool isMultiInput() const { return true;} void set_running(int r) { gt->running = r; } /** * \brief Skip first pop * * Set up spontaneous start */ inline void skipfirstpop(bool sk) { gt->skipfirstpop(sk); ff_node::skipfirstpop(sk); } #ifdef DFF_ENABLED inline void skipallpop(bool sk) { gt->skipallpop(sk); ff_node::skipallpop(sk); } #endif /** * \brief run * * \return 0 if successful, otherwise -1 is returned. * */ int run(bool=false) { if (!gt) return -1; if (gt->get_filter() == nullptr) gt->set_filter(this); if (!prepared) if (prepare()<0) return -1; if (ff_node::skipfirstpop()) gt->skipfirstpop(); if (!default_mapping) gt->no_mapping(); if (gt->run()<0) { error("ff_minode, running gather module\n"); return -1; } return 0; } int freeze_and_run(bool=false) { gt->freeze(); return run(); } int run_then_freeze(ssize_t nw=-1) { if (gt->isfrozen()) { // true means that next time threads are frozen again gt->thaw(true, nw); return 0; } gt->freeze(); return run(); } int wait(/* timeout */) { if (gt->wait()<0) return -1; return 0; } int wait_freezing() { return gt->wait_freezing(); } /** * \brief checks if the node is running * */ bool done() const { return gt->done(); } bool fromInput() const { return gt->fromInput(); } /** * \brief Gets the channel id from which the data has just been received * */ ssize_t get_channel_id() const { return gt->get_channel_id();} size_t get_num_inchannels() const { return gt->get_num_inchannels(); } size_t get_num_outchannels() const { if (gt->get_filter() == (ff_node*)this) return (gt->get_out_buffer()?1:0); return gt->get_num_outchannels(); } size_t get_num_feedbackchannels() const { return gt->get_num_feedbackchannels(); } /** * For a multi-input node the number of EOS to receive before terminating is equal to * the current number of input channels. */ ssize_t get_neos() const { return get_num_inchannels(); } /** * \internal * \brief Gets the gt * * It gets the internal gatherer. * * \return A pointer to the FastFlow gatherer. */ inline ff_gatherer *getgt() const { return gt;} const struct timeval getstarttime() const { return gt->getstarttime();} const struct timeval getstoptime() const { return gt->getstoptime();} const struct timeval getwstartime() const { return gt->getwstartime();} const struct timeval getwstoptime() const { return gt->getwstoptime();} #if defined(TRACE_FASTFLOW) /** * \brief Prints the FastFlow trace * * It prints the trace of FastFlow. */ inline void ffStats(std::ostream & out) { out << "--- multi-input:\n"; gt->ffStats(out); } #else void ffStats(std::ostream & out) { out << "FastFlow trace not enabled\n"; } #endif private: ff_gatherer* gt; bool myowngt; svector inputNodes; svector inputNodesFeedback; svector internalSupportNodes; }; /* ************************* Multi-Ouput node ************************* */ /*! * \ingroup building_blocks * * \brief Multiple output ff_node (the MPSC mediator) * * The ff_node with many output channels. * * This class is defined in \ref farm.hpp */ class ff_monode: public ff_node { friend class ff_a2a; protected: /** * \brief Cardinatlity * * Defines the cardinatlity of the FastFlow node. * * \param barrier defines the barrier * * \return 1 is always returned. */ inline int cardinality(BARRIER_T * const barrier) { this->set_barrier(barrier); return 1; } int create_output_buffer(int nentries, bool fixedsize=FF_FIXED_SIZE) { // this is needed for example when a worker of a farm is a multi-output node // (even without a feedback channel) if (ff_node::create_output_buffer(nentries,fixedsize) <0) return -1; ff_node *t = new ff_buffernode(-1, get_out_buffer(), get_out_buffer()); assert(t); internalSupportNodes.push_back(t); set_output(t); return 0; } int dryrun() { if (prepared) return 0; for(size_t i=0;iregister_worker(outputNodesFeedback[i]); if (outputNodesFeedback.size()>0) lb->set_feedbackid_threshold(outputNodesFeedback.size()); for(size_t i=0;iregister_worker(outputNodes[i]); if (lb->dryrun()<0) return -1; return 0; } int prepare() { if (prepared) return 0; if (ff_monode::dryrun()<0) return -1; prepared=true; return 0; } void propagateEOS(void*task=FF_EOS) { if (lb->getnworkers() == 0) ff_send_out(task); lb->propagateEOS(task); } void blocking_mode(bool blk=true) { blocking_in = blocking_out = blk; lb->blocking_mode(blk); } // consumer virtual inline bool init_input_blocking(pthread_mutex_t *&m, pthread_cond_t *&c, bool feedback=true) { return lb->init_input_blocking(m,c, feedback); } // producer virtual inline bool init_output_blocking(pthread_mutex_t *&m, pthread_cond_t *&c, bool feedback=true) { return lb->init_output_blocking(m,c, feedback); } virtual inline void set_output_blocking(pthread_mutex_t *&m, pthread_cond_t *&c, bool canoverwrite=false) { ff_node::set_output_blocking(m,c, canoverwrite); } virtual inline void set_cons_c(pthread_cond_t *c) { lb->set_cons_c(c); } virtual inline pthread_cond_t &get_cons_c() { return lb->get_cons_c();} public: /** * \brief Constructor * * \param max_num_workers defines the maximum number of workers * */ ff_monode(int max_num_workers=DEF_MAX_NUM_WORKERS): ff_node(), lb(new ff_loadbalancer(max_num_workers)), myownlb(true) { } ff_monode(ff_node *filter, int max_num_workers=DEF_MAX_NUM_WORKERS): ff_node(), lb(new ff_loadbalancer(max_num_workers)),myownlb(true) { if (filter == nullptr) { delete lb; lb = nullptr; return; } lb->set_filter(filter); } ff_monode(const ff_monode& n) : ff_node(n) { // here we re-initialize a new gatherer lb = new ff_loadbalancer(n.lb->max_nworkers); if (!lb) { error("ff_monode, not enough memory\n"); return; } if (n.lb->get_filter()) lb->set_filter(n.lb->get_filter()); myownlb=true; outputNodes=n.outputNodes; outputNodesFeedback=n.outputNodesFeedback; internalSupportNodes = n.internalSupportNodes; // this is a dirty part, we modify a const object..... ff_monode *dirty= const_cast(&n); dirty->internalSupportNodes.resize(0); } /** * \brief Destructor */ virtual ~ff_monode() { if (lb && myownlb) delete lb; for(size_t i=0;iset_filter(filter); } inline void set_barrier(BARRIER_T * const barrier) { lb->set_barrier(barrier); } inline BARRIER_T* get_barrier() const { return lb->get_barrier(); } /** * \brief Assembly the output channels * * Attach output channelnames to ff_node channels */ virtual inline int set_output(const svector & w) { outputNodes += w; return 0; } /** * \brief Assembly an output channels * * Attach a output channelname to ff_node channel */ virtual inline int set_output(ff_node *node) { outputNodes.push_back(node); return 0; } /** * \brief Assembly an output channels * * Attach a output channelname to ff_node channel */ virtual inline int set_output_feedback(ff_node *node) { outputNodesFeedback.push_back(node); return 0; } virtual bool isMultiOutput() const { return true;} virtual inline void get_out_nodes(svector&w) { // it is possible that the multi-output node is register // as emitter of farm if (outputNodes.size() == 0) { if (lb->getNWorkers()>0) w += lb->getWorkers(); else w.push_back(this); return; } w += outputNodes; } virtual inline void get_out_nodes_feedback(svector&w) { w += outputNodesFeedback; } void set_running(int r) { lb->running = r; } /** * \brief Skips the first pop * * Set up spontaneous start */ inline void skipfirstpop(bool sk) { lb->skipfirstpop(sk); ff_node::skipfirstpop(sk); } #ifdef DFF_ENABLED void skipallpop(bool sk) { lb->skipallpop(sk); ff_node::skipallpop(sk); } void set_virtual_outchannels(int n){ noutchannels=n; } void set_virtual_feedbackchannels(int n) { nfeedbackchannels=n; } #endif /** * \brief Provides the next channel id that will be selected for sending out the task * */ int get_next_free_channel(bool forever=true) { return lb->get_next_free_channel(forever); } /** * \brief Sends one task to a specific node id. * * \return true if successful, false otherwise */ virtual inline bool ff_send_out_to(void *task, int id, unsigned long retry=((unsigned long)-1), unsigned long ticks=(ff_node::TICKS2WAIT)) { // NOTE: this callback should be set only if the multi-output node is part of // a composition and the node is not the last stage if (callback) return callback(task,id, retry,ticks, callback_arg); assert(id>=0); return lb->ff_send_out_to(task,id,retry,ticks); } inline bool ff_send_out(void * task, int id=-1, unsigned long retry=((unsigned long)-1), unsigned long ticks=(ff_node::TICKS2WAIT)) { // NOTE: this callback should be set only if the multi-output node is part of // a composition and it is not the last stage if (callback) return callback(task,id, retry,ticks,callback_arg); return lb->schedule_task(task,retry,ticks); } // TODO: broadcast_task should have callback as in ff_send_out // inline void broadcast_task(void *task) { lb->broadcast_task(task); } /** * \brief run * * \param skip_init defines if the initilization should be skipped * * \return 0 if successful, otherwise -1 is returned. */ int run(bool /*skip_init*/=false) { if (!lb) return -1; if (lb->get_filter() == nullptr) lb->set_filter(this); if (!prepared) if (prepare()<0) return -1; if (ff_node::skipfirstpop()) lb->skipfirstpop(true); if (!default_mapping) lb->no_mapping(); if (lb->runlb()<0) { error("ff_monode, running loadbalancer module\n"); return -1; } return 0; } int freeze_and_run(bool=false) { lb->freeze(); return run(true); } int wait(/* timeout */) { if (lb->waitlb()<0) return -1; return 0; } /** * \brief checks if the node is running * */ bool done() const { return lb->done(); } /** * \internal * \brief Gets the internal lb (Emitter) * * It gets the internal lb (Emitter) * * \return A pointer to the lb */ inline ff_loadbalancer * getlb() const { return lb;} // used when a multi-output node is used as a filter in the emitter of a farm // it can also be used to set a particular scheduling policy like for // example the ones pre-defined in the file ordering_policies.hpp void setlb(ff_loadbalancer *elb, bool cleanup=false) { if (lb && myownlb) { delete lb; myownlb = false; } lb = elb; myownlb=cleanup; } /** * \brief Gets the channel id from which the data has just been received * */ ssize_t get_channel_id() const { return lb->get_channel_id();} size_t get_num_feedbackchannels() const { #ifdef DFF_ENABLED if (nfeedbackchannels!=-1) return nfeedbackchannels; #endif return lb->get_num_feedbackchannels(); } size_t get_num_outchannels() const { #ifdef DFF_ENABLED if (noutchannels!=-1) return noutchannels; #endif return lb->get_num_outchannels(); } size_t get_num_inchannels() const { return lb->get_num_inchannels(); } const struct timeval getstarttime() const { return lb->getstarttime();} const struct timeval getstoptime() const { return lb->getstoptime();} const struct timeval getwstartime() const { return lb->getwstartime();} const struct timeval getwstoptime() const { return lb->getwstoptime();} #if defined(TRACE_FASTFLOW) /* * \brief Prints the FastFlow trace * * It prints the trace of FastFlow. */ inline void ffStats(std::ostream & out) { out << "--- multi-output:\n"; lb->ffStats(out); } #else void ffStats(std::ostream & out) { out << "FastFlow trace not enabled\n"; } #endif protected: ff_loadbalancer* lb; bool myownlb; int ondemand=0; #ifdef DFF_ENABLED int noutchannels=-1; int nfeedbackchannels=-1; #endif svector outputNodes; svector outputNodesFeedback; svector internalSupportNodes; }; /* ************************* Multi-Input and Multi-Output node ************************* */ /* (typed version based on ff_minode and ff_monode ) */ /*! * \class ff_minode_t * \ingroup building_blocks * * \brief Typed multiple input ff_node (the SPMC mediator). * * Key method is: \p svc (pure virtual). * * This class is defined in \ref node.hpp */ template struct ff_minode_t: ff_minode { typedef IN_t in_type; typedef OUT_t out_type; ff_minode_t(): GO_ON((OUT_t*)FF_GO_ON), EOS((OUT_t*)FF_EOS),EOSW((OUT_t*)FF_EOSW), GO_OUT((OUT_t*)FF_GO_OUT), EOS_NOFREEZE((OUT_t*) FF_EOS_NOFREEZE) { #ifdef DFF_ENABLED /* WARNING: * the definition of functions alloctaskF, freetaskF, serializeF, deserializeF * IS DUPLICATED for the ff_node_t (see file node.hpp). * */ if constexpr (traits::has_alloctask_v) { this->alloctaskF = [](char* ptr, size_t sz) -> void* { IN_t* p = nullptr; alloctaskWrapper(ptr, sz, p); assert(p); return p; }; } else { this->alloctaskF = [](char*, size_t ) -> void* { IN_t* o = new IN_t; assert(o); return o; }; } if constexpr (traits::has_freetask_v) { this->freetaskF = [](void* o) { freetaskWrapper(reinterpret_cast(o)); }; } else { this->freetaskF = [](void* o) { delete reinterpret_cast(o); }; } // check on Serialization capabilities on the OUTPUT type! if constexpr (traits::is_serializable_v){ this->serializeF = [](void* o, dataBuffer& b) -> bool { bool datacopied=true; std::pair p = serializeWrapper(reinterpret_cast(o), datacopied); b.setBuffer(p.first, p.second); return datacopied; }; } else if constexpr (cereal::traits::is_output_serializable::value) { this->serializeF = [](void* o, dataBuffer& b) -> bool { std::ostream oss(&b); cereal::PortableBinaryOutputArchive ar(oss); ar << *reinterpret_cast(o); return true; }; } // check on Serialization capabilities on the INPUT type! if constexpr (traits::is_deserializable_v){ this->deserializeF = [this](dataBuffer& b, bool& datacopied) -> void* { IN_t* ptr=(IN_t*)this->alloctaskF(b.getPtr(), b.getLen()); datacopied = deserializeWrapper(b.getPtr(), b.getLen(), ptr); assert(ptr); return ptr; }; } else if constexpr(cereal::traits::is_input_serializable::value) { this->deserializeF = [this](dataBuffer& b, bool& datacopied) -> void* { std::istream iss(&b); cereal::PortableBinaryInputArchive ar(iss); IN_t* o = (IN_t*)this->alloctaskF(nullptr,0); assert(o); ar >> *o; datacopied = true; return o; }; } #endif } OUT_t * const GO_ON, *const EOS, *const EOSW, *const GO_OUT, *const EOS_NOFREEZE; virtual ~ff_minode_t() {} virtual OUT_t* svc(IN_t*)=0; inline void *svc(void *task) { return svc(reinterpret_cast(task)); }; inline int all_gather(IN_t* in, std::vector& V) { size_t nw = get_num_inchannels(); svector v(nw); v.resize(nw); for(size_t i=0;i struct ff_monode_t: ff_monode { typedef IN_t in_type; typedef OUT_t out_type; ff_monode_t(): GO_ON((OUT_t*)FF_GO_ON), EOS((OUT_t*)FF_EOS),EOSW((OUT_t*)FF_EOSW), GO_OUT((OUT_t*)FF_GO_OUT), EOS_NOFREEZE((OUT_t*) FF_EOS_NOFREEZE) { #ifdef DFF_ENABLED if constexpr (traits::has_alloctask_v) { this->alloctaskF = [](char* ptr, size_t sz) -> void* { IN_t* p = nullptr; alloctaskWrapper(ptr, sz, p); assert(p); return p; }; } else { this->alloctaskF = [](char*, size_t) -> void* { IN_t* o = new IN_t; assert(o); return o; }; } if constexpr (traits::has_freetask_v) { this->freetaskF = [](void* o) { freetaskWrapper(reinterpret_cast(o)); }; } else { this->freetaskF = [](void* o) { delete reinterpret_cast(o); }; } // check on Serialization capabilities on the OUTPUT type! if constexpr (traits::is_serializable_v){ this->serializeF = [](void* o, dataBuffer& b) -> bool { bool datacopied=true; std::pair p = serializeWrapper(reinterpret_cast(o),datacopied); b.setBuffer(p.first, p.second); return datacopied; }; } else if constexpr (cereal::traits::is_output_serializable::value) { this->serializeF = [](void* o, dataBuffer& b) -> bool { std::ostream oss(&b); cereal::PortableBinaryOutputArchive ar(oss); ar << *reinterpret_cast(o); return true; }; } // check on Serialization capabilities on the INPUT type! if constexpr (traits::is_deserializable_v){ this->deserializeF = [this](dataBuffer& b, bool& datacopied) -> void* { IN_t* ptr=(IN_t*)this->alloctaskF(b.getPtr(), b.getLen()); datacopied = deserializeWrapper(b.getPtr(), b.getLen(), ptr); assert(ptr); return ptr; }; } else if constexpr(cereal::traits::is_input_serializable::value){ this->deserializeF = [this](dataBuffer& b, bool& datacopied) -> void* { std::istream iss(&b);cereal::PortableBinaryInputArchive ar(iss); IN_t* o = (IN_t*)this->alloctaskF(nullptr,0); assert(o); ar >> *o; datacopied = true; return o; }; } #endif } OUT_t * const GO_ON, *const EOS, *const EOSW, *const GO_OUT, *const EOS_NOFREEZE; virtual ~ff_monode_t() {} virtual OUT_t* svc(IN_t*)=0; inline void *svc(void *task) { return svc(reinterpret_cast(task)); }; }; /** * Transforms a standard node into a multi-output node */ struct internal_mo_transformer: ff_monode { internal_mo_transformer(ff_node* n, bool cleanup=false): ff_monode(n),cleanup(cleanup),n(n) { } template internal_mo_transformer(const T& _n) { T *t = new T(_n); assert(t); n = t; cleanup=true; ff_monode::set_filter(n); } internal_mo_transformer(const internal_mo_transformer& t) : ff_monode(t) { cleanup=t.cleanup; n = t.n; ff_monode::set_filter(n); // this is a dirty part, we modify a const object..... internal_mo_transformer *dirty= const_cast(&t); dirty->cleanup=false; } ~internal_mo_transformer() { if (cleanup && n) { delete n; n=nullptr; } } inline int svc_init() { return n->svc_init(); } inline void* svc(void* task) { return n->svc(task);} inline void svc_end() { return n->svc_end(); } inline void eosnotify(ssize_t id) { n->eosnotify(id); } int create_input_buffer(int nentries, bool fixedsize=FF_FIXED_SIZE) { int r= ff_monode::create_input_buffer(nentries,fixedsize); if (r<0) return -1; ff_monode::getlb()->get_filter()->set_input_buffer(ff_monode::get_in_buffer()); return 0; } void set_id(ssize_t id) { if (n) n->set_id(id); ff_monode::set_id(id); } void registerCallback(bool (*cb)(void *,int,unsigned long,unsigned long,void *), void * arg) { n->registerCallback(cb,arg); } int dryrun() { if (prepared) return 0; if (n) { if (!n->callback) n->registerCallback(ff_send_out_motransformer, this); if (n->isMultiOutput()) { assert(!n->isComp()); n->setlb(ff_monode::getlb(), false); } } return ff_monode::dryrun(); } int run(bool skip_init=false) { assert(n); if (!prepared) { if (n && n->prepare()<0) return -1; } assert(blocking_in == blocking_out); n->blocking_mode(blocking_in); if (!default_mapping) { n->no_mapping(); ff_monode::no_mapping(); } ff_monode::getlb()->get_filter()->set_id(get_my_id()); return ff_monode::run(skip_init); } static inline bool ff_send_out_motransformer(void * task, int id, unsigned long retry, unsigned long ticks, void *obj) { bool r= ((internal_mo_transformer *)obj)->ff_send_out_to(task, (id<0?0:id), retry, ticks); return r; } bool cleanup; ff_node *n=nullptr; }; /** * Transforms a standard node into a multi-input node * * NOTE: it is importat to call the eosnotify method only if * all input EOSs have been received and not at each EOS. */ struct internal_mi_transformer: ff_minode { internal_mi_transformer(ff_node* n, bool cleanup=false):ff_minode(n),cleanup(cleanup),n(n) {} template internal_mi_transformer(const T& _n) { T *t = new T(_n); assert(t); n = t; cleanup=true; ff_minode::set_filter(n); } internal_mi_transformer(const internal_mi_transformer& t) : ff_minode(t) { cleanup=t.cleanup; n = t.n; ff_minode::set_filter(n); // this is a dirty part, we modify a const object..... internal_mi_transformer *dirty= const_cast(&t); dirty->cleanup=false; dirty->n = nullptr; } ~internal_mi_transformer() { if (cleanup && n) delete n; } inline int svc_init() { return n->svc_init(); } inline void* svc(void*task) { return n->svc(task); } inline void svc_end() { return n->svc_end(); } int set_input(const svector & w) { n->neos += w.size(); return ff_minode::set_input(w); } int set_input(ff_node *node) { n->neos+=1; return ff_minode::set_input(node); } #if 0 int set_output(ff_node *node) { return ff_minode::set_output(node); } #endif int create_output_buffer(int nentries, bool fixedsize=FF_FIXED_SIZE) { if (ff_minode::getgt()->get_out_buffer()) return -1; int r= ff_minode::create_output_buffer(nentries,fixedsize); if (r<0) return -1; ff_minode::getgt()->set_output_buffer(ff_minode::get_out_buffer()); return 0; } void registerCallback(bool (*cb)(void *,int,unsigned long,unsigned long,void *), void * arg) { n->registerCallback(cb,arg); } int set_output_buffer(FFBUFFER * const o) { return n->set_output_buffer(o); } FFBUFFER * get_out_buffer() const { return n->get_out_buffer();} bool ff_send_out(void * task, int id=-1, unsigned long retry=((unsigned long)-1), unsigned long ticks=(TICKS2WAIT)) { return n->ff_send_out(task,id,retry,ticks); } bool init_output_blocking(pthread_mutex_t *&m, pthread_cond_t *&c, bool feedback=true) { return n->init_output_blocking(m,c,feedback); } void set_output_blocking(pthread_mutex_t *&m, pthread_cond_t *&c, bool canoverwrite=false) { n->set_output_blocking(m,c, canoverwrite); } inline void eosnotify(ssize_t id) { n->eosnotify(id); } void set_id(ssize_t id) { if (n) n->set_id(id); ff_minode::set_id(id); } int dryrun() { if (prepared) return 0; if (n) { if (n->isMultiInput()) { assert(!n->isComp()); n->setgt(ff_minode::getgt(), false); } } return ff_minode::dryrun(); } int run(bool skip_init=false) { assert(n); if (!prepared) { if (n && n->prepare()<0) return -1; } assert(blocking_in == blocking_out); n->blocking_mode(blocking_in); if (!default_mapping) { n->no_mapping(); ff_minode::no_mapping(); } ff_minode::getgt()->get_filter()->set_id(get_my_id()); return ff_minode::run(skip_init); } bool cleanup; ff_node *n=nullptr; }; } // namespace #endif /* FF_MULTINODE_HPP */