RCRegion.ccGo to the documentation of this file.00001 #ifndef PLATFORM_APERIOS
00002 #include "RCRegion.h"
00003 #include "LockScope.h"
00004 #include "Shared/debuget.h"
00005 #include <unistd.h>
00006 #include <sstream>
00007 #include <sys/stat.h>
00008 #include <errno.h>
00009
00010 #if TEKKOTSU_SHM_STYLE!=SYSV_SHM && TEKKOTSU_SHM_STYLE!=POSIX_SHM
00011 # error Unknown TEKKOTSU_SHM_STYLE setting
00012 #endif
00013
00014 #if TEKKOTSU_SHM_STYLE==SYSV_SHM
00015 # include <sys/ipc.h>
00016 # include <sys/shm.h>
00017 #elif TEKKOTSU_SHM_STYLE==POSIX_SHM
00018 # include <sys/mman.h>
00019 # include <sys/fcntl.h>
00020 # ifdef USE_UNBACKED_SHM
00021 plist::Primitive<bool> RCRegion::useUniqueMemoryRegions(false);
00022 # else
00023 plist::Primitive<std::string> RCRegion::shmRoot("/tmp/tekkotsu_sim/");
00024 plist::Primitive<bool> RCRegion::useUniqueMemoryRegions(true);
00025 # endif
00026 pid_t RCRegion::rootPID(::getpid());
00027 #endif
00028
00029 using namespace std;
00030
00031 typedef LockScope<ProcessID::NumProcesses> AutoLock;
00032
00033 key_t RCRegion::nextKey=1024;
00034 RCRegion::attachedRegions_t RCRegion::attachedRegions;
00035 bool RCRegion::isFaultShutdown=false;
00036
00037 #if TEKKOTSU_SHM_STYLE==SYSV_SHM
00038
00039
00040
00041 RCRegion::ConflictResolutionStrategy RCRegion::conflictStrategy=RCRegion::RENAME;
00042
00043 #elif TEKKOTSU_SHM_STYLE==POSIX_SHM
00044
00045
00046
00047
00048 RCRegion::ConflictResolutionStrategy RCRegion::conflictStrategy=RCRegion::REPLACE;
00049
00050 #endif
00051
00052 RCRegion * RCRegion::attach(const Identifier& rid) {
00053 attachedRegions_t::iterator it=attachedRegions.find(rid.key);
00054 if(it==attachedRegions.end())
00055 return new RCRegion(rid);
00056 else {
00057 ASSERTRETVAL((*it).second!=NULL,"ERROR: attached region is NULL!",NULL);
00058 (*it).second->AddReference();
00059 return (*it).second;
00060 }
00061 }
00062
00063 void RCRegion::AddReference() {
00064 AutoLock autolock(*lock,ProcessID::getID());
00065
00066 references[ProcessID::getID()]++;
00067 references[ProcessID::NumProcesses]++;
00068
00069
00070
00071
00072 }
00073
00074 void RCRegion::RemoveReference() {
00075
00076 if(references[ProcessID::getID()] == 0) {
00077 cerr << "Warning: RCRegion reference count underflow on " << id.key << " by " << ProcessID::getID() << "! ";
00078 for(unsigned int i=0; i<ProcessID::NumProcesses+1; i++)
00079 cerr << ' ' << references[i];
00080 cerr << endl;
00081 return;
00082 }
00083
00084 lock->lock(ProcessID::getID());
00085 bool wasLastProcRef=(--references[ProcessID::getID()] == 0);
00086 bool wasLastAnyRef=(--references[ProcessID::NumProcesses] == 0);
00087 ASSERT(wasLastProcRef || !wasLastAnyRef,"global reference decremented beyond process reference");
00088
00089 lock->unlock();
00090 if(isFaultShutdown) {
00091 cerr << "Process " << ProcessID::getID() << " dereferenced " << id.key << ". Counts are now:";
00092 for(unsigned int i=0; i<ProcessID::NumProcesses+1; i++)
00093 cerr << ' ' << references[i];
00094 cerr << endl;
00095 }
00096 if(wasLastProcRef) {
00097
00098 #if TEKKOTSU_SHM_STYLE==SYSV_SHM
00099 if(shmdt(base)<0)
00100 perror("Warning: Region detach");
00101 base=NULL;
00102 references=NULL;
00103 if(wasLastAnyRef) {
00104
00105 if(shmctl(id.shmid,IPC_RMID,NULL)<0)
00106 perror("Warning: Region delete");
00107 }
00108 #elif TEKKOTSU_SHM_STYLE==POSIX_SHM
00109 if(munmap(base,calcRealSize(id.size))<0) {
00110 perror("Warning: Shared memory unmap (munmap)");
00111 }
00112 base=NULL;
00113 references=NULL;
00114 if(wasLastAnyRef) {
00115
00116 if(!unlinkRegion()) {
00117 int err=errno;
00118 if(isFaultShutdown && (err==EINVAL || err==ENOENT))
00119
00120
00121 cerr << "Region " << id.key << " appears to have been successfully unlinked" << endl;
00122 else {
00123 cerr << "Warning: Shared memory unlink (shm_unlink) of region " << id.key << " returned " << strerror(err);
00124 if(err==EINVAL || err==ENOENT)
00125 cerr << "\n May have already been unlinked by a dying process.";
00126 cerr << endl;
00127 }
00128 } else if(isFaultShutdown)
00129
00130 cerr << "Region " << id.key << " appears to have been successfully unlinked (nonstandard)" << endl;
00131 }
00132 #else
00133 # error "Unknown TEKKOTSU_SHM_STYLE setting"
00134 #endif
00135 delete this;
00136 }
00137
00138 }
00139
00140 void RCRegion::AddSharedReference() {
00141 AutoLock autolock(*lock,ProcessID::getID());
00142
00143 references[ProcessID::NumProcesses]++;
00144
00145
00146
00147
00148 }
00149
00150 void RCRegion::RemoveSharedReference() {
00151 AutoLock autolock(*lock,ProcessID::getID());
00152
00153 if(references[ProcessID::NumProcesses]==0) {
00154 cerr << "Warning: RCRegion shared reference count underflow on " << id.key << " by " << ProcessID::getID() << "! ";
00155 for(unsigned int i=0; i<ProcessID::NumProcesses+1; i++)
00156 cerr << ' ' << references[i];
00157 cerr << endl;
00158 return;
00159 }
00160 references[ProcessID::NumProcesses]--;
00161
00162
00163
00164
00165 }
00166
00167
00168 void RCRegion::aboutToFork(ProcessID::ProcessID_t newID) {
00169
00170 attachedRegions_t::const_iterator it=attachedRegions.begin();
00171 for(; it!=attachedRegions.end(); ++it) {
00172
00173 (*it).second->references[newID]=(*it).second->references[ProcessID::getID()];
00174 (*it).second->references[ProcessID::NumProcesses]+=(*it).second->references[newID];
00175
00176
00177
00178
00179 }
00180 }
00181
00182 void RCRegion::faultShutdown() {
00183 if(isFaultShutdown) {
00184 cerr << "WARNING: RCRegion::faultShutdown() called again... ignoring" << endl;
00185 return;
00186 }
00187 isFaultShutdown=true;
00188 if(attachedRegions.size()==0) {
00189 cerr << "WARNING: RCRegion::faultShutdown() called without any attached regions (may be a good thing?)" << endl;
00190 return;
00191 }
00192 #if TEKKOTSU_SHM_STYLE==POSIX_SHM
00193
00194
00195 attachedRegions_t::const_iterator it=attachedRegions.begin();
00196 for(; it!=attachedRegions.end(); ++it) {
00197 cerr << "RCRegion::faultShutdown(): Process " << ProcessID::getID() << " unlinking " << (*it).second->id.key << endl;
00198 #ifdef USE_UNBACKED_SHM
00199 shm_unlink(getQualifiedName((*it).second->id.key).c_str());
00200 #else
00201 unlink(getQualifiedName((*it).second->id.key).c_str());
00202 #endif
00203 }
00204 #endif
00205 for(unsigned int i=0; i<100; i++) {
00206 unsigned int attempts=ProcessID::NumProcesses;
00207 unsigned int lastSize=attachedRegions.size();
00208 while(attachedRegions.size()==lastSize && attempts-->0)
00209 (*attachedRegions.begin()).second->RemoveReference();
00210 if(attempts==-1U) {
00211 cout << "Warning: could not dereference " << attachedRegions.begin()->second->id.key << endl;
00212 attachedRegions.erase(attachedRegions.begin());
00213 }
00214 if(attachedRegions.size()==0)
00215 break;
00216 }
00217 }
00218
00219 RCRegion::~RCRegion() {
00220 attachedRegions.erase(id.key);
00221 ASSERT(base==NULL,"destructed with attachment!");
00222 ASSERT(references==NULL,"destructed with local references!");
00223
00224 }
00225
00226 unsigned int RCRegion::calcRealSize(unsigned int size) {
00227 size=((size+align-1)/align)*align;
00228 size+=extra;
00229 unsigned int pagesize=::getpagesize();
00230 unsigned int pages=(size+pagesize-1)/pagesize;
00231 return pages*pagesize;
00232 }
00233
00234 #if TEKKOTSU_SHM_STYLE==SYSV_SHM
00235
00236 void RCRegion::init(size_t sz, key_t sug_key, bool create) {
00237 id.size=sz;
00238 sz=calcRealSize(sz);
00239 if(create) {
00240 int flags = 0666 | IPC_CREAT | IPC_EXCL;
00241 if(sug_key==IPC_PRIVATE) {
00242 if((id.shmid=shmget(sug_key, sz, flags)) < 0) {
00243 int err=errno;
00244 if(err != EEXIST) {
00245 cerr << "ERROR: Getting new private region " << key << " of size " << sz ": " << strerror(err) << " (shmget)" << endl;
00246 exit(EXIT_FAILURE);
00247 }
00248 }
00249 id.key=sug_key;
00250 } else {
00251 nextKey=sug_key;
00252 switch(conflictStrategy) {
00253 case RENAME:
00254 while((id.shmid=shmget(id.key=nextKey++, sz, flags)) < 0) {
00255 int err=errno;
00256 if(err != EEXIST) {
00257 cerr << "ERROR: Getting new region " << key << " of size " << sz ": " << strerror(err) << " (shmget)" << endl;
00258 exit(EXIT_FAILURE);
00259 }
00260 }
00261 break;
00262 case REPLACE:
00263 if((id.shmid=shmget(id.key=nextKey, sz, flags)) >= 0)
00264 break;
00265 int err=errno;
00266 if(err != EEXIST) {
00267 cerr << "ERROR: Getting new region " << key << " of size " << sz ": " << strerror(err) << " (shmget)" << endl;
00268 exit(EXIT_FAILURE);
00269 }
00270 #ifdef DEBUG
00271 cerr << "Warning: conflicted key " << key << ", attempting to replace\n"
00272 << " (may have been leftover from a previous crash)" << endl;
00273 #endif
00274 if(shmctl(id.shmid,IPC_RMID,NULL)<0)
00275 perror("Warning: Region delete from conflict - is another simulator running?");
00276
00277 case EXIT:
00278 if((id.shmid=shmget(id.key=nextKey, sz, flags)) < 0) {
00279 int err=errno;
00280 cerr << "ERROR: Getting new region " << key << " of size " << sz ": " << strerror(err) << " (shmget)" << endl;
00281 exit(EXIT_FAILURE);
00282 }
00283 }
00284 }
00285 } else {
00286 int flags = 0666;
00287 if((id.shmid=shmget(sug_key, sz, flags)) < 0) {
00288 int err=errno;
00289 cerr << "ERROR: Getting existing region " << key << " of size " << sz ": " << strerror(err) << " (shmget)" << endl;
00290 exit(EXIT_FAILURE);
00291 }
00292 id.key=sug_key;
00293 }
00294
00295 base=static_cast<char*>(shmat(id.shmid, NULL, SHM_RND));
00296 int err=errno;
00297
00298 if (base == reinterpret_cast<char*>(-1)) {
00299 cerr << "ERROR: Attaching region " << key << " of size " << sz << ": " << strerror(err) << " (shmat)" << endl;
00300 if(shmctl(id.shmid,IPC_RMID,NULL)<0)
00301 perror("Region delete");
00302 exit(EXIT_FAILURE);
00303 }
00304 lock=reinterpret_cast<MutexLock<ProcessID::NumProcesses>*>(base+sz-sizeof(MutexLock<ProcessID::NumProcesses>));
00305 references=reinterpret_cast<unsigned int*>(base+sz-extra);
00306 if(create) {
00307 new (lock) MutexLock<ProcessID::NumProcesses>;
00308 AutoLock autolock(*lock,ProcessID::getID());
00309 for(unsigned int i=0; i<ProcessID::NumProcesses+1; i++)
00310 references[i]=0;
00311 }
00312 AddReference();
00313 attachedRegions[id.key]=this;
00314 }
00315
00316 #elif TEKKOTSU_SHM_STYLE==POSIX_SHM
00317
00318 std::string RCRegion::getQualifiedName(const std::string& key) {
00319 #ifdef USE_UNBACKED_SHM
00320 string idval="/";
00321 #else
00322 string idval=shmRoot;
00323 #endif
00324 if(useUniqueMemoryRegions) {
00325 char pidstr[10];
00326 snprintf(pidstr,10,"%d-",rootPID);
00327 idval+=pidstr;
00328 }
00329 idval+=key;
00330 return idval;
00331 }
00332 int RCRegion::openRegion(int mode) const {
00333 #ifdef USE_UNBACKED_SHM
00334 return shm_open(getQualifiedName().c_str(),mode,0666);
00335 #else
00336 return open(getQualifiedName().c_str(),mode,0666);
00337 #endif
00338 }
00339 bool RCRegion::unlinkRegion() const {
00340 #ifdef USE_UNBACKED_SHM
00341 return shm_unlink(getQualifiedName().c_str())==0;
00342 #else
00343 return unlink(getQualifiedName().c_str())==0;
00344 #endif
00345 }
00346 void RCRegion::init(size_t sz, const std::string& name, bool create) {
00347 id.size=sz;
00348 sz=calcRealSize(sz);
00349 #ifndef USE_UNBACKED_SHM
00350 struct stat statbuf;
00351
00352 if(stat(shmRoot.substr(0,shmRoot.rfind('/')).c_str(),&statbuf)) {
00353 for(string::size_type c=shmRoot.find('/',1); c!=string::npos; c=shmRoot.find('/',c+1)) {
00354
00355 if(stat(shmRoot.substr(0,c).c_str(),&statbuf)) {
00356 mkdir(shmRoot.substr(0,c).c_str(),0777);
00357 } else if(!(statbuf.st_mode&S_IFDIR)) {
00358 cerr << "*** ERROR " << shmRoot.substr(0,c) << " exists and is not a directory" << endl;
00359 cerr << " Cannot create file-backed shared memory regions in " << shmRoot << endl;
00360 exit(EXIT_FAILURE);
00361 }
00362 }
00363 cout << "Created '" << shmRoot.substr(0,shmRoot.rfind('/')) << "' for file-backed shared memory storage" << endl;
00364 } else if(!(statbuf.st_mode&S_IFDIR)) {
00365 cerr << "*** ERROR " << shmRoot.substr(0,shmRoot.rfind('/')) << " exists and is not a directory" << endl;
00366 cerr << " Cannot create file-backed shared memory regions with prefix " << shmRoot << endl;
00367 exit(EXIT_FAILURE);
00368 }
00369 #endif
00370 int fd;
00371 if(name.size()>=MAX_NAME_LEN)
00372 cerr << "*** WARNING RCRegion named " << name << " will be clipped to " << name.substr(0,MAX_NAME_LEN-1) << endl;
00373 strncpy(id.key,name.c_str(),MAX_NAME_LEN-1);
00374 id.key[MAX_NAME_LEN-1]='\0';
00375 if(create) {
00376 static unsigned int renameSN=0;
00377 switch(conflictStrategy) {
00378 case RENAME: {
00379 char origName[MAX_NAME_LEN];
00380 strncpy(origName,id.key,MAX_NAME_LEN);
00381 if((fd=openRegion(O_RDWR|O_CREAT|O_EXCL))>=0)
00382 break;
00383 do {
00384 int err=errno;
00385 if(err!=EEXIST) {
00386 cerr << "ERROR: Opening new region " << id.key << ": " << strerror(err) << " (shm_open)" << endl;
00387 exit(EXIT_FAILURE);
00388 }
00389 unsigned int p=snprintf(id.key,MAX_NAME_LEN,"%s-%d",origName,++renameSN);
00390 if(p>=MAX_NAME_LEN) {
00391 cerr << "ERROR: conflicted key " << origName << ", attempting to rename, but generated name is too long" << endl;
00392 exit(EXIT_FAILURE);
00393 }
00394
00395 #ifdef DEBUG
00396 cerr << "Warning: conflicted key " << origName << ", attempting to rename as " << id.key << "\n"
00397 << " (may have been leftover from a previous crash)" << endl;
00398 #endif
00399 } while((fd=openRegion(O_RDWR|O_CREAT|O_EXCL))<0);
00400 break;
00401 }
00402 case REPLACE: {
00403 if((fd=openRegion(O_RDWR|O_CREAT|O_EXCL))>=0)
00404 break;
00405 int err=errno;
00406 if(err!=EEXIST) {
00407 cerr << "ERROR: Opening new region " << id.key << ": " << strerror(err) << " (shm_open)" << endl;
00408 exit(EXIT_FAILURE);
00409 }
00410 #ifdef DEBUG
00411 cerr << "Warning: conflicted key " << id.key << ", attempting to replace\n"
00412 << " (may have been leftover from a previous crash)" << endl;
00413 #endif
00414 if(!unlinkRegion())
00415 perror("Warning: Shared memory unlink (shm_unlink)");
00416 }
00417
00418 case EXIT: {
00419 if((fd=openRegion(O_RDWR|O_CREAT|O_EXCL))<0) {
00420 int err=errno;
00421 cerr << "ERROR: Opening new region " << id.key << ": " << strerror(err) << " (shm_open)" << endl;
00422 if(err==EEXIST)
00423 cerr << "This error suggests a leaked memory region, perhaps from a bad crash on a previous run.\n"
00424 << "You may either be able to use shm_unlink to remove the region, or reboot.\n"
00425 << "Also make sure that no other copies of the simulator are already running." << endl;
00426 exit(EXIT_FAILURE);
00427 }
00428 }
00429 }
00430 if (ftruncate(fd,sz)<0) {
00431 int err=errno;
00432 cerr << "ERROR: Sizing region " << id.key << " to " << sz << ": " << strerror(err) << " (ftruncate)" << endl;
00433 if(close(fd)<0)
00434 perror("Warning: Closing temporary file descriptor from shm_open");
00435 if(!unlinkRegion())
00436 perror("Warning: Shared memory unlink (shm_unlink)");
00437 exit(EXIT_FAILURE);
00438 }
00439 } else {
00440 if((fd=openRegion(O_RDWR))<0) {
00441 int err=errno;
00442 cerr << "ERROR: Opening existing region " << id.key << ": " << strerror(err) << " (shm_open)" << endl;
00443 exit(EXIT_FAILURE);
00444 }
00445 }
00446 base=static_cast<char*>(mmap(NULL,sz,PROT_READ|PROT_WRITE,MAP_SHARED,fd,(off_t)0));
00447 int err=errno;
00448 if (base == reinterpret_cast<char*>(-1)) {
00449 cerr << "ERROR: Attaching region " << id.key << " of size " << sz << ": " << strerror(err) << " (mmap)" << endl;
00450 if(close(fd)<0)
00451 perror("Warning: Closing temporary file descriptor from shm_open");
00452 if(!unlinkRegion())
00453 perror("Warning: Shared memory unlink (shm_unlink)");
00454 exit(EXIT_FAILURE);
00455 }
00456 if(close(fd)<0) {
00457 perror("Warning: Closing temporary file descriptor from shm_open");
00458 }
00459 lock=reinterpret_cast<MutexLock<ProcessID::NumProcesses>*>(base+sz-sizeof(MutexLock<ProcessID::NumProcesses>));
00460 references=reinterpret_cast<unsigned int*>(base+sz-extra);
00461 if(create) {
00462 new (lock) MutexLock<ProcessID::NumProcesses>;
00463 AutoLock autolock(*lock,ProcessID::getID());
00464 for(unsigned int i=0; i<ProcessID::NumProcesses+1; i++)
00465 references[i]=0;
00466 }
00467 AddReference();
00468 attachedRegions[id.key]=this;
00469 }
00470
00471 #else
00472 # error "Unknown TEKKOTSU_SHM_STYLE setting"
00473 #endif
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506 #endif
|