Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

SemaphoreManager.cc

Go to the documentation of this file.
00001 #ifndef PLATFORM_APERIOS
00002 
00003 #include "SemaphoreManager.h"
00004 #include "Shared/debuget.h"
00005 #include "Thread.h"
00006 #include <cstdlib>
00007 #include <cerrno>
00008 #include <cstdio>
00009 #include <exception>
00010 #include <stdexcept>
00011 #include <iostream>
00012 #include <sys/types.h>
00013 #include <sys/sem.h>
00014 #include <cstring>
00015 
00016 #if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__MACH__)
00017 /* union semun is defined by including <sys/sem.h> */
00018 #else
00019 /*! @cond INTERNAL */
00020 /* according to X/OPEN we have to define it ourselves */
00021 union semun {
00022   int val;                  /* value for SETVAL */
00023   struct semid_ds *buf;     /* buffer for IPC_STAT, IPC_SET */
00024   unsigned short *array;    /* array for GETALL, SETALL */
00025   /* Linux specific part: */
00026   struct seminfo *__buf;    /* buffer for IPC_INFO */
00027 };
00028 /*! @endcond */
00029 #endif
00030 
00031 using namespace std;
00032 
00033 SemaphoreManager::SemaphoreManager()
00034 : sems(), nsem(sems_t::MAX_ENTRIES), semid(-1), mysem(sems.end()), refc(sems.end())
00035 {init();}
00036 
00037 SemaphoreManager::SemaphoreManager(unsigned int numRequest)
00038 : sems(), nsem(numRequest+2), semid(-1), mysem(sems.end()), refc(sems.end())
00039 {init();}
00040 
00041 void SemaphoreManager::init() {
00042   if(nsem>sems_t::MAX_ENTRIES) {
00043     cout << "SemaphoreManager created with request for " << nsem << " semaphores, but sems_t::MAX_ENTRIES is " << sems_t::MAX_ENTRIES << endl;
00044     nsem=sems_t::MAX_ENTRIES;
00045   }
00046   unsigned int req=nsem;
00047 
00048   //the seminfo structure is kernel-private and I can't find a portable way to access
00049   //SEMMSL without it.
00050   /*semun params; 
00051   seminfo info;
00052   params.__buf=info;
00053   if(semctl(semid,-1,IPC_INFO,params)<0) {
00054     perror("WARNING: SemaphoreManager query (semctl)");
00055     //we'll just forge ahead with the default value...
00056     //exit(EXIT_FAILURE);
00057   } else {
00058     if(nsem>info.semmsl)
00059       nsem=info.semmsl;
00060   }*/
00061   
00062   //So instead we'll do a binary search for the size:
00063   unsigned int lowbound=0; //inclusive
00064   unsigned int highbound=nsem; //inclusive
00065   //note that first pass asks for highbound - if it succeeds there's no search
00066   while(lowbound!=highbound) {
00067     semid=semget(IPC_PRIVATE,nsem,IPC_CREAT | IPC_EXCL | 0666);
00068     if(semid<0) {
00069       if(errno!=EINVAL && errno!=ENOSPC) {
00070         perror("ERROR: SemaphoreManager upper limit detection (semget)");
00071         exit(EXIT_FAILURE);
00072       }
00073       //too big
00074       highbound=nsem-1;
00075     } else {
00076       //succeeded -- too low?
00077       if(semctl(semid,-1,IPC_RMID)<0) {
00078         perror("ERROR: SemaphoreManager destruction (semctl)");
00079         exit(EXIT_FAILURE);
00080       }
00081       lowbound=nsem;
00082     }
00083     nsem=(lowbound+highbound+1)/2;
00084   }
00085   //get the semaphore set
00086   semid=semget(IPC_PRIVATE,nsem,IPC_CREAT | IPC_EXCL | 0666);
00087   if(semid<0) {
00088     perror("ERROR: SemaphoreManager construction (semget)");
00089         exit(EXIT_FAILURE);
00090   }
00091   if(nsem!=req)
00092     cerr << "WARNING: System can only allocate " << nsem << " semaphores per set for id=" << semid << " (SEMMSL or SEMMNS max reached). " << req << " were requested." << endl;
00093     
00094   //initialize to 0 (unlocked)
00095   unsigned short int semvals[sems_t::MAX_ENTRIES];
00096   for(unsigned int i=0; i<nsem; i++)
00097     semvals[i]=0;
00098   semun params; 
00099   params.array=semvals;
00100   if(semctl(semid,-1,SETALL,params)<0) {
00101     perror("ERROR: SemaphoreManager construction (semctl)");
00102     exit(EXIT_FAILURE);
00103   }
00104   
00105   //burn any extra ids we couldn't actually get from the system
00106   if(nsem!=sems_t::MAX_ENTRIES) {
00107     //first use up all the IDs
00108     while(sems.new_back()!=sems.end()) {}
00109     //now free the first nsem
00110     for(unsigned int i=0; i<nsem; i++)
00111       sems.pop_front();
00112   }
00113     
00114   //take one for ourselves to lock handing out semaphores
00115   mysem=sems.new_front();
00116   if(mysem==sems.end()) {
00117     cerr << "ERROR: could not allocate SemaphoreManager internal lock" << endl;
00118     exit(EXIT_FAILURE);
00119   }
00120   //only one semaphore can be in the process of creation or release at any given time, we have the lock
00121   setValue(mysem,1);
00122   //take another for ourselves to use as a reference count on the semaphore set
00123   refc=sems.new_front();
00124   if(refc==sems.end()) {
00125     cerr << "ERROR: could not allocate SemaphoreManager reference counter" << endl;
00126     exit(EXIT_FAILURE);
00127   }
00128   //reference count starts at 0 -- underflow is signalled by negative count
00129   setValue(refc,0);
00130   //cerr << "Semaphore set " << semid << " created" << endl;
00131 }
00132 
00133 SemaphoreManager::SemaphoreManager(const SemaphoreManager& mm)
00134 : sems(), nsem(mm.nsem), semid(mm.semid), mysem(mm.mysem), refc(mm.refc)
00135 {
00136   ASSERT(mm.semid!=-1,"Copy of SemaphoreManager with invalid semid!");
00137   lower(mysem,1); //get a lock on the new set
00138   sems=mm.sems; //we didn't copy sems earlier because we need a lock for this
00139   raise(refc,1); //add 1 to reference counter for our new set
00140   raise(mysem,1); //release lock on new set
00141   //cerr << "Semaphore set " << semid << " copied" << endl;
00142 }
00143 
00144 SemaphoreManager& SemaphoreManager::operator=(const SemaphoreManager& mm) {
00145   if(&mm==this)
00146     return *this;
00147   //ASSERT(semid!=-1,"Assignment to SemaphoreManager with invalid semid!");
00148   //ASSERT(mm.semid!=-1,"Assignment of SemaphoreManager with invalid semid!");
00149   if(semid==mm.semid) {
00150     //both reference the same set, just update some fields
00151     if(mm.semid!=-1)
00152       mm.lower(mm.mysem,1); //get a lock on the new set
00153     mysem=mm.mysem;
00154     sems=mm.sems;
00155     nsem=mm.nsem;
00156     if(mm.semid!=-1)
00157       mm.raise(mm.mysem,1); //release lock on new set
00158   } else {
00159     //we're replacing one set with the other, need to dereference our current set
00160     //cerr << "Semaphore set " << semid << " dereferenced" << endl;
00161     if(semid!=-1) {
00162       lower(mysem,1); //lock current set
00163       if(!lower(refc,1,false)) { //remove 1 from the reference counter for our current set
00164         //ran out of references to the old set, delete it
00165         //cerr << "Semaphore set " << semid << " deleted" << endl;
00166         sems.erase(refc);
00167         sems.erase(mysem);
00168         for(semid_t it=sems.begin(); it!=sems.end(); it=sems.next(it))
00169           if(it<nsem)
00170             cerr << "Warning: semaphore id " << it << " from set " << semid << " was still active when the set was dereferenced" << endl;
00171         if(semctl(semid,-1,IPC_RMID)<0) {
00172           perror("ERROR: SemaphoreManager deletion from operator= (semctl)");
00173           exit(EXIT_FAILURE);
00174         }
00175         semid=-1;
00176       } else
00177         raise(mysem,1); // it's still referenced, unlock for others
00178     }
00179     if(mm.semid!=-1)
00180       mm.lower(mm.mysem,1); //get a lock on the new set
00181     mysem=mm.mysem;
00182     sems=mm.sems;
00183     nsem=mm.nsem;
00184     semid=mm.semid;
00185     if(mm.semid!=-1) {
00186       raise(refc=mm.refc,1); //add 1 to reference counter for our new set
00187       mm.raise(mm.mysem,1); //release lock on new set
00188     }
00189     //cerr << "Semaphore set " << semid << " assigned" << endl;
00190   }
00191   return *this;
00192 }
00193 
00194 SemaphoreManager::~SemaphoreManager() {
00195   if(semid==-1)
00196     return;
00197   //cerr << "Semaphore set " << semid << " dereferenced" << endl;
00198   lower(mysem,1); //lock current set
00199   if(!lower(refc,1,false)) { //remove 1 from the reference counter for our current set
00200     //ran out of references to the old set, delete it
00201     //cerr << "Semaphore set " << semid << " deleted" << endl;
00202     /* // on the final shutdown, the process-local copies can't tell if semaphores were freed remotely
00203     sems.erase(refc);
00204     sems.erase(mysem);
00205     for(semid_t it=sems.begin(); it!=sems.end(); it=sems.next(it))
00206       cerr << "Warning: semaphore id " << it << " from set " << semid << " was still active when the set was dereferenced" << endl;
00207     */
00208     if(semctl(semid,-1,IPC_RMID)<0) {
00209       perror("ERROR: SemaphoreManager deletion from destructor (semctl)");
00210       exit(EXIT_FAILURE);
00211     }
00212     semid=-1;
00213   } else
00214     raise(mysem,1);
00215 }
00216 
00217 void SemaphoreManager::aboutToFork() {
00218   raise(refc,1);
00219 }
00220 
00221 void SemaphoreManager::faultShutdown() {
00222   if(semid==-1)
00223     return; // already released set
00224   if(semctl(semid,-1,IPC_RMID)<0)
00225     perror("WARNING: SemaphoreManager faultShutdown (semctl)");
00226   semid=-1;
00227 }
00228 
00229 SemaphoreManager::semid_t SemaphoreManager::getSemaphore() {
00230   lower(mysem,1);
00231   semid_t id=sems.new_front();
00232   raise(mysem,1);
00233   if(id!=sems.end())
00234     setValue(id,0);
00235   intrPolicy[id]=INTR_RETRY;
00236   return id;
00237 }
00238 void SemaphoreManager::releaseSemaphore(semid_t id) {
00239   lower(mysem,1);
00240   sems.erase(id);
00241   raise(mysem,1);
00242 }
00243 
00244 bool SemaphoreManager::lower(semid_t id, unsigned int x, bool block/*=true*/) const {
00245   sembuf sb={id,-x,(block?0:IPC_NOWAIT)};
00246   while(true) {
00247     Thread::requestInterruptOnCancel();
00248     int res = semop(semid,&sb,1);
00249     int theErr = errno;
00250     Thread::unrequestInterruptOnCancel();
00251     if(res==0)
00252       break;
00253     if(theErr==EAGAIN)
00254       return false;
00255     if(theErr==EINTR) {
00256       switch(intrPolicy[id]) {
00257         case INTR_CANCEL_VERBOSE:
00258           perror("ERROR: SemaphoreManager unable to lower semaphore (semop)");
00259           cerr << "       semop was interrupted by signal, cancelling lower()";
00260         case INTR_CANCEL:
00261           return false;
00262         case INTR_RETRY_VERBOSE:
00263           perror("ERROR: SemaphoreManager unable to lower semaphore (semop)");
00264           cerr << "       semop was interrupted by signal.  Trying again...";
00265           break;
00266         case INTR_RETRY:
00267           break; //while loop will retry
00268         case INTR_THROW_VERBOSE:
00269           perror("ERROR: SemaphoreManager unable to lower semaphore (semop)");
00270           cerr << "       semop was interrupted by signal.  Throwing exception...";
00271         case INTR_THROW:
00272           throw std::runtime_error("EINTR returned by lower semop");
00273         case INTR_EXIT:
00274           perror("ERROR: SemaphoreManager unable to lower semaphore (semop)");
00275           cerr << "       semop was interrupted by signal.  Exiting...";
00276           exit(EXIT_FAILURE);
00277       }
00278     } else {
00279       if(theErr==EIDRM)
00280         usleep(500000); // probably in the process of shutdown, wait half a second before complaining
00281       cerr << "ERROR: SemaphoreManager unable to lower semaphore (semop): " << strerror(theErr) << endl;
00282       cerr << "       ";
00283       if(theErr==EIDRM) {
00284         cerr << "Semaphore set has been removed.  " << endl;
00285       }
00286       if(theErr==EINVAL) {
00287         cerr << "Semaphore set was deleted.  " << endl;
00288       }
00289       //prevent recuring problems
00290       cerr << "Goodbye" << endl;
00291       exit(EXIT_FAILURE);
00292     }
00293   }
00294   return true;
00295 }
00296 void SemaphoreManager::raise(semid_t id, unsigned int x) const {
00297   sembuf sb={id,x,0};
00298   if(semop(semid,&sb,1)<0) {
00299     perror("ERROR: SemaphoreManager unable to raise semaphore (semop)");
00300   }
00301 }
00302 int SemaphoreManager::getValue(semid_t id) const {
00303   int ans=semctl(semid,id,GETVAL);
00304   if(ans<0)
00305     perror("ERROR: SemaphoreManager getValue (semctl)");
00306   return ans;
00307 }
00308 void SemaphoreManager::setValue(semid_t id, int x) const {
00309   semun params; 
00310   params.val=x;
00311   if(semctl(semid,id,SETVAL,params)<0) {
00312     perror("ERROR: SemaphoreManager::setValue (semctl)");
00313     exit(EXIT_FAILURE);
00314   }
00315 }
00316 int SemaphoreManager::getNumZeroBlockers(semid_t id) const {
00317   int ans=semctl(semid,id,GETZCNT);
00318   if(ans<0)
00319     perror("ERROR: SemaphoreManager getNumZeroBlockers (semctl)");
00320   return ans;
00321 }
00322 bool SemaphoreManager::testZero(semid_t id, bool block/*=true*/) const {
00323   sembuf sb={id,0,(block?0:IPC_NOWAIT)};
00324   while(true) {
00325     Thread::requestInterruptOnCancel();
00326     int res = semop(semid,&sb,1);
00327     int theErr = errno;
00328     Thread::unrequestInterruptOnCancel();
00329     if(res==0)
00330       break;
00331     if(theErr==EAGAIN)
00332       return false;
00333     if(theErr!=EINTR) { // && theErr!=ERESTART?
00334       if(theErr==EIDRM)
00335         usleep(500000); // probably in the process of shutdown, wait half a second before complaining
00336       cerr << "ERROR: SemaphoreManager unable to testZero() (semop): " << strerror(theErr) << '\n';
00337       cerr << "       ";
00338       if(theErr==EIDRM) {
00339         cerr << "Semaphore set has been removed.  " << endl;
00340       } else if(theErr==EINVAL) {
00341         cerr << "Semaphore set was deleted.  " << endl;
00342       } else {
00343         cerr << "Error code was " << theErr << endl;
00344       }
00345       cerr << "Goodbye" << endl;
00346       exit(EXIT_FAILURE);
00347     } else {
00348       switch(intrPolicy[id]) {
00349         case INTR_CANCEL_VERBOSE:
00350           perror("ERROR: SemaphoreManager unable to testZero (semop)");
00351           cerr << "       semop was interrupted by signal, cancelling testZero()";
00352         case INTR_CANCEL:
00353           return false;
00354         case INTR_RETRY_VERBOSE:
00355           perror("ERROR: SemaphoreManager unable to testZero (semop)");
00356           cerr << "       semop was interrupted by signal.  Trying again...";
00357           break;
00358         case INTR_RETRY:
00359           break; //while loop will retry
00360         case INTR_THROW_VERBOSE:
00361           perror("ERROR: SemaphoreManager unable to testZero (semop)");
00362           cerr << "       semop was interrupted by signal.  Throwing exception...";
00363         case INTR_THROW:
00364           throw std::runtime_error("EINTR returned by testZero semop");
00365         case INTR_EXIT:
00366           perror("ERROR: SemaphoreManager unable to testZero (semop)");
00367           cerr << "       semop was interrupted by signal.  Exiting...";
00368           exit(EXIT_FAILURE);
00369       }
00370     }
00371   }
00372   return true;
00373 }
00374 bool SemaphoreManager::testZero_add(semid_t id, int x, bool testblock/*=true*/, bool addblock/*=true*/) const {
00375   sembuf sb[2]={
00376     {id,0,(testblock?0:IPC_NOWAIT)},
00377     {id,x,(addblock?0:IPC_NOWAIT)}
00378   };
00379   while(true) {
00380     Thread::requestInterruptOnCancel();
00381     int res = semop(semid,sb,2);
00382     int theErr = errno;
00383     try {
00384       Thread::unrequestInterruptOnCancel();
00385     } catch(...) { // thread cancelled
00386       if(res==0) { // if we weren't interrupted, then semop was successful, undo it
00387 #ifdef DEBUG
00388         ASSERT(lower(id,x,false),"Could not undo semop after thread cancel in testZero_add (would block?)");
00389 #else
00390         lower(id,x,true); // block should not occur
00391 #endif
00392       }
00393       throw;
00394     }
00395     if(res==0)
00396       break;
00397     if(theErr==EAGAIN)
00398       return false;
00399     if(theErr!=EINTR) { // && theErr!=ERESTART?
00400       if(theErr==EIDRM)
00401         usleep(500000); // probably in the process of shutdown, wait half a second before complaining
00402       cerr << "ERROR: SemaphoreManager unable to testZero_add() (semop): " << strerror(theErr) << '\n';
00403       cerr << "       ";
00404       if(theErr==EIDRM) {
00405         cerr << "Semaphore set has been removed.  " << endl;
00406       } else if(theErr==EINVAL) {
00407         cerr << "Semaphore set was deleted.  " << endl;
00408       } else {
00409         cerr << "Error code was " << theErr << endl;
00410       }
00411       cerr << "Goodbye" << endl;
00412       exit(EXIT_FAILURE);
00413     } else {
00414       switch(intrPolicy[id]) {
00415         case INTR_CANCEL_VERBOSE:
00416           perror("ERROR: SemaphoreManager unable to testZero_add (semop)");
00417           cerr << "       semop was interrupted by signal, cancelling testZero_add()";
00418         case INTR_CANCEL:
00419           return false;
00420         case INTR_RETRY_VERBOSE:
00421           perror("ERROR: SemaphoreManager unable to testZero_add (semop)");
00422           cerr << "       semop was interrupted by signal.  Trying again...";
00423           break;
00424         case INTR_RETRY:
00425           break; //while loop will retry
00426         case INTR_THROW_VERBOSE:
00427           perror("ERROR: SemaphoreManager unable to testZero_add (semop)");
00428           cerr << "       semop was interrupted by signal.  Throwing exception...";
00429         case INTR_THROW:
00430           throw std::runtime_error("EINTR returned by testZero_add semop");
00431         case INTR_EXIT:
00432           perror("ERROR: SemaphoreManager unable to testZero_add (semop)");
00433           cerr << "       semop was interrupted by signal.  Exiting...";
00434           exit(EXIT_FAILURE);
00435       }
00436     }
00437   }
00438   return true;
00439 }
00440 bool SemaphoreManager::add_testZero(semid_t id, int x, bool addblock/*=true*/, bool testblock/*=true*/) const {
00441   sembuf sb[2]={
00442     {id,x,(addblock?0:IPC_NOWAIT)},
00443     {id,0,(testblock?0:IPC_NOWAIT)}
00444   };
00445   while(true) {
00446     Thread::requestInterruptOnCancel();
00447     int res = semop(semid,sb,2);
00448     int theErr = errno;
00449     Thread::unrequestInterruptOnCancel();
00450     if(res==0)
00451       break;
00452     if(theErr==EAGAIN)
00453       return false;
00454     if(theErr!=EINTR) { // && theErr!=ERESTART?
00455       if(theErr==EIDRM)
00456         usleep(500000); // probably in the process of shutdown, wait half a second before complaining
00457       cerr << "ERROR: SemaphoreManager unable to add_testZero() (semop): " << strerror(theErr) << '\n';
00458       cerr << "       ";
00459       if(theErr==EIDRM) {
00460         cerr << "Semaphore set has been removed.  " << endl;
00461       } else if(theErr==EINVAL) {
00462         cerr << "Semaphore set was deleted.  " << endl;
00463       } else {
00464         cerr << "Error code was " << theErr << endl;
00465       }
00466       cerr << "Goodbye" << endl;
00467       exit(EXIT_FAILURE);
00468     } else {
00469       switch(intrPolicy[id]) {
00470         case INTR_CANCEL_VERBOSE:
00471           perror("ERROR: SemaphoreManager unable to add_testZero (semop)");
00472           cerr << "       semop was interrupted by signal, cancelling add_testZero()";
00473         case INTR_CANCEL:
00474           return false;
00475         case INTR_RETRY_VERBOSE:
00476           perror("ERROR: SemaphoreManager unable to add_testZero (semop)");
00477           cerr << "       semop was interrupted by signal.  Trying again...";
00478           break;
00479         case INTR_RETRY:
00480           break; //while loop will retry
00481         case INTR_THROW_VERBOSE:
00482           perror("ERROR: SemaphoreManager unable to add_testZero (semop)");
00483           cerr << "       semop was interrupted by signal.  Throwing exception...";
00484         case INTR_THROW:
00485           throw std::runtime_error("EINTR returned by add_testZero semop");
00486         case INTR_EXIT:
00487           perror("ERROR: SemaphoreManager unable to add_testZero (semop)");
00488           cerr << "       semop was interrupted by signal.  Exiting...";
00489           exit(EXIT_FAILURE);
00490       }
00491     }
00492   }
00493   return true;
00494 }
00495 
00496 bool SemaphoreManager::add_testZero_add(semid_t id, int x1, int x2, bool add1block/*=true*/, bool testblock/*=true*/, bool add2block/*=true*/) const {
00497   sembuf sb[3]={
00498     {id,x1,(add1block?0:IPC_NOWAIT)},
00499     {id,0,(testblock?0:IPC_NOWAIT)},
00500     {id,x2,(add2block?0:IPC_NOWAIT)}
00501   };
00502   while(true) {
00503     Thread::requestInterruptOnCancel();
00504     int res = semop(semid,sb,3);
00505     int theErr = errno;
00506     Thread::unrequestInterruptOnCancel();
00507     if(res==0)
00508       break;
00509     if(theErr==EAGAIN)
00510       return false;
00511     if(theErr!=EINTR) { // && theErr!=ERESTART?
00512       if(theErr==EIDRM)
00513         usleep(500000); // probably in the process of shutdown, wait half a second before complaining
00514       cerr << "ERROR: SemaphoreManager unable to add_testZero() (semop): " << strerror(theErr) << '\n';
00515       cerr << "       ";
00516       if(theErr==EIDRM) {
00517         cerr << "Semaphore set has been removed.  " << endl;
00518       } else if(theErr==EINVAL) {
00519         cerr << "Semaphore set was deleted.  " << endl;
00520       } else {
00521         cerr << "Error code was " << theErr << endl;
00522       }
00523       cerr << "Goodbye" << endl;
00524       exit(EXIT_FAILURE);
00525     } else {
00526       switch(intrPolicy[id]) {
00527         case INTR_CANCEL_VERBOSE:
00528           perror("ERROR: SemaphoreManager unable to add_testZero (semop)");
00529           cerr << "       semop was interrupted by signal, cancelling add_testZero()";
00530         case INTR_CANCEL:
00531           return false;
00532         case INTR_RETRY_VERBOSE:
00533           perror("ERROR: SemaphoreManager unable to add_testZero (semop)");
00534           cerr << "       semop was interrupted by signal.  Trying again...";
00535           break;
00536         case INTR_RETRY:
00537           break; //while loop will retry
00538         case INTR_THROW_VERBOSE:
00539           perror("ERROR: SemaphoreManager unable to add_testZero (semop)");
00540           cerr << "       semop was interrupted by signal.  Throwing exception...";
00541         case INTR_THROW:
00542           throw std::runtime_error("EINTR returned by add_testZero semop");
00543         case INTR_EXIT:
00544           perror("ERROR: SemaphoreManager unable to add_testZero (semop)");
00545           cerr << "       semop was interrupted by signal.  Exiting...";
00546           exit(EXIT_FAILURE);
00547       }
00548     }
00549   }
00550   return true;
00551 }
00552 
00553 /*! @file
00554  * @brief Implements SemaphoreManager, which initializes, manages, and releases a set of System V style semaphores
00555  * @author ejt (Creator)
00556  */
00557 
00558 #endif //Aperios check

Tekkotsu v5.1CVS
Generated Fri Mar 16 05:26:51 2012 by Doxygen 1.6.3