Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

WorldStatePool.h

Go to the documentation of this file.
00001 //-*-c++-*-
00002 #ifndef INCLUDED_WorldStatePool_h_
00003 #define INCLUDED_WorldStatePool_h_
00004 
00005 #include "WorldState.h"
00006 #include "IPC/MutexLock.h"
00007 #include "IPC/ListMemBuf.h"
00008 #include "Shared/Resource.h"
00009 #include "Motion/PostureEngine.h"
00010 #include <stdexcept>
00011 
00012 class RCRegion;
00013 
00014 #ifndef WORLDSTATEPOOL_NUM_STATES
00015 //! provides default value for WorldStatePool::NUM_STATES, allows you to override from build settings, without touching source
00016 #define WORLDSTATEPOOL_NUM_STATES 3
00017 #endif
00018 
00019 //! holds multiple instances of WorldState, allows one process to be updating while another is reading
00020 /*! 
00021 Use the AutoGetReadState and AutoGetWriteState to access individual WorldState entries... their
00022 constructors and destructors allow WorldStatePool to keep track of which entries are in use.
00023 
00024 When a state wants to write, it is given the oldest unused entry to write into.  During writing,
00025 other accessors can read the newest (complete) entry, or concurrently write into a different
00026 entry (in case they don't want to wait for the other process to finish).
00027 
00028 A global lock (#lock) is used while choosing an entry, and individual locks (#writeLocks) are
00029 used while writing into entries (to easily allow readers to block on the lock until writing is done)
00030 
00031 One point of trickiness is that entries being written are moved to the end of the list when
00032 the writing begins, not when it is complete.  Readers can always scan backwards in the list
00033 to find the newest entries, but writers must check the end to see if newer (or equivalent)
00034 frames are already in progress, as well as the beginning to find the oldest unreferenced.
00035 
00036 When a writer tries to access an entry, and another writer is already processing that frame,
00037 if blocking is set then the writer will given that entry once the original is done with it (so
00038 check your frame index when you receive it so you can tell if it was already processed).
00039 If blocking is *not* set, then you will get a separate entry with no indication another process
00040 is also working on the same frame.
00041 */
00042 class WorldStatePool : public Resource {
00043 public:
00044   class Request : public Resource::Data {
00045   protected:
00046     //! constructor, sets the WorldState point to be assigned, whether to block, and whether is an instance of ReadRequest
00047     /*! @a wantRead is because we can't trust RTTI (i.e. dynamic_cast) to work correctly on Aperios :( */
00048     Request(WorldState*& target, bool block, bool wantRead) : Resource::Data(),
00049       bufUsed(-1U), tgt(target), prev(NULL), bl(block), depth(0), isRead(wantRead)
00050     {}
00051     //! shallow copy constructor supported
00052     Request(const Request& r) : Resource::Data(r), bufUsed(r.bufUsed), tgt(r.tgt), prev(r.prev), bl(r.bl), depth(r.depth), isRead(r.isRead) {}
00053     //! shallow assignment supported
00054     Request& operator=(const Request& r) { bufUsed=r.bufUsed; tgt=r.tgt; prev=r.prev; bl=r.bl; depth=r.depth; Resource::Data::operator=(r); return *this; }
00055     
00056   public:
00057     unsigned int bufUsed; //!< the entry index used
00058     WorldState*& tgt; //!< reference to pointer, which is set to an element of the pool when the request goes through
00059     WorldState* prev; //!< stores previous value at #tgt so it can be restored upon release (needed to support recursive usage)
00060     bool bl; //!< whether to block if a write is in progress, or use most recent "complete" entry
00061     unsigned int depth; //!< supports recursive read requests
00062     bool isRead; //!< true if instance is a read request
00063   };
00064   //! retrieves the current WorldState entry, and through it's destructor, marks the entry available again when it goes out of scope
00065   class ReadRequest : public Request {
00066   public:
00067     //! stores the current completed WorldState into #tgt, optionally blocking if an update is in progress (otherwise it returns the previous completed entry)
00068     ReadRequest(WorldState*& target, bool block) : Request(target,block,true) {}
00069     //! shallow copy constructor supported
00070     ReadRequest(const ReadRequest& r) : Request(r) {}
00071     //! shallow assignment supported
00072     ReadRequest& operator=(const ReadRequest& r) { Request::operator=(r); return *this; }
00073   };
00074   //! retrieves the current WorldState entry, and through it's destructor, marks the entry available again when it goes out of scope
00075   /*! By default, when the release occurs, the entry is marked "complete" unless you have called setComplete(false) before then */
00076   class WriteRequest : public Request {
00077   public:
00078     //! stores the oldest unreferenced WorldState into #tgt, optionally blocking if an update is currently in progress for the same frame
00079     WriteRequest(WorldState*& target, bool block, unsigned int frame_number) : Request(target,block,false),
00080       src(NULL), srcRequest(src,false), frame(frame_number), statusCache(0), completeCache(false)
00081     {}
00082     //! shallow copy constructor supported
00083     WriteRequest(const WriteRequest& r) : Request(r), src(r.src), srcRequest(r.srcRequest), frame(r.frame), statusCache(r.statusCache), completeCache(r.completeCache) {}
00084     //! shallow assignment supported
00085     WriteRequest& operator=(const WriteRequest& r) { src=r.src; srcRequest=r.srcRequest; frame=r.frame; statusCache=r.statusCache; completeCache=r.completeCache; Request::operator=(r); return *this; }
00086     
00087     WorldState* src; //!< will be set to the previously written element, so you can copy over unmodified values
00088     ReadRequest srcRequest; //!< used to get #src
00089     unsigned int frame; //!< should be initialized to the frame number about to be written
00090     
00091     unsigned int getStatus() const { return statusCache; } //!< returns the WorldStatePool::status value for the target WorldState entry (see documentation for WorldStatePool::status)
00092     void setStatus(unsigned int status) { statusCache=status; } //!< sets the WorldStatePool::status value for the target WorldState entry (see documentation for WorldStatePool::status)
00093     bool getComplete() const { return completeCache; } //!< returns the WorldStatePool::complete value for the target WorldState entry (see documentation for WorldStatePool::complete)
00094     void setComplete(bool complete) { completeCache=complete; } //!< returns the WorldStatePool::complete value for the target WorldState entry (see documentation for WorldStatePool::complete)
00095     
00096   protected:
00097     unsigned int statusCache; //!< when using resource, this field is set to the status field for that entry, and when released, this value is stored back
00098     bool completeCache; //!< when using resource, this field is set to the complete flag for that entry, and when released, this value is stored back
00099   };
00100   
00101   //! constructor
00102   WorldStatePool();
00103   
00104 #ifdef PLATFORM_APERIOS
00105   //! returned by isUnread containing information parsed from the incoming message
00106   class UpdateInfo {
00107   public:
00108     //! constructor, sets #msg to NULL, #frameNumber to -1U
00109     UpdateInfo()
00110 #ifdef DEBUG
00111     : msg(NULL), frameNumber(-1U), intendedBuf(-1U) {}
00112 #else
00113     : msg(NULL), frameNumber(-1U) {}
00114 #endif
00115     OSensorFrameVectorData* msg; //!< incoming data
00116     unsigned int frameNumber; //!< serial number of the message
00117 #ifdef DEBUG
00118     unsigned int intendedBuf; //!< the write buffer read() is expected to use;  This is for debugging, if this isn't the buffer selected, display warning
00119 #endif
00120   private:
00121     UpdateInfo(const UpdateInfo&); //!< not implemented
00122     UpdateInfo operator=(const UpdateInfo&); //!< not implemented
00123   };
00124   
00125   //! returns true if the process should call WorldState::read (i.e. @a msg has new or unprocessed data (such as motion needs to supply feedback))
00126   /*! only one call to this can be made at a time per process (not threadsafe, but is multi-process safe)
00127     *  @param msg the incoming sensor data from the system -- should be const, but accessor functions from Sony aren't marked const themselves :-/
00128     *  @param[out] lastFrameNumber if the incoming frame is already complete (no need to read), then the frame's number will be assigned here
00129     *  @return returns a static UpdateInfo structure (to be passed to read()) if the message is unread, otherwise returns NULL. The structure is static -- DO NOT DELETE IT */
00130   WorldStatePool::UpdateInfo* isUnread(OSensorFrameVectorData& msg, unsigned int& lastFrameNumber);
00131     
00132 #else //PLATFORM_LOCAL
00133   //! flags for the status field on each WriteRequest -- tracks partial completion when multiple writers are involved
00134   enum status_t {
00135     SENSORS_APPLIED=1<<0, //!< bit flag signals sensor data has been applied to the write target
00136     FEEDBACK_APPLIED=1<<1 //!< bit flag signals motion feedback has been applied to the write target (only required if feedback is being generated)
00137   };
00138   
00139   //! returned by isUnread containing information parsed from the incoming message
00140   class UpdateInfo {
00141   public:
00142     UpdateInfo()
00143 #ifdef DEBUG
00144       : verbose(false), frameNumber(-1U), filename(), dataInQueue(false), payload(NULL), payloadSize(0), intendedBuf(-1U) {}
00145 #else
00146       : verbose(false), frameNumber(-1U), filename(), dataInQueue(false), payload(NULL), payloadSize(0) {}
00147 #endif
00148     bool verbose; //!< status of processing the message should be displayed
00149     unsigned int frameNumber; //!< serial number of the message
00150     std::string filename; //!< source of the data in the message
00151     bool dataInQueue; //!< sender indicates data is in the queue (if this is heartbeat, treat it as data)
00152     char* payload; //!< pointer to beginning of the data (NULL if no data available, i.e. heartbeat message)
00153     unsigned int payloadSize; //!< size of data (0 if no data available, i.e. heartbeat message)
00154 #ifdef DEBUG
00155     unsigned int intendedBuf; //!< the write buffer read() is expected to use;  This is for debugging, if this isn't the buffer selected, display warning
00156 #endif
00157   private:
00158     UpdateInfo(const UpdateInfo&); //!< not implemented
00159     UpdateInfo operator=(const UpdateInfo&); //!< not implemented
00160   };
00161   
00162   //! returns true if the process should call read (i.e. @a msg has new or unprocessed data (such as motion needs to supply feedback))
00163   /*! only one call to this can be made at a time per process (not threadsafe, but is multi-process safe)
00164    *  @param msg incoming message to test
00165    *  @param curFrameNumber the most recent frame number sent, i.e. SharedGlobals::MotionSimConfig::frameNumber
00166    *  @param[out] lastFrameNumber if the incoming frame is already complete (no need to read), then the frame's number will be assigned here
00167    *  @param haveFeedback you should pass true if motion feedback <em>can be</em> provided to read() (note the feedback doesn't necessarily need to be available right now, just if the call to read is necessary)
00168    *  @param motionOverride true if motion feedback overrides sensor data (i.e SharedGlobals::MotionSimConfig::override)
00169    *  @return returns a static UpdateInfo structure (to be passed to read()) if the message is unread, otherwise returns NULL. The structure is static -- DO NOT DELETE IT */
00170   WorldStatePool::UpdateInfo* isUnread(const RCRegion& msg, unsigned int curFrameNumber, unsigned int& lastFrameNumber, bool haveFeedback, bool motionOverride);
00171   //! takes a sensor update from the simulator/system, updates an entry in the pool with the new information
00172   /*! If isUnread() returns non-NULL, you should acquire a WriteRequest, and check that it succeeds, is current, and is still incomplete, before calling read()
00173    *  @param info the info structure returned by isUnread
00174    *  @param wsw the WriteRequest aquired before calling read (yes, you should submit a WriteRequest for @a info.frameNumber between isUnread() and read())
00175    *  @param feedbackGenerated pass true if feedback will be generated by a caller to read() -- doesn't need to be <em>this</em> process, but <em>a</em> process.
00176    *  @param zeroPIDFeedback pass true if sensor data should override motion feedback for joints with 0's for PID control (i.e. SharedGlobals::MotionSimConfig::zeroPIDFeedback)
00177    *  @param feedback pointer to actual motion feedback, or NULL if not available in this process (weight values of feedback are ignored, assumes all values are valid) */
00178   bool read(const UpdateInfo& info, WriteRequest& wsw, bool feedbackGenerated, bool zeroPIDFeedback, const PostureEngine* feedback=NULL);
00179 #endif
00180 
00181   //! processes a request, passed as either a ReadRequest or WriteRequest, to access an entry in the pool
00182   virtual void useResource(Data& d) { doUseResource(d); }
00183   //! completes access to an entry in the pool, you must pass the same request instance here which you originally passed to useResource()
00184   virtual void releaseResource(Data& d) { doReleaseResource(d); }
00185   
00186   //! does the actual work of useResource()
00187   /*! this is split off as a non-virtual function to avoid some process
00188    *  identity issues that occur with virtual functions under Aperios */
00189   void doUseResource(Data& d);
00190   //! does the actual work of releaseResource()
00191   /*! this is split off as a non-virtual function to avoid some process
00192    *  identity issues that occur with virtual functions under Aperios */
00193   void doReleaseResource(Data& d);
00194 
00195 #ifdef PLATFORM_APERIOS
00196   //! registers #stateLookupMap with WorldState::setWorldStateLookup()
00197   void InitAccess() { WorldState::setWorldStateLookup(stateLookupMap); }
00198 #endif
00199   
00200 protected:
00201   //! number of buffers to set up
00202   static const unsigned int NUM_STATES=WORLDSTATEPOOL_NUM_STATES;
00203   
00204   //! shorthand for the type of #order
00205   typedef ListMemBuf<unsigned int, NUM_STATES> order_t;
00206   //! indicies of entries, in the order they were written (i.e. corresponding value in #frames should be monotonically increasing)
00207   order_t order;
00208   
00209   //! shorthand to test if all three P, I, and D values are 0 for the specified joint index (relative to 0, not PIDJointOffset)
00210   static bool isZeroPID(WorldState* s, unsigned int i) { return s->pids[i][0]==0 && s->pids[i][1]==0 && s->pids[i][2]==0; }
00211   
00212   //! called when access to an entry for reading is requested
00213   unsigned int getCurrentReadState(WorldState*& tgt, bool block);
00214   //! called when an read access to an entry is complete
00215   void doneReadingState(unsigned int i);
00216   
00217   //! returns true if the specified element of #states has been marked completed
00218   bool isComplete(unsigned int idx) const;
00219   //! returns index of buffer in #states to use for write request
00220   unsigned int selectWriteState(unsigned int frame, bool block) const { order_t::index_t idx; return selectWriteState(frame,block,idx); }
00221   //! returns index of buffer in #states to use for write request, stores index of corresponding entry of #order in @a idx
00222   unsigned int selectWriteState(unsigned int frame, bool block, order_t::index_t& idx) const;
00223   //! called when access to an entry for writing is requested
00224   unsigned int getCurrentWriteState(WorldState*& tgt, unsigned int frame, bool block);
00225   //! called when an write access to an entry is complete
00226   void doneWritingState(unsigned int i, bool completed);
00227   
00228   //! entries to hand out
00229   WorldState states[NUM_STATES];
00230   //! serial numbers of corresponding entries in #states, set when writing begins, should be monotonically increasing relative to #order (i.e. if you run through #order and look at corresponding values in #frames, should be monotonically increasing serial numbers)
00231   unsigned int frames[NUM_STATES];
00232   //! flag set when a reader is blocking for writing to finish, until read is satisified
00233   unsigned int reading[NUM_STATES];
00234   //! count of writers in line for access (occurs when a writer is blocking on another writer of the same frame, or no other frames are free)
00235   unsigned int writing[NUM_STATES];
00236   //! the status is intended as a bitfield to support communication between writers if they need to cooperatively fill out an entry
00237   /*! The value is set to 0 before handing out to a writer with a new frame number */
00238   unsigned int status[NUM_STATES];
00239 #ifdef PLATFORM_APERIOS
00240   //! this lock indicates/controls whether the state is available for reading
00241   /*! The lock is set before handing out to a writer with a new frame number, and released
00242    *  when a writer has marked the entry complete (via the WriteRequest upon releaseResource()) */
00243   MutexLock<ProcessID::NumProcesses> complete[NUM_STATES];
00244 #else
00245   //! this semaphore is set to positive value when writing begins, and then lowered to zero when complete
00246   /*! A semaphore is used on "normal" systems because the MutexLock also implies a
00247    *  thread lock, which we actually don't want in this case because a different thread
00248    *  may complete the entry than the one which started it.  Since Aperios does not allow
00249    *  multithreading, we don't have to worry about it there.
00250    *
00251    *  As an aside, we @e could use this to store #writing, but that would only be feasible if
00252    *  Aperios gave us semaphores.  Bah. */
00253   SemaphoreManager::semid_t complete[NUM_STATES];
00254 #endif
00255   
00256   //! locks to be acquired before handing out corresponding #states entry for writing
00257   MutexLock<ProcessID::NumProcesses> writeLocks[NUM_STATES];
00258   //! lock on WorldStatePool's own members
00259   MutexLock<ProcessID::NumProcesses> lock;
00260 
00261 #ifdef PLATFORM_APERIOS
00262   //! the current state in use by each process
00263   WorldState* stateLookupMap[ProcessID::NumProcesses];
00264 #endif
00265 
00266 private:
00267   WorldStatePool(const WorldStatePool&); //!< this shouldn't be called...
00268   WorldStatePool& operator=(const WorldStatePool&); //!< this shouldn't be called...
00269 };
00270 
00271 /*! @file
00272  * @brief 
00273  * @author Ethan Tira-Thompson (ejt) (Creator)
00274  *
00275  * $Author: ejt $
00276  * $Name: tekkotsu-3_0 $
00277  * $Revision: 1.15 $
00278  * $State: Exp $
00279  * $Date: 2006/09/19 05:39:33 $
00280  */
00281 
00282 #endif

Tekkotsu v3.0
Generated Wed Oct 4 00:03:47 2006 by Doxygen 1.4.7