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
00018 #else
00019
00020
00021 union semun {
00022 int val;
00023 struct semid_ds *buf;
00024 unsigned short *array;
00025
00026 struct seminfo *__buf;
00027 };
00028
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
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063 unsigned int lowbound=0;
00064 unsigned int highbound=nsem;
00065
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
00074 highbound=nsem-1;
00075 } else {
00076
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
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
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
00106 if(nsem!=sems_t::MAX_ENTRIES) {
00107
00108 while(sems.new_back()!=sems.end()) {}
00109
00110 for(unsigned int i=0; i<nsem; i++)
00111 sems.pop_front();
00112 }
00113
00114
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
00121 setValue(mysem,1);
00122
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
00129 setValue(refc,0);
00130
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);
00138 sems=mm.sems;
00139 raise(refc,1);
00140 raise(mysem,1);
00141
00142 }
00143
00144 SemaphoreManager& SemaphoreManager::operator=(const SemaphoreManager& mm) {
00145 if(&mm==this)
00146 return *this;
00147
00148
00149 if(semid==mm.semid) {
00150
00151 if(mm.semid!=-1)
00152 mm.lower(mm.mysem,1);
00153 mysem=mm.mysem;
00154 sems=mm.sems;
00155 nsem=mm.nsem;
00156 if(mm.semid!=-1)
00157 mm.raise(mm.mysem,1);
00158 } else {
00159
00160
00161 if(semid!=-1) {
00162 lower(mysem,1);
00163 if(!lower(refc,1,false)) {
00164
00165
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);
00178 }
00179 if(mm.semid!=-1)
00180 mm.lower(mm.mysem,1);
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);
00187 mm.raise(mm.mysem,1);
00188 }
00189
00190 }
00191 return *this;
00192 }
00193
00194 SemaphoreManager::~SemaphoreManager() {
00195 if(semid==-1)
00196 return;
00197
00198 lower(mysem,1);
00199 if(!lower(refc,1,false)) {
00200
00201
00202
00203
00204
00205
00206
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;
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) 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;
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);
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
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) 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) {
00334 if(theErr==EIDRM)
00335 usleep(500000);
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;
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, bool addblock) 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(...) {
00386 if(res==0) {
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);
00391 #endif
00392 }
00393 throw;
00394 }
00395 if(res==0)
00396 break;
00397 if(theErr==EAGAIN)
00398 return false;
00399 if(theErr!=EINTR) {
00400 if(theErr==EIDRM)
00401 usleep(500000);
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;
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, bool testblock) 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) {
00455 if(theErr==EIDRM)
00456 usleep(500000);
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;
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, bool testblock, bool add2block) 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) {
00512 if(theErr==EIDRM)
00513 usleep(500000);
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;
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
00554
00555
00556
00557
00558 #endif //Aperios check