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 }