/* *************************************************************************** * * 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. * **************************************************************************** */ /* Authors: * Nicolo' Tonci * Massimo Torquati */ #ifndef WRAPPER_H #define WRAPPER_H #include #include #include #include #include #include #include #include using namespace ff; template struct DummyNode : public ff_node_t { Tout* svc(Tin* in){ return nullptr;} }; /* Wrapper IN class */ class WrapperIN: public internal_mi_transformer { private: int inchannels; // number of input channels the wrapped node is supposed to have int feedbackChannels = 0; public: WrapperIN(ff_node* n, int inchannels=1, bool cleanup=false): internal_mi_transformer(this, false), inchannels(inchannels){ this->n = n; this->cleanup= cleanup; } void registerCallback(bool (*cb)(void *,int,unsigned long,unsigned long,void *), void * arg) { internal_mi_transformer::registerCallback(cb,arg); } int svc_init() { if (this->n->isMultiInput()) { ff_minode* mi = reinterpret_cast(this->n); mi->set_running(get_num_feedbackchannels()+1); } return n->svc_init(); } void * svc(void* in) { // with feedback channels it might be null if (in == nullptr) return n->svc(nullptr); message_t* msg = (message_t*)in; if (this->n->isMultiInput()) { int channelid = msg->sender; ff_minode* mi = reinterpret_cast(this->n); mi->set_input_channelid(channelid, fromInput()); if (!this->fromInput()) return n->svc(in); } // received a logical EOS if (msg->data.getLen() == 0){ this->n->eosnotify(msg->sender); delete msg; return GO_ON; } bool datacopied=true; void* inputData = this->n->deserializeF(msg->data, datacopied); if (!datacopied) msg->data.doNotCleanup(); delete msg; return n->svc(inputData); } void svc_end(){this->n->svc_end();} ff_node* getOriginal(){ return this->n; } }; /* Wrapper OUT class */ class WrapperOUT: public internal_mo_transformer { private: int outchannels; // number of output channels the wrapped node is supposed to have int defaultDestination; int myID; int feedbackChannels, localFeedbacks, remoteFeedbacks; public: WrapperOUT(ff_node* n, int id, int outchannels=-1, int remoteFeedbacks = 0, bool cleanup=false, int defaultDestination = -1): internal_mo_transformer(this, false), outchannels(outchannels), defaultDestination(defaultDestination), myID(id), remoteFeedbacks(remoteFeedbacks){ this->n = n; this->cleanup= cleanup; registerCallback(ff_send_out_to_cbk, this); } bool serialize(void* in, int id) { if (localFeedbacks){ if (id < localFeedbacks) { if (id == -1) return ff_send_out(in); return ff_send_out_to(in, id); } // from 0 to feedbackChannels-1 are feedback channels // from feedbackChannels to outchannels-1 are forward channels id -= localFeedbacks; } message_t* msg = new message_t; msg->feedback = (id < remoteFeedbacks && remoteFeedbacks); if (!msg->feedback) id -= remoteFeedbacks; bool datacopied = this->n->serializeF(in, msg->data); msg->sender = myID; msg->chid = id; if (!datacopied) msg->data.freetaskF = this->n->freetaskF; if (localFeedbacks) { // all forward channels are multiplexed in feedbackChannels which coicide with the sender node! ff_send_out_to(msg, localFeedbacks); } else ff_send_out(msg); if (datacopied) this->n->freetaskF(in); return true; } int svc_init() { // save the channel id fo the sender, useful for when there are feedbacks in the application // these are local feedback channels localFeedbacks = internal_mo_transformer::get_num_feedbackchannels(); feedbackChannels = localFeedbacks + remoteFeedbacks; if (this->n->isMultiOutput()) { ff_monode* mo = reinterpret_cast(this->n); mo->set_virtual_outchannels(outchannels); mo->set_virtual_feedbackchannels(feedbackChannels); } return n->svc_init(); } void * svc(void* in) { void* out = n->svc(in); if (out > FF_TAG_MIN) return out; serialize(out, defaultDestination); return GO_ON; } void svc_end(){n->svc_end();} int run(bool skip_init=false) { return internal_mo_transformer::run(skip_init); } /** returns the total number of output channels */ size_t get_num_outchannels() const { return outchannels; } size_t get_num_feedbackchannels() const { return feedbackChannels; } ff::ff_node* getOriginal(){return this->n;} static inline bool ff_send_out_to_cbk(void* task, int id, unsigned long retry, unsigned long ticks, void* obj) { return ((WrapperOUT*)obj)->serialize(task, id); } }; /* Wrapper INOUT class */ class WrapperINOUT: public internal_mi_transformer { private: int inchannels; // number of input channels the wrapped node is supposed to have int defaultDestination; int myID; int remoteFeedbacks; public: WrapperINOUT(ff_node* n, int id, int inchannels=1, int remoteFeedbacks = 0, bool cleanup=false, int defaultDestination = -1): internal_mi_transformer(this, false), inchannels(inchannels), defaultDestination(defaultDestination), myID(id), remoteFeedbacks(remoteFeedbacks){ this->n = n; this->cleanup = cleanup; registerCallback(ff_send_out_to_cbk, this); } void registerCallback(bool (*cb)(void *,int,unsigned long,unsigned long,void *), void * arg) { internal_mi_transformer::registerCallback(cb,arg); } bool serialize(void* in, int id) { if ((void*)in > FF_TAG_MIN) return ff_node::ff_send_out(in); message_t* msg = new message_t; msg->feedback = (id < remoteFeedbacks && remoteFeedbacks); bool datacopied= this->n->serializeF(in, msg->data); msg->sender = myID; // FIX! msg->chid = id; if (!msg->feedback) msg->chid -= remoteFeedbacks; if (!datacopied) msg->data.freetaskF = this->n->freetaskF; ff_node::ff_send_out(msg); if (datacopied) this->n->freetaskF(in); return true; } int svc_init() { /*if (this->n->isMultiInput()) { // ??? what?? ff_minode* mi = reinterpret_cast(this->n); // what????? mi->set_running(inchannels); }*/ return n->svc_init(); } void svc_end(){this->n->svc_end();} void * svc(void* in) { void* out; if (in != nullptr) { message_t* msg = (message_t*)in; // received a logical EOS if (msg->data.getLen() == 0){ this->n->eosnotify(msg->sender); // TODO: msg->sender here is not consistent... always 0 delete msg; return GO_ON; } if (this->n->isMultiInput()) { int channelid = msg->sender; ff_minode* mi = reinterpret_cast(this->n); mi->set_input_channelid(channelid, !msg->feedback); } bool datacopied=true; out = n->svc(this->n->deserializeF(msg->data, datacopied)); if (!datacopied) msg->data.doNotCleanup(); delete msg; } else // it can happen if we have a feedback channel out = n->svc(nullptr); serialize(out, defaultDestination); return GO_ON; } bool init_output_blocking(pthread_mutex_t *&m, pthread_cond_t *&c, bool feedback=true) { return ff_node::init_output_blocking(m,c,feedback); } void set_output_blocking(pthread_mutex_t *&m, pthread_cond_t *&c, bool canoverwrite=false) { ff_node::set_output_blocking(m,c,canoverwrite); } ff_node* getOriginal(){ return this->n; } static inline bool ff_send_out_to_cbk(void* task, int id, unsigned long retry, unsigned long ticks, void* obj) { return ((WrapperINOUT*)obj)->serialize(task, id); } }; #endif