Orocos Real-Time Toolkit  2.5.0
SignalBase.cpp
00001 /***************************************************************************
00002   tag: Peter Soetens  Wed Jan 18 14:11:39 CET 2006  SignalBase.cxx
00003 
00004                         SignalBase.cxx -  description
00005                            -------------------
00006     begin                : Wed January 18 2006
00007     copyright            : (C) 2006 Peter Soetens
00008     email                : peter.soetens@mech.kuleuven.be
00009 
00010  ***************************************************************************
00011  *   This library is free software; you can redistribute it and/or         *
00012  *   modify it under the terms of the GNU General Public                   *
00013  *   License as published by the Free Software Foundation;                 *
00014  *   version 2 of the License.                                             *
00015  *                                                                         *
00016  *   As a special exception, you may use this file as part of a free       *
00017  *   software library without restriction.  Specifically, if other files   *
00018  *   instantiate templates or use macros or inline functions from this     *
00019  *   file, or you compile this file and link it with other files to        *
00020  *   produce an executable, this file does not by itself cause the         *
00021  *   resulting executable to be covered by the GNU General Public          *
00022  *   License.  This exception does not however invalidate any other        *
00023  *   reasons why the executable file might be covered by the GNU General   *
00024  *   Public License.                                                       *
00025  *                                                                         *
00026  *   This library is distributed in the hope that it will be useful,       *
00027  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00028  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
00029  *   Lesser General Public License for more details.                       *
00030  *                                                                         *
00031  *   You should have received a copy of the GNU General Public             *
00032  *   License along with this library; if not, write to the Free Software   *
00033  *   Foundation, Inc., 59 Temple Place,                                    *
00034  *   Suite 330, Boston, MA  02111-1307  USA                                *
00035  *                                                                         *
00036  ***************************************************************************/
00037 
00038 
00039 #include "SignalBase.hpp"
00040 #include <boost/lambda/bind.hpp>
00041 
00042 #ifdef ORO_SIGNAL_USE_LIST_LOCK_FREE
00043 #else
00044 #include "../os/MutexLock.hpp"
00045 #endif
00046 
00047 namespace RTT {
00048     namespace internal {
00049 
00050         // ConnectionBase
00051 
00052         void intrusive_ptr_add_ref( ConnectionBase* p ) { p->ref(); }
00053         void intrusive_ptr_release( ConnectionBase* p ) { p->deref(); }
00054 
00055         void ConnectionBase::ref() { refcount.inc(); };
00056         void ConnectionBase::deref() { if ( refcount.dec_and_test() ) delete this; };
00057 
00058         ConnectionBase::ConnectionBase(SignalBase* sig)
00059             : mconnected(false), m_sig(sig)
00060             , refcount(0)
00061         {
00062         }
00063 
00064         ConnectionBase::~ConnectionBase() {
00065         }
00066 
00067         bool ConnectionBase::connect() {
00068             if( !m_sig ) return false;
00069             mconnected = true;
00070             return true;
00071         }
00072         bool ConnectionBase::disconnect() {
00073             if (!m_sig) return false;
00074             mconnected = false;
00075             return true;
00076         }
00077         void ConnectionBase::destroy() {
00078             if( !m_sig ) return;
00079             mconnected = false;
00080             SignalBase* copy = m_sig;
00081             m_sig = 0;
00082             copy->conn_destroy(this);
00083             // after this point this object may be destructed !
00084         }
00085 
00086         // SignalBase
00087 
00088         void SignalBase::conn_setup( connection_t conn ) {
00089             // allocate empty slot in list.
00090 #ifdef ORO_SIGNAL_USE_LIST_LOCK_FREE
00091             mconnections.grow(1);
00092 #else
00093 #ifdef ORO_SIGNAL_USE_RT_LIST
00094             mconnections.rt_grow(1);
00095 #else
00096             connection_t d(0);
00097             mconnections.push_back( d );
00098 #endif
00099 #endif
00100             this->conn_connect( conn );
00101         }
00102 
00103         void SignalBase::conn_connect( connection_t conn ) {
00104             assert( conn.get() && "virtually impossible ! only connection base should call this function !" );
00105 
00106 #ifdef ORO_SIGNAL_USE_LIST_LOCK_FREE
00107             mconnections.append( conn );
00108 #else
00109             // derived class must make sure that list contained enough list items !
00110             //assert( itend != mconnections.end() );
00111 
00112             // connection (push_back) in emit() does not invalidate iterators, so this
00113             // function is straightforwardly implemented.
00114             os::MutexLock lock(m);
00115 #ifdef ORO_SIGNAL_USE_RT_LIST
00116             mconnections.push_back( conn );
00117 #else
00118             iterator tgt;
00119             connection_t empty;
00120             // find empty slot
00121             if ( (tgt = std::find( mconnections.begin(),
00122                                    mconnections.end(),
00123                                    empty )) != mconnections.end() ) {
00124                 *tgt = conn;
00125             }
00126 #endif
00127 #endif
00128         }
00129 
00130         void SignalBase::conn_destroy( connection_t conn ) {
00131             this->conn_disconnect(conn);
00132             // increase number of connections destroyed.
00133 #ifdef ORO_SIGNAL_USE_LIST_LOCK_FREE
00134             // free memory
00135             mconnections.shrink(1);
00136 #else
00137 #ifdef ORO_SIGNAL_USE_RT_LIST
00138             // free memory
00139             mconnections.rt_shrink(1);
00140 #else
00141             ++concount;
00142 #endif
00143 #endif
00144         }
00145 
00146         void SignalBase::conn_disconnect( connection_t conn ) {
00147             assert( conn.get() && "virtually impossible ! only connection base should call this function !" );
00148 
00149 #ifdef ORO_SIGNAL_USE_LIST_LOCK_FREE
00150             mconnections.erase( conn );
00151 #else
00152             iterator tgt;
00153             // avoid invalidating iterator of emit() upon self or cross removal of conn.
00154             os::MutexLock lock(m);
00155             if ( (tgt = std::find( mconnections.begin(),
00156                                    mconnections.end(),
00157                                    conn)) != mconnections.end() ) {
00158 #ifdef ORO_SIGNAL_USE_RT_LIST
00159                 if ( !emitting ) {
00160                     mconnections.erase( tgt ); // safe to remove we hold mutex + no self removal.
00161                     return;
00162                 }
00163                 // only done when in emit(). cleanup() is guaranteed to be called afterwards.
00164                 ++disconcount; // used in cleanup() to detect self-disconnections.
00165 #endif
00166                 // cfr for loop in cleanup()
00167                 connection_t d(0);
00168                 *tgt = d; //clear out, no erase, keep all iterators valid !
00169             }
00170 #endif
00171         }
00172 
00173 #ifdef ORO_SIGNAL_USE_LIST_LOCK_FREE
00174             // NOP
00175 #else
00176         void SignalBase::cleanup() {
00177             // this is called from within emit().
00178             // mutex already locked !
00179 #ifdef ORO_SIGNAL_USE_RT_LIST
00180             // this construct allows self+cross removal (cfr conn_disconnect) in emit().
00181             iterator it = mconnections.begin();
00182             iterator newit(it);
00183             const_iterator end = mconnections.end();
00184             for (; newit != end && disconcount > 0 ; it=newit ) {
00185                 if (!*it) {
00186                     // it & newit become invalid after erase !
00187                     // do not change this construct unthoughtfully !
00188                     if ( it == mconnections.begin() ) {
00189                         mconnections.erase( it );
00190                         newit = mconnections.begin();
00191                     } else {
00192                         ++newit;
00193                         mconnections.erase( it );
00194                     }
00195                     --disconcount;
00196                 } else
00197                     ++newit;
00198             }
00199 #else
00200             while ( concount > 0 ) {
00201                 mconnections.erase( --(mconnections.end()) );
00202                 --concount;
00203             }
00204 #endif
00205             // remove zeros. too expensive ?
00206             //itend = std::remove( mconnections.begin(), itend, 0);
00207         }
00208 #endif
00209 
00210         SignalBase::SignalBase() :
00211 #ifdef ORO_SIGNAL_USE_LIST_LOCK_FREE
00212             mconnections(4) // this is a 'sane' starting point, this number will be grown if required.
00213 #else
00214 #ifdef ORO_SIGNAL_USE_RT_LIST
00215             disconcount(0)
00216 #else
00217             concount(0)
00218 #endif
00219 #endif
00220             ,emitting(false)
00221     {
00222 #ifdef ORO_SIGNAL_USE_LIST_LOCK_FREE
00223         // NOP
00224 #else
00225         itend = mconnections.end();
00226 #endif
00227     }
00228 
00229         SignalBase::~SignalBase(){
00230             // call destroy on all connections.
00231             destroy();
00232         }
00233 
00234 #ifdef ORO_SIGNAL_USE_LIST_LOCK_FREE
00235         // required for GCC 4.0.2
00236         ConnectionBase* getPointer( ConnectionBase::shared_ptr c ) {
00237             return c.get();
00238         }
00239 #endif
00240 
00241         void SignalBase::disconnect() {
00242 #ifdef ORO_SIGNAL_USE_LIST_LOCK_FREE
00243             mconnections.apply( boost::lambda::bind(&ConnectionBase::disconnect, boost::lambda::bind( &getPointer, boost::lambda::_1) ) ); // works for any compiler
00244 #else
00245             // avoid invalidating iterator
00246             os::MutexLock lock(m);
00247             for( iterator tgt = mconnections.begin(); tgt != mconnections.end(); ++tgt)
00248                 (*tgt)->disconnect();
00249 #endif
00250         }
00251 
00252         void SignalBase::destroy() {
00253             while ( !mconnections.empty() ) {
00254                 if ( mconnections.front() )
00255                     mconnections.front()->destroy(); // this calls-back conn_disconnect.
00256 #ifdef ORO_SIGNAL_USE_LIST_LOCK_FREE
00257                 // NOP
00258 #else
00259 #ifdef ORO_SIGNAL_USE_RT_LIST
00260                 // NOP
00261 #else
00262                 mconnections.erase( mconnections.begin() );
00263 #endif
00264 #endif
00265             }
00266         }
00267 
00268         void SignalBase::reserve( size_t conns ) {
00269 #ifdef ORO_SIGNAL_USE_LIST_LOCK_FREE
00270             mconnections.reserve( conns );
00271 #endif
00272         }
00273 
00274     }
00275 }