/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /*! \file dnode.hpp * \brief Contains the definition of the \p ff_dnode class, which is an extension * of the base class \p ff_node, with features oriented to distributed systems. */ /* *************************************************************************** * 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_DINOUT_HPP #define FF_DINOUT_HPP #include #include namespace ff { /*! * \class ff_dinout * \ingroup building_blocks * * \brief A \ref ff::ff_dnode serving both Input and Output to the network * */ template class ff_dinout:public dnode { public: typedef typename CommImpl::TransportImpl::msg_t msg_t; enum {SENDER=true, RECEIVER=false}; protected: // this is called once as soon as the message just sent // is no longer in use by the run-time static dnode_cbk_t cb; static inline void freeMsg(void * data, void * arg) { if (cb) cb(data,arg); } static inline void freeHdr(void * data, void *) { delete static_cast(data); } inline bool isEos(const char data[msg_t::HEADER_LENGHT]) const { return !(data[0]-'E' + data[1]-'O' + data[2]-'S' + data[3]-'\0'); } /// Default constructor ff_dinout():ff_node(),skipdnode(true) {} /// Destructor: closes all connections. virtual ~ff_dinout() { com.close(); delete com.getDescriptor(); } virtual inline bool push(void * ptr) { if (skipdnode || !P) return ff_node::push(ptr); // gets the peers involved in one single communication const int peers=com.getDescriptor()->getPeers(); if (ptr == (void*)FF_EOS) { //cerr << "DNODE prepare to send FF_DEOS to " << peers <<" peers\n"; for(int i=0;i v; for(int i=0;i v; callbackArg.resize(0); prepare(v, ptr); msg_t hdr(new uint32_t(v.size()), msg_t::HEADER_LENGHT, freeHdr); com.putmore(hdr); callbackArg.resize(v.size()); for(size_t j=0;j* v[sendingPeers]; for(int i=0;i(hdr.getData()))) { //std::cerr << "DNODE gets DEOS\n"; if (++neos==com.getDescriptor()->getPeers()) { com.done(); *ptr = (void*)FF_EOS; neos=0; return true; } if (sendingPeers==1) i=-1; // reset for index continue; } uint32_t len = *static_cast(hdr.getData()); int ventry = (sendingPeers==1)?0:sender; prepare(v[ventry], len, sender); assert(v[ventry]->size() == len); for(size_t j=0;joperator[](j)),sender)) { error("dnode:pop: ERROR: receiving data from peer"); return false; } } com.done(); unmarshalling(v, sendingPeers, *ptr); return true; } public: int initIn(const std::string& name, const std::string& address, const int peers, typename CommImpl::TransportImpl* const transp, const int nodeId=-1) { return In.init(name,address,peers,transp,false, nodeId); } // serialization/deserialization methods // The first prepare is used by the Producer (p=true in the init method) // whereas the second prepare and the unmarshalling methods are used // by the Consumer (p=false in the init method). virtual void prepare(svector& v, void* ptr, const int sender=-1) { struct iovec iov={ptr,sizeof(void*)}; v.push_back(iov); setCallbackArg(NULL); } // COMMENT: // When using ZeroMQ (from zguide.zeromq.org): // "There is no way to do zero-copy on receive: ØMQ delivers you a // buffer that you can store as long as you wish but it will not // write data directly into application buffers." // // /* * Used to give to the run-time a pool of messages on which * input message frames will be received * \param len the number of input messages expected * \param sender the message sender * \param v vector containing the pool of messages */ virtual void prepare(svector*& v, size_t len, const int sender=-1) { svector * v2 = new svector(len); assert(v2); for(size_t i=0;ipush_back(m); } v = v2; } /* * This method is called once, when all frames composing the message have been received * by the run-time. Within that method, it is possible to convert or re-arrange * all the frames back to their original data or object layout. */ virtual void unmarshalling(svector* const v[], const int vlen, void *& task) { assert(vlen==1 && v[0]->size()==1); task = v[0]->operator[](0)->getData(); delete v[0]; } /* * This methed can be used to pass an additional parameter (the 2nd one) * to the callback function. Typically it is called in the prepare method of the * producer. */ void setCallbackArg(void* arg) { callbackArg.push_back(arg);} /* * Runs the \p dnode as a stand-alone thread.\n * Typically, it should not be called by application code unless you want to have just * a sequential \p dnode */ int run(bool=false) { return ff_node::run(); } /// Waits the thread to finish int wait() { return ff_node::wait(); } /* jumps the first pop from the input queue or from the input * external channel. This is typically used in the first stage * of a cyclic graph (e.g. the first stage of a torus pipeline) */ void skipfirstpop(bool sk) { ff_node::skipfirstpop(sk); } protected: bool skipdnode; bool P; int neos; svector callbackArg; CommImpl com; }; template dnode_cbk_t ff_dnode::cb=0; } // namespace ff #endif /* FF_DINOUT_HPP */