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

Tekkotsu v2.4.1
Generated Tue Aug 16 16:32:48 2005 by Doxygen 1.4.4