Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

WorldStatePool.cc

Go to the documentation of this file.
00001 #include "WorldStatePool.h"
00002 #include "Shared/MarkScope.h"
00003 #include "Shared/debuget.h"
00004 #include "IPC/RCRegion.h"
00005 #ifndef PLATFORM_APERIOS
00006 #  include "Events/EventRouter.h"
00007 #  include "local/LoadFileThread.h"
00008 #endif
00009 
00010 //better to put this here instead of the header
00011 using namespace std; 
00012 
00013 WorldStatePool::WorldStatePool() : order(), lock() {
00014   //initialize order with 1,2,3...NUM_STATES
00015   while(order.size()<order.getMaxCapacity()) {
00016     unsigned int i=order.size();
00017     reading[i]=writing[i]=frames[i]=0;
00018     status[i]=0;
00019 #ifdef PLATFORM_APERIOS
00020     stateLookupMap[i]=NULL;
00021 #else
00022     complete[i]=MutexLockBase::getSemaphoreManager()->getSemaphore();
00023 #endif
00024     order.push_back(i);
00025   }
00026 #ifdef PLATFORM_APERIOS
00027   complete[order.size()-1].lock(ProcessID::getID());
00028 #else
00029   MutexLockBase::getSemaphoreManager()->setValue(complete[order.size()-1],1);
00030 #endif
00031 }
00032 
00033 void WorldStatePool::doUseResource(Data& d) {
00034   Request& r=static_cast<Request&>(d);
00035   if(r.isRead) {
00036     ReadRequest& rd = static_cast<ReadRequest&>(d);
00037     if(rd.depth++==0) {
00038       rd.prev=rd.tgt;
00039       rd.bufUsed=getCurrentReadState(rd.tgt,rd.bl);
00040       //cout << ProcessID::getIDStr() << " reading " << rd.bufUsed << " (" << state << ")" << endl;
00041     }
00042   } else {
00043     WriteRequest& write = static_cast<WriteRequest&>(d);
00044     if(write.depth++==0) {
00045       useResource(write.srcRequest);
00046       write.prev=write.tgt;
00047       write.bufUsed=getCurrentWriteState(write.tgt,write.frame,write.bl);
00048       if(write.bufUsed!=-1U) {
00049         write.setStatus(status[write.bufUsed]);
00050         write.setComplete(isComplete(write.bufUsed));
00051         //cout << ProcessID::getIDStr() << " writing " << write.bufUsed << " (" << state << ")" << endl;
00052       }
00053     }
00054   }
00055 #ifdef PLATFORM_APERIOS
00056   stateLookupMap[ProcessID::getID()]=state;
00057 #endif
00058 }
00059 void WorldStatePool::doReleaseResource(Data& d) {
00060   Request& r=static_cast<Request&>(d);
00061   if(r.isRead) {
00062     ReadRequest& rd = static_cast<ReadRequest&>(d);
00063     if(rd.depth==0)
00064       throw std::underflow_error("WorldStatePool read resource usage underflow");
00065     if(--rd.depth==0) {
00066       if(rd.bufUsed!=-1U) {
00067         doneReadingState(rd.bufUsed);
00068         rd.bufUsed=-1U;
00069       }
00070       rd.tgt=rd.prev;
00071     }
00072   } else {
00073     WriteRequest& write = static_cast<WriteRequest&>(d);
00074     if(write.depth==0)
00075       throw std::underflow_error("WorldStatePool write resource usage underflow");
00076     if(--write.depth==0) {
00077       if(write.bufUsed!=-1U) {
00078         status[write.bufUsed]=write.getStatus();
00079         doneWritingState(write.bufUsed,write.getComplete());
00080         write.bufUsed=-1U;
00081       }
00082       write.tgt=write.prev;
00083       releaseResource(write.srcRequest);
00084     }
00085   }
00086 #ifdef PLATFORM_APERIOS
00087   stateLookupMap[ProcessID::getID()]=state;
00088 #endif
00089 }
00090 
00091 #ifdef PLATFORM_APERIOS
00092 
00093 WorldStatePool::UpdateInfo* WorldStatePool::isUnread(OSensorFrameVectorData& msg, unsigned int& lastFrameNumber) {
00094   //cout << ProcessID::getIDStr() << " checking tgtFr=" << msg.GetInfo(0)->frameNumber << " lastFr=" << lastFrameNumber << endl;
00095   
00096   static UpdateInfo info; // avoiding heap allocation by using static here, but this means not threadsafe (shouldn't be a problem...)
00097   info.msg=&msg;
00098   info.frameNumber=msg.GetInfo(0)->frameNumber;
00099   unsigned int toUse=selectWriteState(info.frameNumber,true);
00100 #ifdef DEBUG
00101   info.intendedBuf=toUse;
00102 #endif
00103   if(toUse==-1U)
00104     return NULL; //error occurred, should already be reported
00105   //cout << ProcessID::getIDStr() << " Intending to use " << toUse << " holding frame " << frames[toUse] << " marked " << (isComplete(toUse)?"complete":"not complete") << " with status " << status[toUse] << " for frame " << info.frameNumber << endl;
00106   if(frames[toUse]==info.frameNumber && isComplete(toUse)) {
00107     if(info.frameNumber-lastFrameNumber!=NumFrames)
00108       cout << ProcessID::getIDStr() << " dropped " << (info.frameNumber-lastFrameNumber-NumFrames)/NumFrames << " sensor frame(s)" << endl;
00109     lastFrameNumber=info.frameNumber;
00110     //cout << ProcessID::getIDStr() << " decided frame is complete or nothing to contribute" << endl;
00111     return NULL;
00112   }
00113   return &info;
00114 }
00115 
00116 #else //PLATFORM_LOCAL
00117 
00118 WorldStatePool::UpdateInfo* WorldStatePool::isUnread(const RCRegion& msg, unsigned int curFrameNumber, unsigned int& lastFrameNumber, bool /*haveFeedback*/, bool /*motionOverride*/) {
00119   //cout << ProcessID::getIDStr() << " checking tgtFr=" << curFrameNumber << " lastFr=" << lastFrameNumber << " fdbkAv=" << haveFeedback << " ovrd="<<motionOverride << endl;
00120   
00121   static UpdateInfo info; // avoiding heap allocation by using static here, but this means not threadsafe (shouldn't be a problem...)
00122   info.payload=LoadFileThread::deserializeHeader(msg.Base(),msg.Size(),&info.verbose,&info.frameNumber,&info.filename,&info.dataInQueue,&info.payloadSize);
00123   if(info.payload==NULL)
00124     throw std::runtime_error("deserialization of sensor update header failed");
00125   if(info.frameNumber<curFrameNumber) {
00126     // this means there's a newer one already in the queue -- skip this (and we'll report the drop next time -- no assignment to lastFrameNumber)
00127     return NULL;
00128   }
00129   if(info.payloadSize==0)
00130     info.payload=NULL;
00131   
00132   unsigned int toUse=selectWriteState(info.frameNumber,true);
00133 #ifdef DEBUG
00134   info.intendedBuf=toUse;
00135 #endif
00136   if(toUse==-1U)
00137     return NULL; //error occurred, should already be reported
00138   if(info.verbose) {
00139     if(info.payloadSize==0)
00140       cout << ProcessID::getIDStr() << " received sensor heartbeat at " << get_time() << endl;
00141     else
00142       cout << ProcessID::getIDStr() << " received sensor data \"" << info.filename << "\" at " << get_time() << endl;
00143   }
00144   //cout << "Intending to use " << toUse << " holding frame " << frames[toUse] << " marked " << (isComplete(toUse)?"complete":"not complete") << " with status " << status[toUse] << " for frame " << info.frameNumber << endl;
00145   if(frames[toUse]==info.frameNumber && isComplete(toUse)) {
00146     if(info.frameNumber-lastFrameNumber!=1 && info.verbose)
00147       cout << ProcessID::getIDStr() << " dropped " << (info.frameNumber-lastFrameNumber-1) << " sensor frame(s)" << endl;
00148     lastFrameNumber=info.frameNumber;
00149     //cout << ProcessID::getIDStr() << " decided frame is complete or nothing to contribute" << endl;
00150     return NULL;
00151   }
00152   return &info;
00153 }
00154 
00155 bool WorldStatePool::read(const WorldStatePool::UpdateInfo& info, WriteRequest& wsw, bool feedbackGenerated, bool zeroPIDFeedback, const PostureEngine* feedback/*=NULL*/) {
00156   //cout << ProcessID::getIDStr() << " writing " << wsw.frame << " state=" << state << "(" << wsw.bufUsed << ") source=" << wsw.src << "("<<wsw.srcRequest.bufUsed<<") status="<<wsw.getStatus() << " complete="<<wsw.getComplete() << endl;
00157   
00158   ASSERT(feedbackGenerated || feedback==NULL, "feedbackGenerated is false, yet feedback was supplied?");
00159   if(wsw.getComplete()) {
00160     // this could happen if both processes were trying to get the write lock at the same time
00161     // and the first in filled everything by itself (i.e. no feedback required, or the one with feedback was there first)
00162     return true;
00163   } 
00164   ASSERT((wsw.getStatus()&FEEDBACK_APPLIED)==0, "feedback applied, but apparently not completed "<<wsw.getComplete());
00165   //ASSERT(wsw.bufUsed==info.intendedBuf,"read() not using expected state buffer"); //not a problem (I think? just fell an even number of frames behind the other process?)
00166   
00167   if((wsw.getStatus()&SENSORS_APPLIED) != 0) {
00168     // sensors already applied, just need to do feedback, if any
00169     if(feedback!=NULL) {
00170       // have feedback, copy it in
00171       if(zeroPIDFeedback) {
00172         // copy all values, regardless of PID values
00173         for(unsigned int i=0; i<NumOutputs; i++)
00174           state->outputs[i]=feedback->getOutputCmd(i).value;
00175       } else {
00176         // only copy non-zero PID values
00177         for(unsigned int i=0; i<NumOutputs; i++)
00178           if(!isZeroPID(wsw.src,i))
00179             state->outputs[i]=feedback->getOutputCmd(i).value;
00180       }
00181       wsw.setStatus(wsw.getStatus() | FEEDBACK_APPLIED);
00182     }
00183   
00184   } else {
00185     // need to do sensors (if present), as well as feedback (if any)
00186     static PostureEngine pose; //this is already thread-unsafe, might as well use another static tmp here to avoid initialization cost
00187     if(info.payload==NULL) {
00188       
00189       // no new sensors, but still have to copy over old values (downside of having multiple state buffers)
00190       pose.setLoadedSensors(wsw.src);
00191       if(!feedbackGenerated) {
00192         // no feedback either, take all of the old positions
00193         pose.takeSnapshot(*wsw.src);
00194         pose.setWeights(1);
00195       } else if(zeroPIDFeedback) {
00196         // feedback is applied, regardless of PID settings
00197         if(feedback!=NULL) {
00198           for(unsigned int i=0; i<NumPIDJoints; ++i)
00199             pose(i+PIDJointOffset)=feedback->getOutputCmd(i).value;
00200           wsw.setStatus(wsw.getStatus() | FEEDBACK_APPLIED);
00201         } //else wait for process with feedback to fill it in
00202       } else {
00203         // feedback is only applied to non-zero PIDs, the rest need to take the previous value
00204         if(feedback!=NULL) {
00205           // this process does have the feedback, fill in everything 
00206           for(unsigned int i=0; i<NumPIDJoints; ++i)
00207             if(isZeroPID(wsw.src,i))
00208               pose(i+PIDJointOffset)=wsw.src->outputs[i+PIDJointOffset];
00209             else
00210               pose(i+PIDJointOffset)=feedback->getOutputCmd(i).value;
00211           wsw.setStatus(wsw.getStatus() | FEEDBACK_APPLIED);
00212         } else {
00213           // this process doesn't have the feedback, only fill in the non-feedback
00214           for(unsigned int i=0; i<NumPIDJoints; ++i)
00215             if(isZeroPID(wsw.src,i))
00216               pose(i+PIDJointOffset)=wsw.src->outputs[i+PIDJointOffset];
00217         }
00218       }
00219       
00220     } else {
00221       
00222       unsigned int stateFrame=state->frameNumber; //back up current frame number so we can restore it after the load 
00223       // new sensor values are provided
00224       pose.setLoadedSensors(state);
00225       //cout << "Parsing sensor data" << endl;
00226       if(!pose.loadBuffer(info.payload,info.payloadSize)) {
00227         cerr << "ERROR: Corrupted sensor readings received by Main" << endl;
00228         return false;
00229       }
00230       state->frameNumber=stateFrame; // restore the frame number over what was loaded from the file so state->read doesn't unnecessarily skip it below
00231       
00232       if(stateFrame>wsw.src->frameNumber)
00233         return false; // I'm not sure how this occurs, but sometimes (if doing a lot of paging/kernel stuff that screws up the scheduler) the source is newer than our update
00234       ASSERT(stateFrame<wsw.src->frameNumber || stateFrame==1 && wsw.src->frameNumber==1,"already updated (" << stateFrame << " vs " << wsw.src->frameNumber << ")?  So why did " << ProcessID::getIDStr() << " parse...");
00235       
00236       if(!feedbackGenerated) {
00237         // no feedback, so just use all of the values we just loaded
00238         // weight should already be set to 1 from load (otherwise would need pose.setWeights(1) or assign wsw.src values)
00239         for(unsigned int i=0; i<NumOutputs; ++i)
00240           ASSERT(pose(i).weight>0,"zero weight found after loading"); // one would expect the compiler to no-op this loop ifndef DEBUG
00241       } else if(zeroPIDFeedback) {
00242         // feedback is applied, regardless of PID settings
00243         if(feedback!=NULL) {
00244           for(unsigned int i=0; i<NumPIDJoints; ++i)
00245             pose(i+PIDJointOffset)=feedback->getOutputCmd(i).value;
00246           wsw.setStatus(wsw.getStatus() | FEEDBACK_APPLIED);
00247         } //else wait for process with feedback to fill it in
00248       } else {
00249         // feedback is only applied to non-zero PIDs, the rest need to take the previous value
00250         if(feedback!=NULL) {
00251           // this process does have the feedback, fill in everything 
00252           for(unsigned int i=0; i<NumPIDJoints; ++i)
00253             if(!isZeroPID(wsw.src,i))
00254               pose(i+PIDJointOffset)=feedback->getOutputCmd(i).value;
00255             else
00256               ASSERT(pose(i+PIDJointOffset).weight!=0,"zero weight found after loading");
00257           wsw.setStatus(wsw.getStatus() | FEEDBACK_APPLIED);
00258         } else {
00259           // this process doesn't have the feedback, only fill in the non-feedback
00260           for(unsigned int i=0; i<NumPIDJoints; ++i)
00261             if(!isZeroPID(wsw.src,i))
00262               pose(i+PIDJointOffset).weight=0; // don't apply sensor values to non-zero pids (this will come from feedback later)
00263             else
00264               ASSERT(pose(i+PIDJointOffset).weight!=0,"zero weight found after loading");
00265         }
00266       }
00267     }
00268     state->read(pose,wsw.src,erouter);
00269     //ASSERT((state->frameNumber-5)/4==wsw.frame,"state frame number and message serial number are desynchronized " << state->frameNumber << " vs " << wsw.frame);
00270     state->frameNumber=wsw.frame*4+5; //lets dropped messages be skipped
00271     wsw.setStatus(wsw.getStatus() | SENSORS_APPLIED);
00272   }
00273   wsw.setComplete(feedbackGenerated && wsw.getStatus()==(SENSORS_APPLIED|FEEDBACK_APPLIED) || !feedbackGenerated && (wsw.getStatus()&SENSORS_APPLIED)!=0);
00274   //cout << ProcessID::getIDStr() << " fr=" << info.frameNumber << " pyld=" << info.payloadSize << " fdbkG=" << feedbackGenerated << " pidfdbk=" << zeroPIDFeedback << " fdbk=" << feedback << " set status="<<wsw.getStatus() << " complete=" << wsw.getComplete() << endl;
00275   return true;
00276 }
00277 
00278 #endif
00279 
00280 
00281 unsigned int WorldStatePool::getCurrentReadState(WorldState*& tgt, bool block) {
00282   unsigned int toUse=-1U;
00283   if(block) {
00284     while(toUse!=order.end()) {
00285       {
00286         MarkScope l(lock);
00287         if(toUse!=-1U) { //newer frame added while we were waiting
00288           //std::cerr << ProcessID::getID() << " WARNING WorldStatePool: whoa new frame while blocking read " << toUse << " vs " << order.back() << endl;
00289           reading[toUse]--;
00290         }
00291         toUse=order.back();
00292         reading[toUse]++;
00293       } //release WorldStatePool lock to allow new readers to come through
00294 #ifdef PLATFORM_APERIOS
00295       MarkScope wl(complete[toUse]); //now block until writing lock is released
00296 #else
00297       MutexLockBase::getSemaphoreManager()->testZero(complete[toUse],true); //block until complete semaphore is released (set to 0)
00298 #endif
00299     }
00300   } else {
00301     MarkScope l(lock);
00302     order_t::index_t idx;
00303     for(idx=order.prev(order.end()); idx!=order.end(); idx=order.prev(idx)) {
00304       toUse=order[idx];
00305       if(isComplete(toUse))
00306         break;
00307     }
00308     if(idx==order.end()) {
00309       std::cerr << "ERROR: WorldStatePool unable to read state because none available" << std::endl;
00310       return -1U;
00311     }
00312     reading[toUse]++;
00313   }
00314   //std::cout << ProcessID::getID() << " reading " << toUse << endl;
00315   //ASSERTRETVAL(toUse!=-1U,"toUse was not chosen!",-1U);
00316   tgt=&states[toUse];
00317   return toUse;
00318 }
00319 void WorldStatePool::doneReadingState(unsigned int i) {
00320   MarkScope l(lock);
00321   reading[i]--;
00322   //std::cout << ProcessID::getID() << " reading " << i << " - done" << endl;
00323 }
00324 
00325 bool WorldStatePool::isComplete(unsigned int idx) const {
00326 #ifdef PLATFORM_APERIOS
00327   return static_cast<unsigned int>(complete[idx].owner())==MutexLockBase::NO_OWNER;
00328 #else
00329   return MutexLockBase::getSemaphoreManager()->testZero(complete[idx],false);
00330 #endif
00331 }
00332 
00333 unsigned int WorldStatePool::selectWriteState(unsigned int frame, bool block, order_t::index_t& idx) const {
00334   //check the end to verify a newer (or equivalent) update isn't already in progress
00335   idx=order.prev(order.end());
00336   unsigned int toUse=order[idx];
00337   if(frames[toUse]>frame) {
00338     // can happen if one thread (say MotionExec running a motion update) takes too long, another thread (say motion gotSensors updater) blocks on it, is delayed too long
00339     // we'll leave it up to the client to detect and display an appropriate message if dropping updates is a problem.
00340     //std::cerr << "WARNING: WorldStatePool found a newer write already in progress (writing " << frame << ", found " << frames[toUse] << ")" << std::endl;
00341     return -1U;
00342   }
00343   if(frames[toUse]!=frame || !block) {
00344     //start at the beginning and run forward to get the oldest unreferenced entry
00345     order_t::index_t fallback=order.end();
00346     for(idx=order.begin(); idx!=order.end(); idx=order.next(idx)) {
00347       toUse=order[idx];
00348       if(frames[toUse]>=frame) {
00349         idx=order.end();
00350         break;
00351       }
00352       if(writing[toUse]==0) { //note: could be incomplete -- if this is the oldest frame and it's not currently being written to, but yet wasn't completed, we'll recycle it
00353         if(reading[toUse]==0)
00354           break; //found our entry!
00355       } else if(fallback==order.end())
00356         fallback=idx; //in case we don't find an unreferenced one later in the list
00357     }
00358     if(idx==order.end()) {
00359       if(block && fallback!=order.end())
00360         toUse=order[idx=fallback];
00361       else {
00362         std::cerr << "ERROR: WorldStatePool unable to update state because none available" << std::endl;
00363         return -1U;
00364       }
00365     }
00366   }
00367   return toUse;
00368 }
00369 
00370 unsigned int WorldStatePool::getCurrentWriteState(WorldState*& tgt, unsigned int frame, bool block) {
00371   unsigned int toUse;
00372   { //artificial scope to limit lock on next line
00373     MarkScope l(lock);
00374     order_t::index_t idx;
00375     toUse=selectWriteState(frame,block,idx);
00376     if(toUse==-1U)
00377       return -1U;
00378     //std::cout << ProcessID::getID() << " writing " << toUse << " (frame " << frame << ")" << endl;
00379     //ASSERTRETVAL(toUse!=-1U,"toUse was not chosen!",-1U);
00380     writing[toUse]++;
00381     if(frames[toUse]!=frame) {
00382       //move this entry to the back of the list, keep frames sorted (in order of write initiation / frame number)
00383       order.erase(idx);
00384       order.push_back(toUse);
00385       frames[toUse]=frame;
00386       status[toUse]=0;
00387 #ifdef PLATFORM_APERIOS
00388       complete[toUse].releaseAll(); //make sure it wasn't an incomplete frame that's being recycled
00389       complete[toUse].lock(ProcessID::getID());
00390 #else
00391       MutexLockBase::getSemaphoreManager()->setValue(complete[toUse],1);
00392 #endif
00393     }
00394   }
00395   if(block) {
00396     writeLocks[toUse].lock(ProcessID::getID());
00397   } else if(!writeLocks[toUse].try_lock(ProcessID::getID())) {
00398     std::cerr << "WARNING: WorldStatePool unable to acquire write lock (blocking reader?).  Trying again..." << std::endl;
00399     writeLocks[toUse].lock(ProcessID::getID());
00400   }
00401   if(toUse!=order.back()) { //newer frame added while we were waiting
00402     // This is a very rare case
00403     //std::cerr << ProcessID::getID() << " WARNING WorldStatePool: whoa new frame while blocking write " << toUse << " vs " << order.back() << endl;
00404     // go back to square one
00405     doneWritingState(toUse,false);
00406     return getCurrentWriteState(tgt,frame,block);
00407   }
00408   tgt=&states[toUse];
00409   return toUse;
00410 }
00411 void WorldStatePool::doneWritingState(unsigned int i, bool completed) {
00412   MarkScope l(lock);
00413   //std::cout << ProcessID::getID() << " writing " << i << " - done" << endl;
00414   writing[i]--;
00415   if(completed) {
00416 #ifdef PLATFORM_APERIOS
00417     if(!isComplete(i))
00418       complete[i].unlock();
00419 #else
00420     MutexLockBase::getSemaphoreManager()->setValue(complete[i],0);
00421 #endif
00422   }
00423   writeLocks[i].unlock();
00424 }
00425 
00426 
00427 
00428 /*! @file
00429  * @brief 
00430  * @author Ethan Tira-Thompson (ejt) (Creator)
00431  *
00432  * $Author: ejt $
00433  * $Name: tekkotsu-3_0 $
00434  * $Revision: 1.21 $
00435  * $State: Exp $
00436  * $Date: 2006/09/28 20:42:51 $
00437  */

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