Tekkotsu Homepage | Demos | Overview | Downloads | Dev. Resources | Reference | Credits |
MotionPtr.hGo to the documentation of this file.00001 //-*-c++-*- 00002 #ifndef INCLUDED_MotionPtr_h_ 00003 #define INCLUDED_MotionPtr_h_ 00004 00005 #include "Shared/Resource.h" 00006 #include "Events/EventRouter.h" 00007 #include "MMAccessor.h" 00008 #include "IPC/SharedObject.h" 00009 #include "Shared/debuget.h" 00010 #include <stack> 00011 00012 //! A shared_ptr for MotionCommands; provides allocation and access to a MotionCommand, ensuring mutually exclusive access and scope-based deallocation 00013 /*! The MotionPtr class performs several jobs: 00014 * - Maintain a reference to the enveloping shared memory region, via SharedObject 00015 * - Access to object within a shared memory region, enforcing mutual exclusion via MMAccessor 00016 * 00017 * The first point means that even if the motion is removed from the motion manager or dereferenced by an 00018 * external source, your MotionPtr can still access the motion without error. Further, since the motion's MC_ID 00019 * is stored in the motion, user code can test whether a motion is currently active or not. A corollary is 00020 * that when this class goes out of scope, the memory region will also be dereferenced, preventing leaks. 00021 * To enforce this more rigidly, if the MotionPtr detects that the memory region it is dereferencing is 00022 * only referenced by the MotionManager, it will remove the motion from the MotionManager as well. 00023 * 00024 * The second job addresses the concern that the Motion process may be querying the motion 00025 * for output values while you're in the middle of updating the motion command's parameters. 00026 * By overloading operator-> to return an MMAccessor, each access to the motion is protected 00027 * by a mutual exclusion lock for the scope of access. Note that if you want to maintain a lock 00028 * over several lines (for either performance or because the motion is left in a bad state between 00029 * the calls), then you can use MarkScope to maintain the lock: 00030 * @code 00031 * MotionPtr<Foo> foo; 00032 * foo->bar(); // a lock is obtained for the duration of this call 00033 * foo->tweak(); // a second lock is obtained for this call 00034 * @endcode 00035 * 00036 * Alternatively: 00037 * @code 00038 * MotionPtr<Foo> foo; 00039 * MarkScope lockfoo(foo); // a lock is obtained until 'lockfoo' goes out of scope 00040 * foo->bar(); // the lock is maintained across these calls... 00041 * foo->tweak(); 00042 * @endcode 00043 * 00044 * The motion in MotionPtr is always "valid", and will be allocated using the templated motion's default 00045 * constructor at the first access. If you wish to call a different constructor for the motion, you can 00046 * pass a SharedObject created with the custom constructor calls to either MotionPtr's constructor or 00047 * using operator=: 00048 * @code 00049 * MotionPtr<Foo> foo; // technically, no Foo has been allocated yet, but that's transparent 00050 * foo->bar(); // first access, now the allocation actually occurs using default constructor 00051 * foo = SharedObject<Foo>(1,2,4); // replacing the motion, this one constructed by Foo(1,2,4) 00052 * 00053 * MotionPtr<Foo> foo2(SharedObject<Foo>(8,16,32)); // use motion initialized by Foo(8,16,32) 00054 * @endcode 00055 * 00056 * You can share a motion between multiple MotionPtr instances, thanks to reference counting performed 00057 * on the underlying memory region. When a SharedObjects, or another MotionPtrs, is assigned to a MotionPtr, 00058 * a shallow copy is performed internally, so they all reference a common motion command: 00059 * @code 00060 * // ...continuing previous sample 00061 * foo = foo2; // now foo and foo2 both refer to the Foo(8,16,32) instance 00062 * @endcode 00063 * 00064 * One advanced feature is that motions can have their memory region reset between activations 00065 * by calling #retain(false). If motions are not retained, this means that if the motion 00066 * is removed from the motion manager, the MotionPtr will release its reference to the memory region. 00067 * If the motion is accessed again thereafter, a new one will be created. Note that it is only a reference 00068 * counting operation: if external references to the unretained motion remain, the original motion will 00069 * survive, it just won't be accessible by this MotionPtr anymore unless reassigned. 00070 * 00071 * Another advanced technique is if you @e really want to "leak" a motion, such as a self-pruning motion 00072 * to restore some state as the behavior exits, or if another behavior holds only a MC_ID for the motion 00073 * (thus there is no explicit reference to the underlying memory region), then you can extract the motion by: 00074 * @code 00075 * MotionPtr<Foo> foo; // motion you want to extract 00076 * SharedObject<Foo> tmp = foo; // make a new reference to the region 00077 * foo.clear() // drop foo's reference now 00078 * // SharedObject will still dereference foo's memory region as it leaves this scope, 00079 * // but assuming the motion is actually active, the motion manager holds a reference, so the 00080 * // motion will survive (otherwise the memory will still be released as usual) 00081 * @endcode 00082 * 00083 * However, ideally MotionPtrs should be used for extended shared access to a motion (instead of sharing just the MC_ID). 00084 * And if you are adding a "dangling" motion during exit, you should just call motman->addPrunableMotion 00085 * directly with a SharedObject instead of using this class. 00086 */ 00087 template<class T> 00088 class MotionPtr : public Resource, public EventListener { 00089 public: 00090 //! Constructor, defaults to an empty SharedObject instance and will retain the allocation once it is created 00091 explicit MotionPtr() : Resource(), EventListener(), mcObj(SharedObjectBase::NoInit()), checkouts(), retained(true) {} 00092 //! Constructor, adopts a reference to @a obj, and will retain the reference between additions to the motion manager 00093 MotionPtr(const SharedObject<T> obj) : Resource(), EventListener(), mcObj(obj), checkouts(), retained(true) {} 00094 //! Destructor, if this holds the last non-motion manager reference to the motion, removes it from the motion manager 00095 ~MotionPtr() { 00096 if(erouter!=NULL) // allows use of statics in libtekkotsu without crashing on destruction 00097 erouter->removeListener(this,EventBase::motmanEGID); 00098 ASSERTRET(ProcessID::getID()!=ProcessID::MotionProcess,"How is ~MotionPtr running in motion process?"); 00099 if(active()) { 00100 MotionManager::MC_ID mcid = mcObj->getID(); 00101 int rc = mcObj.getRegion()->NumberOfReference(); 00102 if(motman->hasReference(ProcessID::MotionProcess,mcid)) 00103 --rc; 00104 if(rc==2 && motman->hasReference(ProcessID::MainProcess,mcid)) // 2 = 1 for this + 1 for the process's MotionManager 00105 motman->removeMotion(mcObj->getID()); // last non-MotionManager reference, kill the MC 00106 } 00107 } 00108 00109 //! Automatically exposes the underlying shared object so you can pass it to SharedObject-based functions (e.g. BehaviorBase::addMotion() or MotionManager::addPersistentMotion()) 00110 operator const SharedObject<T>&() const { 00111 if(!allocated()) 00112 const_cast<MotionPtr&>(*this) = SharedObject<T>(); 00113 return mcObj; 00114 } 00115 //! Automatically create MMAccessor instance for mutual-exclusion access 00116 /*! This lets you assign the MotionPtr to a MMAccessor, so that the lock persists under the new scope and name. 00117 * You could also use MarkScope with the MotionPtr. */ 00118 operator MMAccessor<T>() const { 00119 if(!allocated()) 00120 const_cast<MotionPtr&>(*this) = SharedObject<T>(); 00121 return *mcObj; 00122 } 00123 //! Forward smart-pointer style access to MMAccessor; this is the secret to one-line locking 00124 /*! Keep in mind you can use MarkScope with the MotionPtr to retain the lock over the course of multiple accesses */ 00125 MMAccessor<T> operator->() const { return MMAccessor<T>(*this); } 00126 //! Forward smart-pointer style access to MMAccessor, just for completeness vs. operator->, which is what you'll want to use 00127 /*! This is actually a little syntactically wrong, because operator*() should return a reference vs. operator->'s pointer, 00128 * but then we would lose the locking guarantee. */ 00129 MMAccessor<T> operator*() const { return MMAccessor<T>(*this); } 00130 00131 //! Reassigns the motion reference; henceforth shall share the region with @a obj (shallow copy) 00132 /*! Internal reference counting by the shared memory region will keep everything happy :) */ 00133 MotionPtr& operator=(const SharedObject<T>& obj) { 00134 if(active()) { 00135 if(!retained) 00136 erouter->removeListener(this,EventBase::motmanEGID,mcObj->getID(),EventBase::deactivateETID); 00137 if(mcObj.getRegion()->NumberOfReference()==3) // 3 = 1 for this + 1 for Main's MotionManager + 1 for Motion's Motion Manager 00138 motman->removeMotion(mcObj->getID()); // last non-MotionManager reference, kill the MC 00139 } 00140 mcObj = obj; 00141 if(active() && !retained) 00142 erouter->addListener(const_cast<MotionPtr*>(this),EventBase::motmanEGID,mcObj->getID(),EventBase::deactivateETID); 00143 return *this; 00144 } 00145 //! Reassigns the motion reference; henceforth shall share the region with @a wr (shallow copy) 00146 /*! Internal reference counting by the shared memory region will keep everything happy :) */ 00147 MotionPtr& operator=(const MotionPtr& wr) const { operator=(wr.mcObj); } 00148 00149 //! Returns the motion command ID associated with the motion, or MotionManager::invalid_MC_ID if the motion is not allocated or otherwise not registered with the motion manager 00150 virtual MotionManager::MC_ID getID() const { 00151 if(!allocated()) 00152 return MotionManager::invalid_MC_ID; 00153 return mcObj->getID(); 00154 } 00155 00156 //! Removes the reference to the current motion memory region. 00157 /*! A new one will be created with the default constructor if a future access is attempted. */ 00158 void clear() { mcObj = SharedObjectBase::NoInit(); } 00159 00160 //! Returns true if the motion is non-NULL 00161 bool allocated() const { return (mcObj.getRegion()!=NULL); } 00162 00163 //! Returns true if the motion is registered with the motion manager (i.e. it is both allocated and has a valid MC_ID) 00164 bool active() const { return (allocated() && mcObj->getID()!=MotionManager::invalid_MC_ID); } 00165 00166 //! Returns #retained 00167 bool retain() const { return retained; } 00168 00169 //! Sets #retained 00170 void retain(bool r) { 00171 if(retained==r) 00172 return; 00173 retained=r; 00174 if(!retained) { 00175 if(active()) 00176 erouter->addListener(this,EventBase::motmanEGID,mcObj->getID(),EventBase::deactivateETID); 00177 else 00178 clear(); 00179 } 00180 } 00181 00182 protected: 00183 virtual void useResource(Data&) { 00184 if(!allocated()) 00185 *this = SharedObject<T>(); 00186 if(mcObj->getID()==MotionManager::invalid_MC_ID) 00187 return; 00188 checkouts.push(mcObj->getID()); 00189 motman->checkoutMotion(checkouts.top()); 00190 } 00191 virtual void releaseResource(Data&) { 00192 ASSERTRET(checkouts.size()>0,"MotionPtr::releaseResource underflow"); 00193 motman->checkinMotion(checkouts.top()); 00194 checkouts.pop(); 00195 } 00196 00197 //! We only listen for motion deactivate events if not #retained, so if we get a call here we should clear the reference 00198 virtual void processEvent(const EventBase& event) { 00199 if(!allocated()) { 00200 std::cerr << "MotionPtr received event " << event.getDescription() << " without any motion" << std::endl; 00201 return; 00202 } 00203 if(event.getGeneratorID()!=EventBase::motmanEGID || event.getSourceID()!=mcObj->getID() || event.getTypeID()!=EventBase::deactivateETID) { 00204 std::cerr << "MotionPtr received event " << event.getDescription() << " does not correspond to deactivation of MCID " << mcObj->getID() << std::endl; 00205 return; 00206 } 00207 if(!retained) { 00208 std::cerr << "MotionPtr received motion deactivation event for " << mcObj->getID() << " even though it is retained" << std::endl; 00209 return; 00210 } 00211 clear(); 00212 } 00213 00214 //! Maintains the reference to the motion's shared memory region 00215 SharedObject<T> mcObj; 00216 00217 //! Retains a history of useResource() calls, to be released by matching releaseResource() calls. 00218 /*! This solves the problem of removing a motion while it is locked... the removal clears the ID value in #mcObj, thus 00219 * we need to store it here so we can checkin (unlock) the motion later. Using a stack lets us handle recursively 00220 * re-adding the motion and receiving a new ID while still locked under the original ID. */ 00221 std::stack<MotionManager::MC_ID> checkouts; 00222 00223 //! If false, clears the motion instance each time it is removed from the motion manager, and then recreate it if a future access occurs. 00224 /*! If true, the reference on #mcObj will be retained between additions to the motion manager, which is the default behavior. */ 00225 bool retained; 00226 }; 00227 00228 /*! @file 00229 * @brief 00230 * @author Ethan Tira-Thompson (ejt) (Creator) 00231 */ 00232 00233 #endif |
Tekkotsu v5.1CVS |
Generated Mon May 9 04:58:45 2016 by Doxygen 1.6.3 |