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

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