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