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

Tekkotsu v4.0
Generated Thu Nov 22 00:54:55 2007 by Doxygen 1.5.4