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 #include <unistd.h>
00016
00017 #if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__MACH__)
00018
00019 #else
00020
00021
00022 union semun {
00023 int val;
00024 struct semid_ds *buf;
00025 unsigned short *array;
00026
00027 struct seminfo *__buf;
00028 };
00029
00030 #endif
00031
00032 using namespace std;
00033
00034 SemaphoreManager::SemaphoreManager()
00035 : sems(), nsem(sems_t::MAX_ENTRIES), semid(-1), mysem(sems.end()), refc(sems.end())
00036 {init();}
00037
00038 SemaphoreManager::SemaphoreManager(unsigned int numRequest)
00039 : sems(), nsem(numRequest+2), semid(-1), mysem(sems.end()), refc(sems.end())
00040 {init();}
00041
00042 void SemaphoreManager::init() {
00043 if(nsem>sems_t::MAX_ENTRIES) {
00044 cout << "SemaphoreManager created with request for " << nsem << " semaphores, but sems_t::MAX_ENTRIES is " << sems_t::MAX_ENTRIES << endl;
00045 nsem=sems_t::MAX_ENTRIES;
00046 }
00047 unsigned int req=nsem;
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064 unsigned int lowbound=0;
00065 unsigned int highbound=nsem;
00066
00067 while(lowbound!=highbound) {
00068 semid=semget(IPC_PRIVATE,nsem,IPC_CREAT | IPC_EXCL | 0666);
00069 if(semid<0) {
00070 if(errno!=EINVAL && errno!=ENOSPC) {
00071 perror("ERROR: SemaphoreManager upper limit detection (semget)");
00072 exit(EXIT_FAILURE);
00073 }
00074
00075 highbound=nsem-1;
00076 } else {
00077
00078 if(semctl(semid,-1,IPC_RMID)<0) {
00079 perror("ERROR: SemaphoreManager destruction (semctl)");
00080 exit(EXIT_FAILURE);
00081 }
00082 lowbound=nsem;
00083 }
00084 nsem=(lowbound+highbound+1)/2;
00085 }
00086
00087 semid=semget(IPC_PRIVATE,nsem,IPC_CREAT | IPC_EXCL | 0666);
00088 if(semid<0) {
00089 perror("ERROR: SemaphoreManager construction (semget)");
00090 exit(EXIT_FAILURE);
00091 }
00092 if(nsem!=req)
00093 cerr << "WARNING: System can only allocate " << nsem << " semaphores per set for id=" << semid << " (SEMMSL or SEMMNS max reached). " << req << " were requested." << endl;
00094
00095
00096 unsigned short int semvals[sems_t::MAX_ENTRIES];
00097 for(unsigned int i=0; i<nsem; i++)
00098 semvals[i]=0;
00099 semun params;
00100 params.array=semvals;
00101 if(semctl(semid,-1,SETALL,params)<0) {
00102 perror("ERROR: SemaphoreManager construction (semctl)");
00103 exit(EXIT_FAILURE);
00104 }
00105
00106
00107 if(nsem!=sems_t::MAX_ENTRIES) {
00108
00109 while(sems.new_back()!=sems.end()) {}
00110
00111 for(unsigned int i=0; i<nsem; i++)
00112 sems.pop_front();
00113 }
00114
00115
00116 mysem=sems.new_front();
00117 if(mysem==sems.end()) {
00118 cerr << "ERROR: could not allocate SemaphoreManager internal lock" << endl;
00119 exit(EXIT_FAILURE);
00120 }
00121
00122 setValue(mysem,1);
00123
00124 refc=sems.new_front();
00125 if(refc==sems.end()) {
00126 cerr << "ERROR: could not allocate SemaphoreManager reference counter" << endl;
00127 exit(EXIT_FAILURE);
00128 }
00129
00130 setValue(refc,0);
00131
00132 }
00133
00134 SemaphoreManager::SemaphoreManager(const SemaphoreManager& mm)
00135 : sems(), nsem(mm.nsem), semid(mm.semid), mysem(mm.mysem), refc(mm.refc)
00136 {
00137 ASSERT(mm.semid!=-1,"Copy of SemaphoreManager with invalid semid!");
00138 lower(mysem,1);
00139 sems=mm.sems;
00140 raise(refc,1);
00141 raise(mysem,1);
00142
00143 }
00144
00145 SemaphoreManager& SemaphoreManager::operator=(const SemaphoreManager& mm) {
00146 if(&mm==this)
00147 return *this;
00148
00149
00150 if(semid==mm.semid) {
00151
00152 if(mm.semid!=-1)
00153 mm.lower(mm.mysem,1);
00154 mysem=mm.mysem;
00155 sems=mm.sems;
00156 nsem=mm.nsem;
00157 if(mm.semid!=-1)
00158 mm.raise(mm.mysem,1);
00159 } else {
00160
00161
00162 if(semid!=-1) {
00163 lower(mysem,1);
00164 if(!lower(refc,1,false)) {
00165
00166
00167 sems.erase(refc);
00168 sems.erase(mysem);
00169 for(semid_t it=sems.begin(); it!=sems.end(); it=sems.next(it))
00170 if(it<nsem)
00171 cerr << "Warning: semaphore id " << it << " from set " << semid << " was still active when the set was dereferenced" << endl;
00172 if(semctl(semid,-1,IPC_RMID)<0) {
00173 perror("ERROR: SemaphoreManager deletion from operator= (semctl)");
00174 exit(EXIT_FAILURE);
00175 }
00176 semid=-1;
00177 } else
00178 raise(mysem,1);
00179 }
00180 if(mm.semid!=-1)
00181 mm.lower(mm.mysem,1);
00182 mysem=mm.mysem;
00183 sems=mm.sems;
00184 nsem=mm.nsem;
00185 semid=mm.semid;
00186 if(mm.semid!=-1) {
00187 raise(refc=mm.refc,1);
00188 mm.raise(mm.mysem,1);
00189 }
00190
00191 }
00192 return *this;
00193 }
00194
00195 SemaphoreManager::~SemaphoreManager() {
00196 if(semid==-1)
00197 return;
00198
00199 lower(mysem,1);
00200 if(!lower(refc,1,false)) {
00201
00202
00203
00204
00205
00206
00207
00208
00209 if(semctl(semid,-1,IPC_RMID)<0) {
00210 perror("ERROR: SemaphoreManager deletion from destructor (semctl)");
00211 exit(EXIT_FAILURE);
00212 }
00213 semid=-1;
00214 } else
00215 raise(mysem,1);
00216 }
00217
00218 void SemaphoreManager::aboutToFork() {
00219 raise(refc,1);
00220 }
00221
00222 void SemaphoreManager::faultShutdown() {
00223 if(semid==-1)
00224 return;
00225 if(semctl(semid,-1,IPC_RMID)<0)
00226 perror("WARNING: SemaphoreManager faultShutdown (semctl)");
00227 semid=-1;
00228 }
00229
00230 SemaphoreManager::semid_t SemaphoreManager::getSemaphore() {
00231 lower(mysem,1);
00232 semid_t id=sems.new_front();
00233 raise(mysem,1);
00234 if(id!=sems.end())
00235 setValue(id,0);
00236 intrPolicy[id]=INTR_RETRY;
00237 return id;
00238 }
00239 void SemaphoreManager::releaseSemaphore(semid_t id) {
00240 lower(mysem,1);
00241 sems.erase(id);
00242 raise(mysem,1);
00243 }
00244
00245 bool SemaphoreManager::lower(semid_t id, unsigned int x, bool block) const {
00246 sembuf sb={id,(short)-x,short(block?0:IPC_NOWAIT)};
00247 while(true) {
00248 Thread::requestInterruptOnCancel();
00249 int res = semop(semid,&sb,1);
00250 int theErr = errno;
00251 Thread::unrequestInterruptOnCancel();
00252 if(res==0)
00253 break;
00254 if(theErr==EAGAIN)
00255 return false;
00256 if(theErr==EINTR) {
00257 switch(intrPolicy[id]) {
00258 case INTR_CANCEL_VERBOSE:
00259 perror("ERROR: SemaphoreManager unable to lower semaphore (semop)");
00260 cerr << " semop was interrupted by signal, cancelling lower()";
00261 case INTR_CANCEL:
00262 return false;
00263 case INTR_RETRY_VERBOSE:
00264 perror("ERROR: SemaphoreManager unable to lower semaphore (semop)");
00265 cerr << " semop was interrupted by signal. Trying again...";
00266 break;
00267 case INTR_RETRY:
00268 break;
00269 case INTR_THROW_VERBOSE:
00270 perror("ERROR: SemaphoreManager unable to lower semaphore (semop)");
00271 cerr << " semop was interrupted by signal. Throwing exception...";
00272 case INTR_THROW:
00273 throw std::runtime_error("EINTR returned by lower semop");
00274 case INTR_EXIT:
00275 perror("ERROR: SemaphoreManager unable to lower semaphore (semop)");
00276 cerr << " semop was interrupted by signal. Exiting...";
00277 exit(EXIT_FAILURE);
00278 }
00279 } else {
00280 if(theErr==EIDRM)
00281 usleep(500000);
00282 cerr << "ERROR: SemaphoreManager unable to lower semaphore (semop): " << strerror(theErr) << endl;
00283 cerr << " ";
00284 if(theErr==EIDRM) {
00285 cerr << "Semaphore set has been removed. " << endl;
00286 }
00287 if(theErr==EINVAL) {
00288 cerr << "Semaphore set was deleted. " << endl;
00289 }
00290
00291 cerr << "Goodbye" << endl;
00292 exit(EXIT_FAILURE);
00293 }
00294 }
00295 return true;
00296 }
00297 void SemaphoreManager::raise(semid_t id, unsigned int x) const {
00298 sembuf sb={id,(short)x,0};
00299 if(semop(semid,&sb,1)<0) {
00300 perror("ERROR: SemaphoreManager unable to raise semaphore (semop)");
00301 }
00302 }
00303 int SemaphoreManager::getValue(semid_t id) const {
00304 int ans=semctl(semid,id,GETVAL);
00305 if(ans<0)
00306 perror("ERROR: SemaphoreManager getValue (semctl)");
00307 return ans;
00308 }
00309 void SemaphoreManager::setValue(semid_t id, int x) const {
00310 semun params;
00311 params.val=x;
00312 if(semctl(semid,id,SETVAL,params)<0) {
00313 perror("ERROR: SemaphoreManager::setValue (semctl)");
00314 exit(EXIT_FAILURE);
00315 }
00316 }
00317 int SemaphoreManager::getNumZeroBlockers(semid_t id) const {
00318 int ans=semctl(semid,id,GETZCNT);
00319 if(ans<0)
00320 perror("ERROR: SemaphoreManager getNumZeroBlockers (semctl)");
00321 return ans;
00322 }
00323 bool SemaphoreManager::testZero(semid_t id, bool block) const {
00324 sembuf sb={id,0,short(block?0:IPC_NOWAIT)};
00325 while(true) {
00326 Thread::requestInterruptOnCancel();
00327 int res = semop(semid,&sb,1);
00328 int theErr = errno;
00329 Thread::unrequestInterruptOnCancel();
00330 if(res==0)
00331 break;
00332 if(theErr==EAGAIN)
00333 return false;
00334 if(theErr!=EINTR) {
00335 if(theErr==EIDRM)
00336 usleep(500000);
00337 cerr << "ERROR: SemaphoreManager unable to testZero() (semop): " << strerror(theErr) << '\n';
00338 cerr << " ";
00339 if(theErr==EIDRM) {
00340 cerr << "Semaphore set has been removed. " << endl;
00341 } else if(theErr==EINVAL) {
00342 cerr << "Semaphore set was deleted. " << endl;
00343 } else {
00344 cerr << "Error code was " << theErr << endl;
00345 }
00346 cerr << "Goodbye" << endl;
00347 exit(EXIT_FAILURE);
00348 } else {
00349 switch(intrPolicy[id]) {
00350 case INTR_CANCEL_VERBOSE:
00351 perror("ERROR: SemaphoreManager unable to testZero (semop)");
00352 cerr << " semop was interrupted by signal, cancelling testZero()";
00353 case INTR_CANCEL:
00354 return false;
00355 case INTR_RETRY_VERBOSE:
00356 perror("ERROR: SemaphoreManager unable to testZero (semop)");
00357 cerr << " semop was interrupted by signal. Trying again...";
00358 break;
00359 case INTR_RETRY:
00360 break;
00361 case INTR_THROW_VERBOSE:
00362 perror("ERROR: SemaphoreManager unable to testZero (semop)");
00363 cerr << " semop was interrupted by signal. Throwing exception...";
00364 case INTR_THROW:
00365 throw std::runtime_error("EINTR returned by testZero semop");
00366 case INTR_EXIT:
00367 perror("ERROR: SemaphoreManager unable to testZero (semop)");
00368 cerr << " semop was interrupted by signal. Exiting...";
00369 exit(EXIT_FAILURE);
00370 }
00371 }
00372 }
00373 return true;
00374 }
00375 bool SemaphoreManager::testZero_add(semid_t id, int x, bool testblock, bool addblock) const {
00376 sembuf sb[2]={
00377 {id,0,short(testblock?0:IPC_NOWAIT)},
00378 {id,(short)x,short(addblock?0:IPC_NOWAIT)}
00379 };
00380 while(true) {
00381 Thread::requestInterruptOnCancel();
00382 int res = semop(semid,sb,2);
00383 int theErr = errno;
00384 try {
00385 Thread::unrequestInterruptOnCancel();
00386 } catch(...) {
00387 if(res==0) {
00388 #ifdef DEBUG
00389 ASSERT(lower(id,x,false),"Could not undo semop after thread cancel in testZero_add (would block?)");
00390 #else
00391 lower(id,x,true);
00392 #endif
00393 }
00394 throw;
00395 }
00396 if(res==0)
00397 break;
00398 if(theErr==EAGAIN)
00399 return false;
00400 if(theErr!=EINTR) {
00401 if(theErr==EIDRM)
00402 usleep(500000);
00403 cerr << "ERROR: SemaphoreManager unable to testZero_add() (semop): " << strerror(theErr) << '\n';
00404 cerr << " ";
00405 if(theErr==EIDRM) {
00406 cerr << "Semaphore set has been removed. " << endl;
00407 } else if(theErr==EINVAL) {
00408 cerr << "Semaphore set was deleted. " << endl;
00409 } else {
00410 cerr << "Error code was " << theErr << endl;
00411 }
00412 cerr << "Goodbye" << endl;
00413 exit(EXIT_FAILURE);
00414 } else {
00415 switch(intrPolicy[id]) {
00416 case INTR_CANCEL_VERBOSE:
00417 perror("ERROR: SemaphoreManager unable to testZero_add (semop)");
00418 cerr << " semop was interrupted by signal, cancelling testZero_add()";
00419 case INTR_CANCEL:
00420 return false;
00421 case INTR_RETRY_VERBOSE:
00422 perror("ERROR: SemaphoreManager unable to testZero_add (semop)");
00423 cerr << " semop was interrupted by signal. Trying again...";
00424 break;
00425 case INTR_RETRY:
00426 break;
00427 case INTR_THROW_VERBOSE:
00428 perror("ERROR: SemaphoreManager unable to testZero_add (semop)");
00429 cerr << " semop was interrupted by signal. Throwing exception...";
00430 case INTR_THROW:
00431 throw std::runtime_error("EINTR returned by testZero_add semop");
00432 case INTR_EXIT:
00433 perror("ERROR: SemaphoreManager unable to testZero_add (semop)");
00434 cerr << " semop was interrupted by signal. Exiting...";
00435 exit(EXIT_FAILURE);
00436 }
00437 }
00438 }
00439 return true;
00440 }
00441 bool SemaphoreManager::add_testZero(semid_t id, int x, bool addblock, bool testblock) const {
00442 sembuf sb[2]={
00443 {id,(short)x,short(addblock?0:IPC_NOWAIT)},
00444 {id,0,short(testblock?0:IPC_NOWAIT)}
00445 };
00446 while(true) {
00447 Thread::requestInterruptOnCancel();
00448 int res = semop(semid,sb,2);
00449 int theErr = errno;
00450 Thread::unrequestInterruptOnCancel();
00451 if(res==0)
00452 break;
00453 if(theErr==EAGAIN)
00454 return false;
00455 if(theErr!=EINTR) {
00456 if(theErr==EIDRM)
00457 usleep(500000);
00458 cerr << "ERROR: SemaphoreManager unable to add_testZero() (semop): " << strerror(theErr) << '\n';
00459 cerr << " ";
00460 if(theErr==EIDRM) {
00461 cerr << "Semaphore set has been removed. " << endl;
00462 } else if(theErr==EINVAL) {
00463 cerr << "Semaphore set was deleted. " << endl;
00464 } else {
00465 cerr << "Error code was " << theErr << endl;
00466 }
00467 cerr << "Goodbye" << endl;
00468 exit(EXIT_FAILURE);
00469 } else {
00470 switch(intrPolicy[id]) {
00471 case INTR_CANCEL_VERBOSE:
00472 perror("ERROR: SemaphoreManager unable to add_testZero (semop)");
00473 cerr << " semop was interrupted by signal, cancelling add_testZero()";
00474 case INTR_CANCEL:
00475 return false;
00476 case INTR_RETRY_VERBOSE:
00477 perror("ERROR: SemaphoreManager unable to add_testZero (semop)");
00478 cerr << " semop was interrupted by signal. Trying again...";
00479 break;
00480 case INTR_RETRY:
00481 break;
00482 case INTR_THROW_VERBOSE:
00483 perror("ERROR: SemaphoreManager unable to add_testZero (semop)");
00484 cerr << " semop was interrupted by signal. Throwing exception...";
00485 case INTR_THROW:
00486 throw std::runtime_error("EINTR returned by add_testZero semop");
00487 case INTR_EXIT:
00488 perror("ERROR: SemaphoreManager unable to add_testZero (semop)");
00489 cerr << " semop was interrupted by signal. Exiting...";
00490 exit(EXIT_FAILURE);
00491 }
00492 }
00493 }
00494 return true;
00495 }
00496
00497 bool SemaphoreManager::add_testZero_add(semid_t id, int x1, int x2, bool add1block, bool testblock, bool add2block) const {
00498 sembuf sb[3]={
00499 {id,(short)x1,short(add1block?0:IPC_NOWAIT)},
00500 {id,0,short(testblock?0:IPC_NOWAIT)},
00501 {id,(short)x2,short(add2block?0:IPC_NOWAIT)}
00502 };
00503 while(true) {
00504 Thread::requestInterruptOnCancel();
00505 int res = semop(semid,sb,3);
00506 int theErr = errno;
00507 Thread::unrequestInterruptOnCancel();
00508 if(res==0)
00509 break;
00510 if(theErr==EAGAIN)
00511 return false;
00512 if(theErr!=EINTR) {
00513 if(theErr==EIDRM)
00514 usleep(500000);
00515 cerr << "ERROR: SemaphoreManager unable to add_testZero() (semop): " << strerror(theErr) << '\n';
00516 cerr << " ";
00517 if(theErr==EIDRM) {
00518 cerr << "Semaphore set has been removed. " << endl;
00519 } else if(theErr==EINVAL) {
00520 cerr << "Semaphore set was deleted. " << endl;
00521 } else {
00522 cerr << "Error code was " << theErr << endl;
00523 }
00524 cerr << "Goodbye" << endl;
00525 exit(EXIT_FAILURE);
00526 } else {
00527 switch(intrPolicy[id]) {
00528 case INTR_CANCEL_VERBOSE:
00529 perror("ERROR: SemaphoreManager unable to add_testZero (semop)");
00530 cerr << " semop was interrupted by signal, cancelling add_testZero()";
00531 case INTR_CANCEL:
00532 return false;
00533 case INTR_RETRY_VERBOSE:
00534 perror("ERROR: SemaphoreManager unable to add_testZero (semop)");
00535 cerr << " semop was interrupted by signal. Trying again...";
00536 break;
00537 case INTR_RETRY:
00538 break;
00539 case INTR_THROW_VERBOSE:
00540 perror("ERROR: SemaphoreManager unable to add_testZero (semop)");
00541 cerr << " semop was interrupted by signal. Throwing exception...";
00542 case INTR_THROW:
00543 throw std::runtime_error("EINTR returned by add_testZero semop");
00544 case INTR_EXIT:
00545 perror("ERROR: SemaphoreManager unable to add_testZero (semop)");
00546 cerr << " semop was interrupted by signal. Exiting...";
00547 exit(EXIT_FAILURE);
00548 }
00549 }
00550 }
00551 return true;
00552 }
00553
00554
00555
00556
00557
00558
00559 #endif //Aperios check