diff --git a/library/DFError.h b/library/DFError.h index f30c5e0c5..375a5c581 100644 --- a/library/DFError.h +++ b/library/DFError.h @@ -221,6 +221,61 @@ namespace DFHack return "The server process has disappeared"; } }; + class DFHACK_EXPORT SHMLockingError : public std::exception + { + public: + SHMLockingError(const char* _type) : type(_type) {} + virtual ~SHMLockingError() throw(){}; + + std::string type; + + virtual const char* what() const throw() + { + std::stringstream s; + s << "SHM locking error: " << type; + return s.str().c_str(); + } + }; + class DFHACK_EXPORT SHMAccessDenied : public std::exception + { + public: + SHMAccessDenied() {} + virtual ~SHMAccessDenied() throw(){}; + + std::string type; + + virtual const char* what() const throw() + { + return "SHM ACCESS DENIED"; + } + }; + class DFHACK_EXPORT SHMVersionMismatch : public std::exception + { + public: + SHMVersionMismatch() {} + virtual ~SHMVersionMismatch() throw(){}; + + std::string type; + + virtual const char* what() const throw() + { + return "SHM VERSION MISMATCH"; + } + }; + class DFHACK_EXPORT SHMAttachFailure : public std::exception + { + public: + SHMAttachFailure() {} + virtual ~SHMAttachFailure() throw(){}; + + std::string type; + + virtual const char* what() const throw() + { + return "SHM ATTACH FAILURE"; + } + }; + } } diff --git a/library/DFHackAPI.cpp b/library/DFHackAPI.cpp index 5eacbfaf2..ff1b71a20 100644 --- a/library/DFHackAPI.cpp +++ b/library/DFHackAPI.cpp @@ -190,7 +190,7 @@ API::~API() delete d; } -#define SHMCMD ((shm_cmd *)d->shm_start)->pingpong +#define SHMCMD(num) ((shm_cmd *)d->shm_start)[num]->pingpong #define SHMHDR ((shm_core_hdr *)d->shm_start) #define SHMMAPSHDR ((Maps::shm_maps_hdr *)d->shm_start) #define SHMDATA(type) ((type *)(d->shm_start + SHM_HEADER)) @@ -243,8 +243,7 @@ bool API::InitMap() off->z_count_offset = z_count_offset; full_barrier const uint32_t cmd = Maps::MAP_INIT + d->maps_module << 16; - SHMCMD = cmd; - g_pProcess->waitWhile(cmd); + g_pProcess->SetAndWait(cmd); //cerr << "Map acceleration enabled!" << endl; } @@ -324,13 +323,8 @@ bool API::ReadBlock40d(uint32_t x, uint32_t y, uint32_t z, mapblock40d * buffer) SHMMAPSHDR->y = y; SHMMAPSHDR->z = z; volatile uint32_t cmd = Maps::MAP_READ_BLOCK_BY_COORDS + (d->maps_module << 16); - full_barrier - SHMCMD = cmd; - full_barrier - if(!g_pProcess->waitWhile(cmd)) - { + if(!g_pProcess->SetAndWait(cmd)) return false; - } memcpy(buffer,SHMDATA(mapblock40d),sizeof(mapblock40d)); return true; } diff --git a/library/DFProcess-linux-SHM.cpp b/library/DFProcess-linux-SHM.cpp index 89dc8a308..3ca1bdebc 100644 --- a/library/DFProcess-linux-SHM.cpp +++ b/library/DFProcess-linux-SHM.cpp @@ -43,96 +43,77 @@ class SHMProcess::Private public: Private() { - my_descriptor = NULL; - my_pid = 0; - my_shm = 0; - my_shmid = -1; - my_window = NULL; + memdescriptor = NULL; + process_ID = 0; + shm_addr = 0; + //shm_addr_with_cl_idx = 0; + shm_ID = -1; + window = NULL; attached = false; - suspended = false; identified = false; useYield = false; - my_SVfileLock = -1; - my_CLfileLock = -1; + server_lock = -1; + client_lock = -1; + suspend_lock = -1; + attachmentIdx = 0; + locked = false; }; ~Private(){}; - memory_info * my_descriptor; - DFWindow * my_window; - pid_t my_pid; - char *my_shm; - int my_shmid; + memory_info * memdescriptor; + DFWindow * window; + pid_t process_ID; + char *shm_addr; + int shm_ID; Process* q; - int my_SVfileLock; - int my_CLfileLock; + int server_lock; + int client_lock; + int suspend_lock; + int attachmentIdx; + + bool attached; - bool suspended; + bool locked; bool identified; bool useYield; - bool validate(char* exe_file, uint32_t pid, std::vector< memory_info* >& known_versions); + bool validate(std::vector< memory_info* >& known_versions); bool Aux_Core_Attach(bool & versionOK, pid_t & PID); - bool waitWhile (uint32_t state); + //bool waitWhile (uint32_t state); + bool SetAndWait (uint32_t state); bool GetLocks(); bool AreLocksOk(); void FreeLocks(); }; -// some helpful macros to keep the code bloat in check -#define SHMCMD ((shm_cmd *)my_shm)->pingpong -#define D_SHMCMD ((shm_cmd *)d->my_shm)->pingpong +#define SHMCMD ( (uint32_t *) shm_addr)[attachmentIdx] +#define D_SHMCMD ( (uint32_t *) (d->shm_addr))[d->attachmentIdx] -#define SHMHDR ((shm_core_hdr *)my_shm) -#define D_SHMHDR ((shm_core_hdr *)d->my_shm) +#define SHMHDR ((shm_core_hdr *)shm_addr) +#define D_SHMHDR ((shm_core_hdr *)(d->shm_addr)) -#define SHMDATA(type) ((type *)(my_shm + SHM_HEADER)) -#define D_SHMDATA(type) ((type *)(d->my_shm + SHM_HEADER)) +#define SHMDATA(type) ((type *)(shm_addr + SHM_HEADER)) +#define D_SHMDATA(type) ((type *)(d->shm_addr + SHM_HEADER)) -/* -Yeah. with no way to synchronize things (locks are slow, the OS doesn't give us enough control over scheduling) -we end up with this silly thing -*/ -bool SHMProcess::Private::waitWhile (uint32_t state) +bool SHMProcess::Private::SetAndWait (uint32_t state) { uint32_t cnt = 0; - struct shmid_ds descriptor; + if(!attached) return false; + SHMCMD = state; while (SHMCMD == state) { - if(cnt == 10000)// check if the other process is still there + if(cnt == 10000)// check if the other process is still there, don't hammer the kernel too much. { - - /* - shmctl(my_shmid, IPC_STAT, &descriptor); - if(descriptor.shm_nattch == 1)// DF crashed or exited - no way to tell? - { - //detach the shared memory - shmdt(my_shm); - attached = suspended = false; - - // we aren't the current process anymore - g_pProcess = NULL; - - throw Error::SHMServerDisappeared(); - return false; - } - else - { - cnt = 0; - } - */ if(!AreLocksOk()) { //detach the shared memory - shmdt(my_shm); - attached = suspended = false; - + shmdt(shm_addr); + FreeLocks(); + attached = locked = identified = false; // we aren't the current process anymore g_pProcess = NULL; - FreeLocks(); - throw Error::SHMServerDisappeared(); - return false; } else { @@ -145,12 +126,9 @@ bool SHMProcess::Private::waitWhile (uint32_t state) } cnt++; } + // server returned a generic error if(SHMCMD == CORE_ERROR) { - SHMCMD = CORE_RUNNING; - attached = suspended = false; - cerr << "shm server error!" << endl; - assert (false); return false; } return true; @@ -160,9 +138,9 @@ bool SHMProcess::Private::waitWhile (uint32_t state) Yeah. with no way to synchronize things (locks are slow, the OS doesn't give us enough control over scheduling) we end up with this silly thing */ -bool SHMProcess::waitWhile (uint32_t state) +bool SHMProcess::SetAndWait (uint32_t state) { - return d->waitWhile(state); + return d->SetAndWait(state); } uint32_t OS_getAffinity() @@ -174,30 +152,13 @@ uint32_t OS_getAffinity() return affinity; } - -bool SHMProcess::Private::Aux_Core_Attach(bool & versionOK, pid_t & PID) -{ - SHMDATA(coreattach)->cl_affinity = OS_getAffinity(); - gcc_barrier - SHMCMD = CORE_ATTACH; - if(!waitWhile(CORE_ATTACH)) - return false; - gcc_barrier - versionOK =( SHMDATA(coreattach)->sv_version == CORE_VERSION ); - PID = SHMDATA(coreattach)->sv_PID; - useYield = SHMDATA(coreattach)->sv_useYield; - #ifdef DEBUG - if(useYield) cerr << "Using Yield!" << endl; - #endif - return true; -} - +// test if we have client and server locks and the server is present bool SHMProcess::Private::AreLocksOk() { // both locks are inited (we hold our lock) - if(my_CLfileLock != -1 && my_SVfileLock != -1) + if(client_lock != -1 && server_lock != -1) { - if(lockf(my_SVfileLock,F_TEST,0) == -1) // and server holds its lock + if(lockf(server_lock,F_TEST,0) == -1) // and server holds its lock { return true; // OK, locks are good } @@ -208,16 +169,23 @@ bool SHMProcess::Private::AreLocksOk() void SHMProcess::Private::FreeLocks() { - if(my_CLfileLock != -1) + attachmentIdx = -1; + if(client_lock != -1) + { + lockf(client_lock,F_ULOCK,0); + close(client_lock); + client_lock = -1; + } + if(server_lock != -1) { - lockf(my_CLfileLock,F_ULOCK,0); - close(my_CLfileLock); - my_CLfileLock = -1; + close(server_lock); + server_lock = -1; } - if(my_SVfileLock != -1) + if(suspend_lock != -1) { - close(my_SVfileLock); - my_SVfileLock = -1; + close(suspend_lock); + locked = false; + suspend_lock = -1; } } @@ -226,123 +194,102 @@ bool SHMProcess::Private::GetLocks() char name[256]; // try to acquire locks // look at the server lock, if it's locked, the server is present - sprintf(name, "/tmp/DFHack/%d/SVlock",my_pid,name); - my_SVfileLock = open(name,O_WRONLY); - if(my_SVfileLock == -1) + sprintf(name, "/tmp/DFHack/%d/SVlock",process_ID); + server_lock = open(name,O_WRONLY); + if(server_lock == -1) { + // cerr << "can't open sv lock" << endl; return false; } - if(lockf( my_SVfileLock, F_TEST, 0 ) != -1) + if(lockf( server_lock, F_TEST, 0 ) != -1) { - close(my_SVfileLock); + cerr << "sv lock not locked" << endl; + close(server_lock); + server_lock = -1; return false; } - // open the client lock, try to lock it - sprintf(name, "/tmp/DFHack/%d/CLlock",my_pid,name); - my_CLfileLock = open(name,O_WRONLY); - if(my_CLfileLock == -1) + for(int i = 0; i < SHM_MAX_CLIENTS; i++) { - close(my_SVfileLock); - return false; - } - if(lockf(my_CLfileLock,F_TLOCK, 0) == -1) - { - // couldn't acquire lock - close(my_SVfileLock); - close(my_CLfileLock); - return false; + // open the client suspend locked + sprintf(name, "/tmp/DFHack/%d/CLSlock%d",process_ID,i); + suspend_lock = open(name,O_WRONLY); + if(suspend_lock == -1) + { + cerr << "can't open cl S-lock " << i << endl; + // couldn't open lock + continue; + } + + // open the client lock, try to lock it + sprintf(name, "/tmp/DFHack/%d/CLlock%d",process_ID,i); + client_lock = open(name,O_WRONLY); + if(client_lock == -1) + { + cerr << "can't open cl lock " << i << endl; + close(suspend_lock); + locked = false; + suspend_lock = -1; + // couldn't open lock + continue; + } + if(lockf(client_lock,F_TLOCK, 0) == -1) + { + // couldn't acquire lock + cerr << "can't acquire cl lock " << i << endl; + close(suspend_lock); + locked = false; + suspend_lock = -1; + close(client_lock); + client_lock = -1; + continue; + } + // ok, we have all the locks we need! + attachmentIdx = i; + return true; } - // ok, we have all the locks! - return true; + close(server_lock); + server_lock = -1; + cerr << "can't get any client locks" << endl; + return false; } SHMProcess::SHMProcess(uint32_t PID, vector< memory_info* >& known_versions) : d(new Private()) { - char exe_link_name [256]; - char target_name[1024]; - int target_result; - - /* - * Locate the segment. - */ - if ((d->my_shmid = shmget(SHM_KEY + PID, SHM_SIZE, 0666)) < 0) - { - return; - } - - /* - * Attach the segment - */ - if ((d->my_shm = (char *) shmat(d->my_shmid, NULL, 0)) == (char *) -1) - { - return; - } - - // set pid and gets lock for it - d->my_pid = PID; - if(!d->GetLocks()) + d->process_ID = PID; + if(!attach()) { - fprintf(stderr,"Couldn't get locks for PID %d'\n", PID); - shmdt(d->my_shm); + // couldn't attach to process return; } - /* * Test bridge version, get PID, sync Yield */ bool bridgeOK; - if(!d->Aux_Core_Attach(bridgeOK,d->my_pid)) + if(!d->Aux_Core_Attach(bridgeOK,d->process_ID)) { - fprintf(stderr,"DF terminated during reading\n"); - shmdt(d->my_shm); - // free locks - d->FreeLocks(); - return; + detach(); + throw Error::SHMAttachFailure(); } if(!bridgeOK) { - fprintf(stderr,"SHM bridge version mismatch\n"); - shmdt(d->my_shm); - // free locks - d->FreeLocks(); - return; - } - - // find the binary - sprintf(exe_link_name,"/proc/%d/exe", d->my_pid); - target_result = readlink(exe_link_name, target_name, sizeof(target_name)-1); - if (target_result == -1) - { - perror("readlink"); - shmdt(d->my_shm); - // free locks - d->FreeLocks(); - return; + detach(); + throw Error::SHMVersionMismatch(); } - // make sure we have a null terminated string... - // see http://www.opengroup.org/onlinepubs/000095399/functions/readlink.html - target_name[target_result] = 0; + // try to identify the DF version (md5 the binary, compare with known versions) + d->validate(known_versions); + d->window = new DFWindow(this); - // try to identify the DF version - d->validate(target_name, d->my_pid, known_versions); - d->my_window = new DFWindow(this); - - gcc_barrier - // at this point, DF is stopped and waiting for commands. make it run again - D_SHMCMD = CORE_RUNNING; - shmdt(d->my_shm); // detach so we don't attach twice when attach() is called - - // free locks - d->FreeLocks(); + // detach + detach(); } bool SHMProcess::isSuspended() { - return d->suspended; + return d->locked; } bool SHMProcess::isAttached() { @@ -354,13 +301,28 @@ bool SHMProcess::isIdentified() return d->identified; } -bool SHMProcess::Private::validate(char * exe_file, uint32_t pid, vector & known_versions) +bool SHMProcess::Private::validate(vector & known_versions) { + char exe_link_name [256]; + char target_name[1024]; + int target_result; + // find the binary + sprintf(exe_link_name,"/proc/%d/exe", process_ID); + target_result = readlink(exe_link_name, target_name, sizeof(target_name)-1); + if (target_result == -1) + { + perror("readlink"); + return false; + } + // make sure we have a null terminated string... + // see http://www.opengroup.org/onlinepubs/000095399/functions/readlink.html + target_name[target_result] = 0; + md5wrapper md5; // get hash of the running DF process - string hash = md5.getHashFromFile(exe_file); + string hash = md5.getHashFromFile(target_name); vector::iterator it; - cerr << exe_file << " " << hash << endl; + // cerr << exe_file << " " << hash << endl; // iterate over the list of memory locations for ( it=known_versions.begin() ; it < known_versions.end(); it++ ) { @@ -368,10 +330,9 @@ bool SHMProcess::Private::validate(char * exe_file, uint32_t pid, vector getString("md5")) // are the md5 hashes the same? { memory_info * m = *it; - my_descriptor = m; - my_pid = pid; + memdescriptor = m; identified = true; - cerr << "identified " << m->getVersion() << endl; + // cerr << "identified " << m->getVersion() << endl; return true; } } @@ -390,31 +351,37 @@ SHMProcess::~SHMProcess() detach(); } // destroy data model. this is assigned by processmanager - if(d->my_window) + if(d->window) { - delete d->my_window; + delete d->window; } delete d; } memory_info * SHMProcess::getDescriptor() { - return d->my_descriptor; + return d->memdescriptor; } DFWindow * SHMProcess::getWindow() { - return d->my_window; + return d->window; } int SHMProcess::getPID() { - return d->my_pid; + return d->process_ID; } -//FIXME: implement +// there is only one we care about. bool SHMProcess::getThreadIDs(vector & threads ) { + if(d->attached) + { + threads.clear(); + threads.push_back(d->process_ID); + return true; + } return false; } @@ -424,7 +391,7 @@ void SHMProcess::getMemRanges( vector & ranges ) char buffer[1024]; char permissions[5]; // r/-, w/-, x/-, p/s, 0 - sprintf(buffer, "/proc/%lu/maps", d->my_pid); + sprintf(buffer, "/proc/%lu/maps", d->process_ID); FILE *mapFile = ::fopen(buffer, "r"); uint64_t offset, device1, device2, node; @@ -451,37 +418,79 @@ bool SHMProcess::suspend() { return false; } - if(d->suspended) + if(d->locked) { return true; } - D_SHMCMD = CORE_SUSPEND; - if(!waitWhile(CORE_SUSPEND)) + + // FIXME: this should be controlled on the server side + // FIXME: IF server got CORE_RUN in this frame, interpret CORE_SUSPEND as CORE_STEP + // did we just resume a moment ago? + if(D_SHMCMD == CORE_RUN) + { + //fprintf(stderr,"%d invokes step\n",d->attachmentIdx); + // wait for the next window + if(!d->SetAndWait(CORE_STEP)) + { + throw Error::SHMLockingError("if(!d->SetAndWait(CORE_STEP))"); + } + } + else { - return false; + //fprintf(stderr,"%d invokes suspend\n",d->attachmentIdx); + // lock now + if(!d->SetAndWait(CORE_SUSPEND)) + { + throw Error::SHMLockingError("if(!d->SetAndWait(CORE_SUSPEND))"); + } } - d->suspended = true; - return true; + //fprintf(stderr,"waiting for lock\n"); + // we wait for the server to give up our suspend lock (held by default) + if(lockf(d->suspend_lock,F_LOCK,0) == 0) + { + d->locked = true; + return true; + } + return false; } +// FIXME: needs a good think-through bool SHMProcess::asyncSuspend() { if(!d->attached) { return false; } - if(d->suspended) + if(d->locked) { return true; } - if(D_SHMCMD == CORE_SUSPENDED) + uint32_t cmd = D_SHMCMD; + if(cmd == CORE_SUSPENDED) { - d->suspended = true; - return true; + // we have to hold the lock to be really suspended + if(lockf(d->suspend_lock,F_LOCK,0) == 0) + { + d->locked = true; + return true; + } + return false; } else { - D_SHMCMD = CORE_SUSPEND; + // did we just resume a moment ago? + if(cmd == CORE_STEP) + { + return false; + } + else if(cmd == CORE_RUN) + { + D_SHMCMD = CORE_STEP; + } + else + { + D_SHMCMD = CORE_SUSPEND; + } return false; } } @@ -491,21 +500,30 @@ bool SHMProcess::forceresume() return resume(); } +// FIXME: wait for the server to advance a step! bool SHMProcess::resume() { if(!d->attached) return false; - if(!d->suspended) + if(!d->locked) return true; - D_SHMCMD = CORE_RUNNING; - d->suspended = false; - return true; + // unlock the suspend lock + if(lockf(d->suspend_lock,F_ULOCK,0) == 0) + { + d->locked = false; + if(d->SetAndWait(CORE_RUN)) // we have to make sure the server responds! + { + return true; + } + throw Error::SHMLockingError("if(d->SetAndWait(CORE_RUN))"); + } + throw Error::SHMLockingError("if(lockf(d->suspend_lock,F_ULOCK,0) == 0)"); + return false; } bool SHMProcess::attach() { - int status; if(g_pProcess != 0) { // FIXME: throw exception here - programmer error @@ -514,31 +532,39 @@ bool SHMProcess::attach() } if(!d->GetLocks()) { - cerr << "server is full or not really there!" << endl; + //cerr << "server is full or not really there!" << endl; return false; } + /* + * Locate the segment. + */ + if ((d->shm_ID = shmget(SHM_KEY + d->process_ID, SHM_SIZE, 0666)) < 0) + { + d->FreeLocks(); + cerr << "can't find segment" << endl; // FIXME: throw + return false; + } + /* * Attach the segment */ - if ((d->my_shm = (char *) shmat(d->my_shmid, NULL, 0)) != (char *) -1) + if ((d->shm_addr = (char *) shmat(d->shm_ID, NULL, 0)) == (char *) -1) { - d->attached = true; - if(suspend()) - { - d->suspended = true; - g_pProcess = this; - return true; - } - d->attached = false; - cerr << "unable to suspend" << endl; - shmdt(d->my_shm); d->FreeLocks(); + cerr << "can't attach segment" << endl; // FIXME: throw return false; } - cerr << "unable to attach" << endl; - d->FreeLocks(); - return false; + d->attached = true; + if(!suspend()) + { + shmdt(d->shm_addr); + d->FreeLocks(); + cerr << "unable to suspend" << endl; + return false; + } + g_pProcess = this; + return true; } bool SHMProcess::detach() @@ -547,18 +573,18 @@ bool SHMProcess::detach() { return false; } - if(d->suspended) + if(d->locked) { resume(); } // detach segment - if(shmdt(d->my_shm) != -1) + if(shmdt(d->shm_addr) != -1) { + d->FreeLocks(); + d->locked = false; d->attached = false; - d->suspended = false; - d->my_shm = 0; + d->shm_addr = 0; g_pProcess = 0; - d->FreeLocks(); return true; } // fail if we can't detach @@ -569,15 +595,16 @@ bool SHMProcess::detach() void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buffer) { + if(!d->locked) throw Error::SHMAccessDenied(); + // normal read under 1MB if(size <= SHM_BODY) { D_SHMHDR->address = src_address; D_SHMHDR->length = size; gcc_barrier - D_SHMCMD = CORE_DFPP_READ; - waitWhile(CORE_DFPP_READ); - memcpy (target_buffer, d->my_shm + SHM_HEADER,size); + d->SetAndWait(CORE_READ); + memcpy (target_buffer, D_SHMDATA(void),size); } // a big read, we pull data over the shm in iterations else @@ -590,9 +617,8 @@ void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buff D_SHMHDR->address = src_address; D_SHMHDR->length = to_read; gcc_barrier - D_SHMCMD = CORE_DFPP_READ; - waitWhile(CORE_DFPP_READ); - memcpy (target_buffer, d->my_shm + SHM_HEADER,size); + d->SetAndWait(CORE_READ); + memcpy (target_buffer, D_SHMDATA(void) ,size); // decrease size by bytes read size -= to_read; // move the cursors @@ -606,54 +632,60 @@ void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buff uint8_t SHMProcess::readByte (const uint32_t offset) { + if(!d->locked) throw Error::SHMAccessDenied(); + D_SHMHDR->address = offset; gcc_barrier - D_SHMCMD = CORE_READ_BYTE; - waitWhile(CORE_READ_BYTE); + d->SetAndWait(CORE_READ_BYTE); return D_SHMHDR->value; } void SHMProcess::readByte (const uint32_t offset, uint8_t &val ) { + if(!d->locked) throw Error::SHMAccessDenied(); + D_SHMHDR->address = offset; gcc_barrier - D_SHMCMD = CORE_READ_BYTE; - waitWhile(CORE_READ_BYTE); + d->SetAndWait(CORE_READ_BYTE); val = D_SHMHDR->value; } uint16_t SHMProcess::readWord (const uint32_t offset) { + if(!d->locked) throw Error::SHMAccessDenied(); + D_SHMHDR->address = offset; gcc_barrier - D_SHMCMD = CORE_READ_WORD; - waitWhile(CORE_READ_WORD); + d->SetAndWait(CORE_READ_WORD); return D_SHMHDR->value; } void SHMProcess::readWord (const uint32_t offset, uint16_t &val) { + if(!d->locked) throw Error::SHMAccessDenied(); + D_SHMHDR->address = offset; gcc_barrier - D_SHMCMD = CORE_READ_WORD; - waitWhile(CORE_READ_WORD); + d->SetAndWait(CORE_READ_WORD); val = D_SHMHDR->value; } uint32_t SHMProcess::readDWord (const uint32_t offset) { + if(!d->locked) throw Error::SHMAccessDenied(); + D_SHMHDR->address = offset; gcc_barrier - D_SHMCMD = CORE_READ_DWORD; - waitWhile(CORE_READ_DWORD); + d->SetAndWait(CORE_READ_DWORD); return D_SHMHDR->value; } void SHMProcess::readDWord (const uint32_t offset, uint32_t &val) { + if(!d->locked) throw Error::SHMAccessDenied(); + D_SHMHDR->address = offset; gcc_barrier - D_SHMCMD = CORE_READ_DWORD; - waitWhile(CORE_READ_DWORD); + d->SetAndWait(CORE_READ_DWORD); val = D_SHMHDR->value; } @@ -663,43 +695,47 @@ void SHMProcess::readDWord (const uint32_t offset, uint32_t &val) void SHMProcess::writeDWord (uint32_t offset, uint32_t data) { + if(!d->locked) throw Error::SHMAccessDenied(); + D_SHMHDR->address = offset; D_SHMHDR->value = data; gcc_barrier - D_SHMCMD = CORE_WRITE_DWORD; - waitWhile(CORE_WRITE_DWORD); + d->SetAndWait(CORE_WRITE_DWORD); } // using these is expensive. void SHMProcess::writeWord (uint32_t offset, uint16_t data) { + if(!d->locked) throw Error::SHMAccessDenied(); + D_SHMHDR->address = offset; D_SHMHDR->value = data; gcc_barrier - D_SHMCMD = CORE_WRITE_WORD; - waitWhile(CORE_WRITE_WORD); + d->SetAndWait(CORE_WRITE_WORD); } void SHMProcess::writeByte (uint32_t offset, uint8_t data) { + if(!d->locked) throw Error::SHMAccessDenied(); + D_SHMHDR->address = offset; D_SHMHDR->value = data; gcc_barrier - D_SHMCMD = CORE_WRITE_BYTE; - waitWhile(CORE_WRITE_BYTE); + d->SetAndWait(CORE_WRITE_BYTE); } void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buffer) { + if(!d->locked) throw Error::SHMAccessDenied(); + // normal write under 1MB if(size <= SHM_BODY) { D_SHMHDR->address = dst_address; D_SHMHDR->length = size; - memcpy(d->my_shm+SHM_HEADER,source_buffer, size); + memcpy(D_SHMDATA(void),source_buffer, size); gcc_barrier - D_SHMCMD = CORE_WRITE; - waitWhile(CORE_WRITE); + d->SetAndWait(CORE_WRITE); } // a big write, we push this over the shm in iterations else @@ -711,10 +747,9 @@ void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buf // write to_write bytes to dst_cursor D_SHMHDR->address = dst_address; D_SHMHDR->length = to_write; - memcpy(d->my_shm+SHM_HEADER,source_buffer, to_write); + memcpy(D_SHMDATA(void),source_buffer, to_write); gcc_barrier - D_SHMCMD = CORE_WRITE; - waitWhile(CORE_WRITE); + d->SetAndWait(CORE_WRITE); // decrease size by bytes written size -= to_write; // move the cursors @@ -729,6 +764,8 @@ void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buf // FIXME: butt-fugly const std::string SHMProcess::readCString (uint32_t offset) { + if(!d->locked) throw Error::SHMAccessDenied(); + std::string temp; char temp_c[256]; int counter = 0; @@ -746,6 +783,8 @@ const std::string SHMProcess::readCString (uint32_t offset) DfVector SHMProcess::readVector (uint32_t offset, uint32_t item_size) { + if(!d->locked) throw Error::SHMAccessDenied(); + /* GNU libstdc++ vector is three pointers long ptr start @@ -762,38 +801,42 @@ DfVector SHMProcess::readVector (uint32_t offset, uint32_t item_size) const std::string SHMProcess::readSTLString(uint32_t offset) { + if(!d->locked) throw Error::SHMAccessDenied(); + D_SHMHDR->address = offset; full_barrier - D_SHMCMD = CORE_READ_STL_STRING; - waitWhile(CORE_READ_STL_STRING); - //int length = ((shm_retval *)d->my_shm)->value; - return(string( (char *)d->my_shm+SHM_HEADER)); + d->SetAndWait(CORE_READ_STL_STRING); + return(string( D_SHMDATA(char) )); } size_t SHMProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity) { + if(!d->locked) throw Error::SHMAccessDenied(); + D_SHMHDR->address = offset; full_barrier - D_SHMCMD = CORE_READ_STL_STRING; - waitWhile(CORE_READ_STL_STRING); + d->SetAndWait(CORE_READ_STL_STRING); size_t length = D_SHMHDR->value; size_t fit = min(bufcapacity - 1, length); - strncpy(buffer,(char *)d->my_shm+SHM_HEADER,fit); + strncpy(buffer,D_SHMDATA(char),fit); buffer[fit] = 0; return fit; } void SHMProcess::writeSTLString(const uint32_t address, const std::string writeString) { + if(!d->locked) throw Error::SHMAccessDenied(); + D_SHMHDR->address = address; - strncpy(d->my_shm+SHM_HEADER,writeString.c_str(),writeString.length()+1); // length + 1 for the null terminator + strncpy(D_SHMDATA(char),writeString.c_str(),writeString.length()+1); // length + 1 for the null terminator full_barrier - D_SHMCMD = CORE_WRITE_STL_STRING; - waitWhile(CORE_WRITE_STL_STRING); + d->SetAndWait(CORE_WRITE_STL_STRING); } string SHMProcess::readClassName (uint32_t vptr) { + if(!d->locked) throw Error::SHMAccessDenied(); + int typeinfo = readDWord(vptr - 0x4); int typestring = readDWord(typeinfo + 0x4); string raw = readCString(typestring); @@ -802,22 +845,18 @@ string SHMProcess::readClassName (uint32_t vptr) return raw.substr(start,end-start - 2); // trim the 'st' from the end } -// FIXME: having this around could lead to bad things in the hands of unsuspecting fools -// *!!DON'T BE AN UNSUSPECTING FOOL!!* -// the whole SHM thing works only because copying DWORDS is an atomic operation on i386 and x86_64 archs -// get module index by name and version. bool 1 = error +// get module index by name and version. bool 0 = error bool SHMProcess::getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) { - modulelookup * payload = (modulelookup *) (d->my_shm + SHM_HEADER); + if(!d->locked) throw Error::SHMAccessDenied(); + + modulelookup * payload = D_SHMDATA(modulelookup); payload->version = version; strncpy(payload->name,name,255); payload->name[255] = 0; - full_barrier - - D_SHMCMD = CORE_ACQUIRE_MODULE; - if(!waitWhile(CORE_ACQUIRE_MODULE)) + if(!SetAndWait(CORE_ACQUIRE_MODULE)) { return false; // FIXME: throw a fatal exception instead } @@ -832,5 +871,26 @@ bool SHMProcess::getModuleIndex (const char * name, const uint32_t version, uint char * SHMProcess::getSHMStart (void) { - return d->my_shm; + if(!d->locked) return 0; //THROW HERE! + + return /*d->shm_addr_with_cl_idx*/ d->shm_addr; +} + +bool SHMProcess::Private::Aux_Core_Attach(bool & versionOK, pid_t & PID) +{ + if(!locked) throw Error::SHMAccessDenied(); + + SHMDATA(coreattach)->cl_affinity = OS_getAffinity(); + if(!SetAndWait(CORE_ATTACH)) return false; + /* + cerr <<"CORE_VERSION" << CORE_VERSION << endl; + cerr <<"server CORE_VERSION" << SHMDATA(coreattach)->sv_version << endl; + */ + versionOK =( SHMDATA(coreattach)->sv_version == CORE_VERSION ); + PID = SHMDATA(coreattach)->sv_PID; + useYield = SHMDATA(coreattach)->sv_useYield; + #ifdef DEBUG + if(useYield) cerr << "Using Yield!" << endl; + #endif + return true; } \ No newline at end of file diff --git a/library/DFProcess-windows-SHM.cpp b/library/DFProcess-windows-SHM.cpp index 8886c0e7c..016ea2dad 100644 --- a/library/DFProcess-windows-SHM.cpp +++ b/library/DFProcess-windows-SHM.cpp @@ -32,81 +32,65 @@ class SHMProcess::Private public: Private() { - my_descriptor = NULL; - my_pid = 0; - my_shm = 0; - my_window = NULL; + memdescriptor = NULL; + process_ID = 0; + shm_addr = 0; + window = NULL; attached = false; - suspended = false; + locked = false; identified = false; useYield = 0; DFSVMutex = 0; DFCLMutex = 0; + DFCLSuspendMutex = 0; + attachmentIdx = -1; }; ~Private(){}; - memory_info * my_descriptor; - DFWindow * my_window; - uint32_t my_pid; - char *my_shm; + memory_info * memdescriptor; + DFWindow * window; + SHMProcess * q; + uint32_t process_ID; + char *shm_addr; HANDLE DFSVMutex; HANDLE DFCLMutex; + HANDLE DFCLSuspendMutex; + int attachmentIdx; bool attached; - bool suspended; + bool locked; bool identified; bool useYield; - bool waitWhile (uint32_t state); - bool isValidSV(); + bool validate(std::vector< memory_info* >& known_versions); + bool Aux_Core_Attach(bool & versionOK, uint32_t & PID); + bool SetAndWait (uint32_t state); + bool GetLocks(); + bool AreLocksOk(); + void FreeLocks(); }; // some helpful macros to keep the code bloat in check -#define SHMCMD ((shm_cmd *)my_shm)->pingpong -#define D_SHMCMD ((shm_cmd *)d->my_shm)->pingpong - -#define SHMHDR ((shm_core_hdr *)my_shm) -#define D_SHMHDR ((shm_core_hdr *)d->my_shm) +#define SHMCMD ( (uint32_t *) shm_addr)[attachmentIdx] +#define D_SHMCMD ( (uint32_t *) (d->shm_addr))[d->attachmentIdx] -#define SHMDATA(type) ((type *)(my_shm + SHM_HEADER)) -#define D_SHMDATA(type) ((type *)(d->my_shm + SHM_HEADER)) +#define SHMHDR ((shm_core_hdr *)shm_addr) +#define D_SHMHDR ((shm_core_hdr *)(d->shm_addr)) -// is the other side still there? -bool SHMProcess::Private::isValidSV() -{ - // try if CL mutex is free - uint32_t result = WaitForSingleObject(DFSVMutex,0); - - switch (result) - { - case WAIT_ABANDONED: - case WAIT_OBJECT_0: - { - ReleaseMutex(DFSVMutex); - return false; - } - case WAIT_TIMEOUT: - { - // mutex is held by DF - return true; - } - default: - case WAIT_FAILED: - { - // TODO: now how do I respond to this? - return false; - } - } -} +#define SHMDATA(type) ((type *)(shm_addr + SHM_HEADER)) +#define D_SHMDATA(type) ((type *)(d->shm_addr + SHM_HEADER)) -bool SHMProcess::waitWhile (uint32_t state) +bool SHMProcess::SetAndWait (uint32_t state) { - return d->waitWhile(state); + return d->SetAndWait(state); } -bool SHMProcess::Private::waitWhile (uint32_t state) +bool SHMProcess::Private::SetAndWait (uint32_t state) { uint32_t cnt = 0; + if(!attached) return false; + SHMCMD = state; + while (SHMCMD == state) { // yield the CPU, only on single-core CPUs @@ -116,13 +100,14 @@ bool SHMProcess::Private::waitWhile (uint32_t state) } if(cnt == 10000) { - if(!isValidSV())// DF not there anymore? + if(!AreLocksOk())// DF not there anymore? { - attached = suspended = false; - ReleaseMutex(DFCLMutex); - UnmapViewOfFile(my_shm); + UnmapViewOfFile(shm_addr); + FreeLocks(); + attached = locked = identified = false; + // we aren't the current process anymore + g_pProcess = NULL; throw Error::SHMServerDisappeared(); - return false; } else { @@ -133,9 +118,6 @@ bool SHMProcess::Private::waitWhile (uint32_t state) } if(SHMCMD == CORE_ERROR) { - SHMCMD = CORE_RUNNING; - attached = suspended = false; - cerr << "shm server error!" << endl; return false; } return true; @@ -149,28 +131,133 @@ uint32_t OS_getAffinity() return dwProcessAffinityMask; } -bool SHMProcess::Private::Aux_Core_Attach(bool & versionOK, uint32_t & PID) +void SHMProcess::Private::FreeLocks() { - SHMDATA(coreattach)->cl_affinity = OS_getAffinity(); - full_barrier - SHMCMD = CORE_ATTACH; - if(!waitWhile(CORE_ATTACH)) + attachmentIdx = -1; + if(DFCLMutex != 0) + { + ReleaseMutex(DFCLMutex); + CloseHandle(DFCLMutex); + DFCLMutex = 0; + } + if(DFSVMutex != 0) + { + CloseHandle(DFSVMutex); + DFSVMutex = 0; + } + if(DFCLSuspendMutex != 0) + { + ReleaseMutex(DFCLSuspendMutex); + CloseHandle(DFCLSuspendMutex); + // FIXME: maybe also needs ReleaseMutex! + DFCLSuspendMutex = 0; + locked = false; + } +} + +bool SHMProcess::Private::GetLocks() +{ + char name[256]; + // try to acquire locks + // look at the server lock, if it's locked, the server is present + sprintf(name,"DFSVMutex-%d",process_ID); + DFSVMutex = OpenMutex(SYNCHRONIZE,0, name); + if(DFSVMutex == 0) + { + // cerr << "can't open sv lock" << endl; return false; - full_barrier - versionOK =( SHMDATA(coreattach)->sv_version == CORE_VERSION ); - PID = SHMDATA(coreattach)->sv_PID; - useYield = SHMDATA(coreattach)->sv_useYield; - #ifdef DEBUG - if(useYield) cerr << "Using Yield!" << endl; - #endif - return true; + } + // unlike the F_TEST of lockf, this one actually locks. we have to release + if(WaitForSingleObject(DFSVMutex,0) == 0) + { + ReleaseMutex(DFSVMutex); + // cerr << "sv lock not locked" << endl; + CloseHandle(DFSVMutex); + DFSVMutex = 0; + return false; + } + + for(int i = 0; i < SHM_MAX_CLIENTS; i++) + { + // open the client suspend locked + sprintf(name, "DFCLSuspendMutex-%d-%d",process_ID,i); + DFCLSuspendMutex = OpenMutex(SYNCHRONIZE,0, name); + if(DFCLSuspendMutex == 0) + { + //cerr << "can't open cl S-lock " << i << endl; + // couldn't open lock + continue; + } + + // open the client lock, try to lock it + + sprintf(name,"DFCLMutex-%d-%d",process_ID,i); + DFCLMutex = OpenMutex(SYNCHRONIZE,0,name); + if(DFCLMutex == 0) + { + //cerr << "can't open cl lock " << i << endl; + CloseHandle(DFCLSuspendMutex); + locked = false; + DFCLSuspendMutex = 0; + continue; + } + uint32_t waitstate = WaitForSingleObject(DFCLMutex,0); + if(waitstate == WAIT_FAILED || waitstate == WAIT_TIMEOUT ) + { + //cerr << "can't acquire cl lock " << i << endl; + CloseHandle(DFCLSuspendMutex); + locked = false; + DFCLSuspendMutex = 0; + CloseHandle(DFCLMutex); + DFCLMutex = 0; + continue; + } + // ok, we have all the locks we need! + attachmentIdx = i; + return true; + } + CloseHandle(DFSVMutex); + DFSVMutex = 0; + // cerr << "can't get any client locks" << endl; + return false; } -SHMProcess::SHMProcess(uint32_t PID, vector & known_versions) -: d(new Private()) +// is the other side still there? +bool SHMProcess::Private::AreLocksOk() { + // both locks are inited (we hold our lock) + if(DFCLMutex != 0 && DFSVMutex != 0) + { + // try if CL mutex is free + switch (WaitForSingleObject(DFSVMutex,0)) + { + case WAIT_ABANDONED: + case WAIT_OBJECT_0: + { + ReleaseMutex(DFSVMutex); + return false; + } + case WAIT_TIMEOUT: + { + // mutex is held by DF + return true; + } + default: + case WAIT_FAILED: + { + // TODO: now how do I respond to this? + return false; + } + } + } + return false; +} + + + + /* char svmutexname [256]; - sprintf(svmutexname,"DFSVMutex-%d",PID); + char clmutexname [256]; sprintf(clmutexname,"DFCLMutex-%d",PID); @@ -185,119 +272,39 @@ SHMProcess::SHMProcess(uint32_t PID, vector & known_versions) { return; } - d->my_pid = PID; - + */ + +SHMProcess::SHMProcess(uint32_t PID, vector & known_versions) +: d(new Private()) +{ + d->process_ID = PID; + d->q = this; // attach the SHM if(!attach()) { return; } - // Test bridge version, get PID, sync Yield bool bridgeOK; - bool error = 0; - if(!d->Aux_Core_Attach(bridgeOK,d->my_pid)) + if(!d->Aux_Core_Attach(bridgeOK,d->process_ID)) { - fprintf(stderr,"DF terminated during reading\n"); - error = 1; + detach(); + throw Error::SHMAttachFailure(); } else if(!bridgeOK) { - fprintf(stderr,"SHM bridge version mismatch\n"); - error = 1; - } - if(error) - { - D_SHMCMD = CORE_RUNNING; - UnmapViewOfFile(d->my_shm); - ReleaseMutex(d->DFCLMutex); - CloseHandle(d->DFSVMutex); - d->DFSVMutex = 0; - CloseHandle(d->DFCLMutex); - d->DFCLMutex = 0; - return; - } - - // try to identify the DF version - do // glorified goto - { - IMAGE_NT_HEADERS32 pe_header; - IMAGE_SECTION_HEADER sections[16]; - HMODULE hmod = NULL; - DWORD junk; - HANDLE hProcess; - bool found = false; - d->identified = false; - // open process, we only need the process open - hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, d->my_pid ); - if (NULL == hProcess) - break; - - // try getting the first module of the process - if(EnumProcessModules(hProcess, &hmod, 1 * sizeof(HMODULE), &junk) == 0) - { - CloseHandle(hProcess); - // cout << "EnumProcessModules fail'd" << endl; - break; - } - // got base ;) - uint32_t base = (uint32_t)hmod; - - // read from this process - uint32_t pe_offset = readDWord(base+0x3C); - read(base + pe_offset , sizeof(pe_header), (uint8_t *)&pe_header); - read(base + pe_offset+ sizeof(pe_header), sizeof(sections) , (uint8_t *)§ions ); - - // iterate over the list of memory locations - vector::iterator it; - for ( it=known_versions.begin() ; it < known_versions.end(); it++ ) - { - uint32_t pe_timestamp; - try - { - pe_timestamp = (*it)->getHexValue("pe_timestamp"); - } - catch(Error::MissingMemoryDefinition& e) - { - continue; - } - if (pe_timestamp == pe_header.FileHeader.TimeDateStamp) - { - memory_info *m = new memory_info(**it); - m->RebaseAll(base); - d->my_descriptor = m; - d->identified = true; - cerr << "identified " << m->getVersion() << endl; - break; - } - } - CloseHandle(hProcess); - } while (0); // glorified goto end - - if(d->identified) - { - d->my_window = new DFWindow(this); - } - else - { - D_SHMCMD = CORE_RUNNING; - UnmapViewOfFile(d->my_shm); - d->my_shm = 0; - ReleaseMutex(d->DFCLMutex); - CloseHandle(d->DFSVMutex); - d->DFSVMutex = 0; - CloseHandle(d->DFCLMutex); - d->DFCLMutex = 0; - return; + detach(); + throw Error::SHMVersionMismatch(); } - full_barrier + d->validate(known_versions); + d->window = new DFWindow(this); // at this point, DF is attached and suspended, make it run detach(); } bool SHMProcess::isSuspended() { - return d->suspended; + return d->locked; } bool SHMProcess::isAttached() { @@ -308,122 +315,224 @@ bool SHMProcess::isIdentified() { return d->identified; } - -SHMProcess::~SHMProcess() +bool SHMProcess::Private::validate(vector & known_versions) { - if(d->attached) + // try to identify the DF version + IMAGE_NT_HEADERS32 pe_header; + IMAGE_SECTION_HEADER sections[16]; + HMODULE hmod = NULL; + DWORD junk; + HANDLE hProcess; + bool found = false; + identified = false; + // open process, we only need the process open + hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, process_ID ); + if (NULL == hProcess) + return false; + + // try getting the first module of the process + if(EnumProcessModules(hProcess, &hmod, 1 * sizeof(HMODULE), &junk) == 0) { - detach(); + CloseHandle(hProcess); + // cout << "EnumProcessModules fail'd" << endl; + return false; } - // destroy data model. this is assigned by processmanager - if(d->my_descriptor) + // got base ;) + uint32_t base = (uint32_t)hmod; + + // read from this process + uint32_t pe_offset = q->readDWord(base+0x3C); + q->read(base + pe_offset , sizeof(pe_header), (uint8_t *)&pe_header); + q->read(base + pe_offset+ sizeof(pe_header), sizeof(sections) , (uint8_t *)§ions ); + + // iterate over the list of memory locations + vector::iterator it; + for ( it=known_versions.begin() ; it < known_versions.end(); it++ ) { - delete d->my_descriptor; + uint32_t pe_timestamp; + try + { + pe_timestamp = (*it)->getHexValue("pe_timestamp"); + } + catch(Error::MissingMemoryDefinition& e) + { + continue; + } + if (pe_timestamp == pe_header.FileHeader.TimeDateStamp) + { + memory_info *m = new memory_info(**it); + m->RebaseAll(base); + memdescriptor = m; + identified = true; + cerr << "identified " << m->getVersion() << endl; + CloseHandle(hProcess); + return true; + } } - if(d->my_window) + return false; +} +SHMProcess::~SHMProcess() +{ + if(d->attached) { - delete d->my_window; + detach(); } - // release mutex handles we have - if(d->DFCLMutex) + // destroy data model. this is assigned by processmanager + if(d->memdescriptor) { - CloseHandle(d->DFCLMutex); + delete d->memdescriptor; } - if(d->DFSVMutex) + if(d->window) { - CloseHandle(d->DFSVMutex); + delete d->window; } delete d; } memory_info * SHMProcess::getDescriptor() { - return d->my_descriptor; + return d->memdescriptor; } DFWindow * SHMProcess::getWindow() { - return d->my_window; + return d->window; } int SHMProcess::getPID() { - return d->my_pid; + return d->process_ID; } -//FIXME: implement bool SHMProcess::getThreadIDs(vector & threads ) { - return false; + HANDLE AllThreads = INVALID_HANDLE_VALUE; + THREADENTRY32 te32; + + AllThreads = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 ); + if( AllThreads == INVALID_HANDLE_VALUE ) + { + return false; + } + te32.dwSize = sizeof(THREADENTRY32 ); + + if( !Thread32First( AllThreads, &te32 ) ) + { + CloseHandle( AllThreads ); + return false; + } + + do + { + if( te32.th32OwnerProcessID == d->process_ID ) + { + threads.push_back(te32.th32ThreadID); + } + } while( Thread32Next(AllThreads, &te32 ) ); + + CloseHandle( AllThreads ); + return true; } -//FIXME: cross-reference with ELF segment entries? +//FIXME: use VirtualQuery to probe for memory ranges, cross-reference with base-corrected PE segment entries void SHMProcess::getMemRanges( vector & ranges ) { - char buffer[1024]; - char permissions[5]; // r/-, w/-, x/-, p/s, 0 + // code here is taken from hexsearch by Silas Dunmore. + // As this IMHO isn't a 'sunstantial portion' of anything, I'm not including the MIT license here - sprintf(buffer, "/proc/%lu/maps", d->my_pid); - FILE *mapFile = ::fopen(buffer, "r"); - uint64_t offset, device1, device2, node; + // I'm faking this, because there's no way I'm using VirtualQuery - while (fgets(buffer, 1024, mapFile)) - { - t_memrange temp; - temp.name[0] = 0; - sscanf(buffer, "%llx-%llx %s %llx %2llu:%2llu %llu %s", - &temp.start, - &temp.end, - (char*)&permissions, - &offset, &device1, &device2, &node, - (char*)&temp.name); - temp.read = permissions[0] == 'r'; - temp.write = permissions[1] == 'w'; - temp.execute = permissions[2] == 'x'; - ranges.push_back(temp); - } + t_memrange temp; + uint32_t base = d->memdescriptor->getBase(); + temp.start = base + 0x1000; // more fakery. + temp.end = base + readDWord(base+readDWord(base+0x3C)+0x50)-1; // yay for magic. + temp.read = 1; + temp.write = 1; + temp.execute = 0; // fake + strcpy(temp.name,"pants"); + ranges.push_back(temp); } bool SHMProcess::suspend() { if(!d->attached) { - cerr << "couldn't suspend, not attached" << endl; return false; } - if(d->suspended) + if(d->locked) { - cerr << "couldn't suspend, already suspended" << endl; return true; } - D_SHMCMD = CORE_SUSPEND; - if(!d->waitWhile(CORE_SUSPEND)) + //cerr << "suspend" << endl;// FIXME: throw + // FIXME: this should be controlled on the server side + // FIXME: IF server got CORE_RUN in this frame, interpret CORE_SUSPEND as CORE_STEP + // did we just resume a moment ago? + if(D_SHMCMD == CORE_RUN) { - cerr << "couldn't suspend, DF not responding to commands" << endl; - return false; + //fprintf(stderr,"%d invokes step\n",d->attachmentIdx); + // wait for the next window + if(!d->SetAndWait(CORE_STEP)) + { + throw Error::SHMLockingError("if(!d->SetAndWait(CORE_STEP))"); + } } - d->suspended = true; - return true; + else + { + //fprintf(stderr,"%d invokes suspend\n",d->attachmentIdx); + // lock now + if(!d->SetAndWait(CORE_SUSPEND)) + { + throw Error::SHMLockingError("if(!d->SetAndWait(CORE_SUSPEND))"); + } + } + //fprintf(stderr,"waiting for lock\n"); + // we wait for the server to give up our suspend lock (held by default) + if( WaitForSingleObject(d->DFCLSuspendMutex,INFINITE) == 0 ) + { + d->locked = true; + return true; + } + return false; } +// FIXME: needs a good think-through bool SHMProcess::asyncSuspend() { if(!d->attached) { return false; } - if(d->suspended) + if(d->locked) { return true; } - if(D_SHMCMD == CORE_SUSPENDED) + //cerr << "async suspend" << endl;// FIXME: throw + uint32_t cmd = D_SHMCMD; + if(cmd == CORE_SUSPENDED) { - d->suspended = true; - return true; + // we have to hold the lock to be really suspended + if( WaitForSingleObject(d->DFCLSuspendMutex,INFINITE) == 0 ) + { + d->locked = true; + return true; + } + return false; } else { - D_SHMCMD = CORE_SUSPEND; + // did we just resume a moment ago? + if(cmd == CORE_STEP) + { + return false; + } + else if(cmd == CORE_RUN) + { + D_SHMCMD = CORE_STEP; + } + else + { + D_SHMCMD = CORE_SUSPEND; + } return false; } } @@ -433,21 +542,26 @@ bool SHMProcess::forceresume() return resume(); } +// FIXME: wait for the server to advance a step! bool SHMProcess::resume() { if(!d->attached) - { - cerr << "couldn't resume because of no attachment" << endl; return false; - } - if(!d->suspended) - { - cerr << "couldn't resume because of not being suspended" << endl; + if(!d->locked) return true; + //cerr << "resume" << endl;// FIXME: throw + // unlock the suspend lock + if( ReleaseMutex(d->DFCLSuspendMutex) != 0) + { + d->locked = false; + if(d->SetAndWait(CORE_RUN)) // we have to make sure the server responds! + { + return true; + } + throw Error::SHMLockingError("if(d->SetAndWait(CORE_RUN))"); } - D_SHMCMD = CORE_RUNNING; - d->suspended = false; - return true; + throw Error::SHMLockingError("if( ReleaseMutex(d->DFCLSuspendMutex) != 0)"); + return false; } @@ -455,49 +569,66 @@ bool SHMProcess::attach() { if(g_pProcess != 0) { - cerr << "there's already a different process attached" << endl; + cerr << "there's already a process attached" << endl; return false; } - if(d->attached) + //cerr << "attach" << endl;// FIXME: throw + if(!d->GetLocks()) { - cerr << "already attached" << endl; + //cerr << "server is full or not really there!" << endl; return false; } + /* // check if DF is there if(!d->isValidSV()) { return false; // NOT } + */ + /* // try locking client mutex uint32_t result = WaitForSingleObject(d->DFCLMutex,0); if( result != WAIT_OBJECT_0 && result != WAIT_ABANDONED) { return false; // we couldn't lock it } - + */ + + /* + * Locate the segment. + */ char shmname [256]; - sprintf(shmname,"DFShm-%d",d->my_pid); - - // now try getting and attaching the shared memory + sprintf(shmname,"DFShm-%d",d->process_ID); HANDLE shmHandle = OpenFileMapping(FILE_MAP_ALL_ACCESS,false,shmname); if(!shmHandle) { - ReleaseMutex(d->DFCLMutex); + d->FreeLocks(); + //ReleaseMutex(d->DFCLMutex); return false; // we couldn't lock it } - // attempt to attach the opened mapping - d->my_shm = (char *) MapViewOfFile(shmHandle,FILE_MAP_ALL_ACCESS, 0,0, SHM_SIZE); - if(!d->my_shm) + /* + * Attach the segment + */ + d->shm_addr = (char *) MapViewOfFile(shmHandle,FILE_MAP_ALL_ACCESS, 0,0, SHM_SIZE); + if(!d->shm_addr) { CloseHandle(shmHandle); - ReleaseMutex(d->DFCLMutex); - return false; // we couldn't attach the mapping + //ReleaseMutex(d->DFCLMutex); + d->FreeLocks(); + return false; // we couldn't attach the mapping // FIXME: throw } - // we close the handle right here so we don't have to keep track of it + // we close the handle right here - it's not needed anymore CloseHandle(shmHandle); + d->attached = true; - suspend(); + if(!suspend()) + { + UnmapViewOfFile(d->shm_addr); + d->FreeLocks(); + //cerr << "unable to suspend" << endl;// FIXME: throw + return false; + } g_pProcess = this; return true; } @@ -508,27 +639,36 @@ bool SHMProcess::detach() { return false; } + //cerr << "detach" << endl;// FIXME: throw + if(d->locked) + { + resume(); + } + //cerr << "detach after resume" << endl;// FIXME: throw // detach segment - UnmapViewOfFile(d->my_shm); + UnmapViewOfFile(d->shm_addr); // release it for some other client - ReleaseMutex(d->DFCLMutex); // we keep the mutex handles + //ReleaseMutex(d->DFCLMutex); // we keep the mutex handles + d->FreeLocks(); d->attached = false; - d->suspended = false; + d->locked = false; + d->shm_addr = false; g_pProcess = 0; return true; } void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buffer) { + if(!d->locked) throw Error::SHMAccessDenied(); + // normal read under 1MB if(size <= SHM_BODY) { D_SHMHDR->address = src_address; D_SHMHDR->length = size; full_barrier - D_SHMCMD = CORE_DFPP_READ; - d->waitWhile(CORE_DFPP_READ); - memcpy (target_buffer, d->my_shm + SHM_HEADER,size); + d->SetAndWait(CORE_READ); + memcpy (target_buffer, D_SHMDATA(void),size); } // a big read, we pull data over the shm in iterations else @@ -541,9 +681,8 @@ void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buff D_SHMHDR->address = src_address; D_SHMHDR->length = to_read; full_barrier - D_SHMCMD = CORE_DFPP_READ; - d->waitWhile(CORE_DFPP_READ); - memcpy (target_buffer, d->my_shm + SHM_HEADER,size); + d->SetAndWait(CORE_READ); + memcpy (target_buffer, D_SHMDATA(void) ,size); // decrease size by bytes read size -= to_read; // move the cursors @@ -557,54 +696,60 @@ void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buff uint8_t SHMProcess::readByte (const uint32_t offset) { + if(!d->locked) throw Error::SHMAccessDenied(); + D_SHMHDR->address = offset; full_barrier - D_SHMCMD = CORE_READ_BYTE; - d->waitWhile(CORE_READ_BYTE); + d->SetAndWait(CORE_READ_BYTE); return D_SHMHDR->value; } void SHMProcess::readByte (const uint32_t offset, uint8_t &val ) { + if(!d->locked) throw Error::SHMAccessDenied(); + D_SHMHDR->address = offset; full_barrier - D_SHMCMD = CORE_READ_BYTE; - d->waitWhile(CORE_READ_BYTE); + d->SetAndWait(CORE_READ_BYTE); val = D_SHMHDR->value; } uint16_t SHMProcess::readWord (const uint32_t offset) { + if(!d->locked) throw Error::SHMAccessDenied(); + D_SHMHDR->address = offset; full_barrier - D_SHMCMD = CORE_READ_WORD; - d->waitWhile(CORE_READ_WORD); + d->SetAndWait(CORE_READ_WORD); return D_SHMHDR->value; } void SHMProcess::readWord (const uint32_t offset, uint16_t &val) { + if(!d->locked) throw Error::SHMAccessDenied(); + D_SHMHDR->address = offset; full_barrier - D_SHMCMD = CORE_READ_WORD; - d->waitWhile(CORE_READ_WORD); + d->SetAndWait(CORE_READ_WORD); val = D_SHMHDR->value; } uint32_t SHMProcess::readDWord (const uint32_t offset) { + if(!d->locked) throw Error::SHMAccessDenied(); + D_SHMHDR->address = offset; full_barrier - D_SHMCMD = CORE_READ_DWORD; - d->waitWhile(CORE_READ_DWORD); + d->SetAndWait(CORE_READ_DWORD); return D_SHMHDR->value; } void SHMProcess::readDWord (const uint32_t offset, uint32_t &val) { + if(!d->locked) throw Error::SHMAccessDenied(); + D_SHMHDR->address = offset; full_barrier - D_SHMCMD = CORE_READ_DWORD; - d->waitWhile(CORE_READ_DWORD); + d->SetAndWait(CORE_READ_DWORD); val = D_SHMHDR->value; } @@ -614,43 +759,47 @@ void SHMProcess::readDWord (const uint32_t offset, uint32_t &val) void SHMProcess::writeDWord (uint32_t offset, uint32_t data) { + if(!d->locked) throw Error::SHMAccessDenied(); + D_SHMHDR->address = offset; D_SHMHDR->value = data; full_barrier - D_SHMCMD = CORE_WRITE_DWORD; - d->waitWhile(CORE_WRITE_DWORD); + d->SetAndWait(CORE_WRITE_DWORD); } // using these is expensive. void SHMProcess::writeWord (uint32_t offset, uint16_t data) { + if(!d->locked) throw Error::SHMAccessDenied(); + D_SHMHDR->address = offset; D_SHMHDR->value = data; full_barrier - D_SHMCMD = CORE_WRITE_WORD; - d->waitWhile(CORE_WRITE_WORD); + d->SetAndWait(CORE_WRITE_WORD); } void SHMProcess::writeByte (uint32_t offset, uint8_t data) { + if(!d->locked) throw Error::SHMAccessDenied(); + D_SHMHDR->address = offset; D_SHMHDR->value = data; full_barrier - D_SHMCMD = CORE_WRITE_BYTE; - d->waitWhile(CORE_WRITE_BYTE); + d->SetAndWait(CORE_WRITE_BYTE); } void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buffer) { + if(!d->locked) throw Error::SHMAccessDenied(); + // normal write under 1MB if(size <= SHM_BODY) { D_SHMHDR->address = dst_address; D_SHMHDR->length = size; - memcpy(d->my_shm+SHM_HEADER,source_buffer, size); + memcpy(D_SHMDATA(void),source_buffer, size); full_barrier - D_SHMCMD = CORE_WRITE; - d->waitWhile(CORE_WRITE); + d->SetAndWait(CORE_WRITE); } // a big write, we push this over the shm in iterations else @@ -662,10 +811,9 @@ void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buf // write to_write bytes to dst_cursor D_SHMHDR->address = dst_address; D_SHMHDR->length = to_write; - memcpy(d->my_shm+SHM_HEADER,source_buffer, to_write); + memcpy(D_SHMDATA(void),source_buffer, to_write); full_barrier - D_SHMCMD = CORE_WRITE; - d->waitWhile(CORE_WRITE); + d->SetAndWait(CORE_WRITE); // decrease size by bytes written size -= to_write; // move the cursors @@ -680,6 +828,8 @@ void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buf // FIXME: butt-fugly const std::string SHMProcess::readCString (uint32_t offset) { + if(!d->locked) throw Error::SHMAccessDenied(); + std::string temp; char temp_c[256]; int counter = 0; @@ -697,6 +847,8 @@ const std::string SHMProcess::readCString (uint32_t offset) DfVector SHMProcess::readVector (uint32_t offset, uint32_t item_size) { + if(!d->locked) throw Error::SHMAccessDenied(); + /* MSVC++ vector is four pointers long ptr allocator @@ -715,38 +867,36 @@ DfVector SHMProcess::readVector (uint32_t offset, uint32_t item_size) const std::string SHMProcess::readSTLString(uint32_t offset) { - //offset -= 4; //msvc std::string pointers are 8 bytes ahead of their data, not 4 + if(!d->locked) throw Error::SHMAccessDenied(); + D_SHMHDR->address = offset; full_barrier - D_SHMCMD = CORE_READ_STL_STRING; - d->waitWhile(CORE_READ_STL_STRING); - int length = D_SHMHDR->value; -// char temp_c[256]; -// strncpy(temp_c, d->my_shm+SHM_HEADER,length+1); // length + 1 for the null terminator - return(string(d->my_shm+SHM_HEADER)); + d->SetAndWait(CORE_READ_STL_STRING); + return(string( D_SHMDATA(char) )); } size_t SHMProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity) { - //offset -= 4; //msvc std::string pointers are 8 bytes ahead of their data, not 4 + if(!d->locked) throw Error::SHMAccessDenied(); + D_SHMHDR->address = offset; full_barrier - D_SHMCMD = CORE_READ_STL_STRING; - d->waitWhile(CORE_READ_STL_STRING); + d->SetAndWait(CORE_READ_STL_STRING); size_t length = D_SHMHDR->value; - size_t real = min(length, bufcapacity - 1); - strncpy(buffer, d->my_shm+SHM_HEADER,real); // length + 1 for the null terminator - buffer[real] = 0; - return real; + size_t fit = min(bufcapacity - 1, length); + strncpy(buffer,D_SHMDATA(char),fit); + buffer[fit] = 0; + return fit; } void SHMProcess::writeSTLString(const uint32_t address, const std::string writeString) { - D_SHMHDR->address = address/*-4*/; - strncpy(d->my_shm+SHM_HEADER,writeString.c_str(),writeString.length()+1); // length + 1 for the null terminator + if(!d->locked) throw Error::SHMAccessDenied(); + + D_SHMHDR->address = address; + strncpy(D_SHMDATA(char),writeString.c_str(),writeString.length()+1); // length + 1 for the null terminator full_barrier - D_SHMCMD = CORE_WRITE_STL_STRING; - d->waitWhile(CORE_WRITE_STL_STRING); + d->SetAndWait(CORE_WRITE_STL_STRING); } string SHMProcess::readClassName (uint32_t vptr) @@ -758,21 +908,51 @@ string SHMProcess::readClassName (uint32_t vptr) return raw; } -// get module index by name and version. bool 1 = error +// get module index by name and version. bool 0 = error bool SHMProcess::getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) { - modulelookup * payload = (modulelookup *) (d->my_shm + SHM_HEADER); + if(!d->locked) throw Error::SHMAccessDenied(); + + modulelookup * payload = D_SHMDATA(modulelookup); payload->version = version; - strcpy(payload->name,name); - full_barrier - D_SHMCMD = CORE_ACQUIRE_MODULE; - d->waitWhile(CORE_ACQUIRE_MODULE); - if(D_SHMHDR->error) return false; + + strncpy(payload->name,name,255); + payload->name[255] = 0; + + if(!SetAndWait(CORE_ACQUIRE_MODULE)) + { + return false; // FIXME: throw a fatal exception instead + } + if(D_SHMHDR->error) + { + return false; + } + //fprintf(stderr,"%s v%d : %d\n", name, version, D_SHMHDR->value); OUTPUT = D_SHMHDR->value; return true; } char * SHMProcess::getSHMStart (void) { - return d->my_shm; + if(!d->locked) throw Error::SHMAccessDenied(); + return /*d->shm_addr_with_cl_idx*/ d->shm_addr; +} + +bool SHMProcess::Private::Aux_Core_Attach(bool & versionOK, uint32_t & PID) +{ + if(!locked) throw Error::SHMAccessDenied(); + + SHMDATA(coreattach)->cl_affinity = OS_getAffinity(); + if(!SetAndWait(CORE_ATTACH)) return false; + /* + cerr <<"CORE_VERSION" << CORE_VERSION << endl; + cerr <<"server CORE_VERSION" << SHMDATA(coreattach)->sv_version << endl; + */ + versionOK =( SHMDATA(coreattach)->sv_version == CORE_VERSION ); + PID = SHMDATA(coreattach)->sv_PID; + useYield = SHMDATA(coreattach)->sv_useYield; + #ifdef DEBUG + if(useYield) cerr << "Using Yield!" << endl; + #endif + return true; } \ No newline at end of file diff --git a/library/DFProcess.h b/library/DFProcess.h index 878e68d37..3a023ca2d 100644 --- a/library/DFProcess.h +++ b/library/DFProcess.h @@ -123,8 +123,14 @@ namespace DFHack virtual bool getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) = 0; // get the SHM start if available virtual char * getSHMStart (void) = 0; - // wait for a SHM state. returns 0 without the SHM + // set a SHM command and wait for a response, return 0 on error or throw exception + virtual bool SetAndWait (uint32_t state) = 0; + /* + // wait while SHM command == state. returns 0 without the SHM virtual bool waitWhile (uint32_t state) = 0; + // set SHM command. + virtual void setCmd (uint32_t newstate) = 0; + */ }; class DFHACK_EXPORT NormalProcess : virtual public Process @@ -181,8 +187,14 @@ namespace DFHack bool getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) {return false;}; // get the SHM start if available char * getSHMStart (void){return 0;}; + // set a SHM command and wait for a response + bool SetAndWait (uint32_t state){return false;}; + /* // wait for a SHM state. returns 0 without the SHM bool waitWhile (uint32_t state){return false;}; + // set SHM command. + void setCmd (uint32_t newstate){}; + */ }; class DFHACK_EXPORT SHMProcess : virtual public Process @@ -240,8 +252,13 @@ namespace DFHack bool getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT); // get the SHM start if available char * getSHMStart (void); + bool SetAndWait (uint32_t state); + /* // wait for a SHM state. returns 0 without the SHM bool waitWhile (uint32_t state); + // set SHM command. + void setCmd (uint32_t newstate); + */ }; #ifdef LINUX_BUILD @@ -299,8 +316,13 @@ namespace DFHack bool getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) {return false;}; // get the SHM start if available char * getSHMStart (void){return 0;}; + bool SetAndWait (uint32_t state){return false;}; + /* // wait for a SHM state. returns 0 without the SHM bool waitWhile (uint32_t state){return false;}; + // set SHM command. + void setCmd (uint32_t newstate){}; + */ }; #endif } diff --git a/precompiled/linux/libdfconnect.so b/precompiled/linux/libdfconnect.so index 6db5d6d3b..7e76d2c37 100755 Binary files a/precompiled/linux/libdfconnect.so and b/precompiled/linux/libdfconnect.so differ diff --git a/precompiled/windows/SDL.dll b/precompiled/windows/SDL.dll index 1bc32d954..c03b5e0df 100644 Binary files a/precompiled/windows/SDL.dll and b/precompiled/windows/SDL.dll differ diff --git a/shmserver/mod-core.cpp b/shmserver/mod-core.cpp index cd3137d62..63622731d 100644 --- a/shmserver/mod-core.cpp +++ b/shmserver/mod-core.cpp @@ -41,19 +41,22 @@ distribution. std::vector module_registry; -// various crud +// shared by shms_OS extern int errorstate; extern char *shm; extern int shmid; + +// file-globals bool useYield = 0; +int currentClient = -1; #define SHMHDR ((shm_core_hdr *)shm) -#define SHMCMD ((shm_cmd *)shm)->pingpong +#define SHMCMD ((uint32_t *)shm )[currentClient] #define SHMDATA(type) ((type *)(shm + SHM_HEADER)) void ReadRaw (void * data) { - memcpy(shm + SHM_HEADER, (void *) SHMHDR->address,SHMHDR->length); + memcpy(SHMDATA(void), (void *) SHMHDR->address,SHMHDR->length); } void ReadDWord (void * data) @@ -73,7 +76,7 @@ void ReadByte (void * data) void WriteRaw (void * data) { - memcpy((void *)SHMHDR->address, shm + SHM_HEADER,SHMHDR->length); + memcpy((void *)SHMHDR->address, SHMDATA(void),SHMHDR->length); } void WriteDWord (void * data) @@ -97,14 +100,14 @@ void ReadSTLString (void * data) unsigned int l = myStringPtr->length(); SHMHDR->value = l; // FIXME: there doesn't have to be a null terminator! - strncpy(shm+SHM_HEADER,myStringPtr->c_str(),l+1); + strncpy( SHMDATA(char),myStringPtr->c_str(),l+1); } void WriteSTLString (void * data) { std::string * myStringPtr = (std::string *) SHMHDR->address; // here we DO expect a 0 terminator - myStringPtr->assign((const char *) (shm + SHM_HEADER)); + myStringPtr->assign( SHMDATA(const char) ); } // MIT HAKMEM bitcount @@ -133,7 +136,7 @@ void CoreAttach (void * data) void FindModule (void * data) { bool found = false; - modulelookup * payload = (modulelookup *) (shm + SHM_HEADER); + modulelookup * payload = SHMDATA(modulelookup); std::string test = payload->name; uint32_t version = payload->version; for(unsigned int i = 0; i < module_registry.size();i++) @@ -175,6 +178,17 @@ void FindCommand (void * data) SHMHDR->error = true; } +void ReleaseSuspendLock( void * data ) +{ + OS_releaseSuspendLock(currentClient); +} + +void AcquireSuspendLock( void * data ) +{ + OS_lockSuspendLock(currentClient); +} + + DFPP_module InitCore(void) { DFPP_module core; @@ -185,7 +199,10 @@ DFPP_module InitCore(void) core.reserve(NUM_CORE_CMDS); // basic states core.set_command(CORE_RUNNING, CANCELLATION, "Running"); - core.set_command(CORE_SUSPEND, CLIENT_WAIT, "Suspend", 0 , CORE_SUSPENDED); + //core.set_command(CORE_RUN, FUNCTION, "Run!",AcquireSuspendLock,CORE_RUNNING); + core.set_command(CORE_RUN, CANCELLATION, "Run!",0,CORE_RUNNING); + core.set_command(CORE_STEP, CANCELLATION, "Suspend on next step",0,CORE_SUSPEND);// set command to CORE_SUSPEND, check next client + core.set_command(CORE_SUSPEND, FUNCTION, "Suspend", ReleaseSuspendLock , CORE_SUSPENDED); core.set_command(CORE_SUSPENDED, CLIENT_WAIT, "Suspended"); core.set_command(CORE_ERROR, CANCELLATION, "Error"); @@ -195,7 +212,7 @@ DFPP_module InitCore(void) core.set_command(CORE_ACQUIRE_COMMAND, FUNCTION, "Command lookup", FindCommand, CORE_SUSPENDED); // raw reads - core.set_command(CORE_DFPP_READ, FUNCTION,"Raw read",ReadRaw, CORE_SUSPENDED); + core.set_command(CORE_READ, FUNCTION,"Raw read",ReadRaw, CORE_SUSPENDED); core.set_command(CORE_READ_DWORD, FUNCTION,"Read DWORD",ReadDWord, CORE_SUSPENDED); core.set_command(CORE_READ_WORD, FUNCTION,"Read WORD",ReadWord, CORE_SUSPENDED); core.set_command(CORE_READ_BYTE, FUNCTION,"Read BYTE",ReadByte, CORE_SUSPENDED); @@ -233,60 +250,90 @@ void KillModules (void) void SHM_Act (void) { + volatile uint32_t atomic = 0; if(errorstate) { return; } - uint32_t numwaits = 0; - check_again: // goto target!!! - if(numwaits == 10000) + //static uint oldcl = 88; + for(currentClient = 0; currentClient < SHM_MAX_CLIENTS;currentClient++) { - // this tests if there's a process on the other side - if(isValidSHM()) + // set the offset for the shared memory used for the client + uint32_t numwaits = 0; + check_again: // goto target!!! + if(numwaits == 10000) { - numwaits = 0; + // this tests if there's a process on the other side + if(isValidSHM(currentClient)) + { + numwaits = 0; + } + else + { + full_barrier + SHMCMD = CORE_RUNNING; + fprintf(stderr,"dfhack: Broke out of loop, other process disappeared.\n"); + } } - else + full_barrier // I don't want the compiler to reorder my code. + + + //fprintf(stderr,"%d: %x %x\n",currentClient, (uint) SHMHDR, (uint) &(SHMHDR->cmd[currentClient])); + + // this is very important! copying two words separately from the command variable leads to inconsistency. + // Always copy the thing in one go. + // Also, this whole SHM thing probably only works on intel processors + atomic = SHMCMD; + full_barrier + + DFPP_module & mod = module_registry[ ((shm_cmd)atomic).parts.module ]; + DFPP_command & cmd = mod.commands[ ((shm_cmd)atomic).parts.command ]; + /* + if(atomic == CORE_RUNNING) { - full_barrier - SHMCMD = CORE_RUNNING; - fprintf(stderr,"dfhack: Broke out of loop, other process disappeared.\n"); + // we are running again for this process + // reaquire the suspend lock + OS_lockSuspendLock(currentClient); + continue; } - } - - // this is very important! copying two words separately from the command variable leads to inconsistency. - // Always copy the thing in one go. - // Also, this whole SHM thing probably only works on intel processors - - volatile shm_cmd atomic = SHMHDR->cmd; - full_barrier - DFPP_module & mod = module_registry[atomic.parts.module]; - DFPP_command & cmd = mod.commands[atomic.parts.command]; - full_barrier - /* - fprintf(stderr, "Called %x\0", cmd._function); - fprintf(stderr, "Client invoked %d:%d = ",atomic.parts.module,atomic.parts.command); - fprintf(stderr, "%s\n",cmd.name.c_str()); - */ - full_barrier - if(cmd._function) - { - cmd._function(mod.modulestate); - } full_barrier - if(cmd.nextState != -1) - { - SHMCMD = cmd.nextState; - } + */ + if(cmd._function) + { + cmd._function(mod.modulestate); + } full_barrier - if(cmd.type != CANCELLATION) - { - if(useYield) + + if(cmd.nextState != -1) + { + /* + char text [512]; + char text2 [512]; + sprintf (text,"Client %d invoked %d:%d = %x = %s\n",currentClient,((shm_cmd)atomic).parts.module,((shm_cmd)atomic).parts.command, cmd._function,cmd.name.c_str()); + sprintf(text2, "Server set %d\n",cmd.nextState); + */ + // FIXME: WHAT HAPPENS WHEN A 'NEXTSTATE' IS FROM A DIFFERENT MODULE THAN 'CORE'? Yeah. It doesn't work. + SHMCMD = cmd.nextState; + //MessageBox(0,text,text2, MB_OK); + + //fflush(stderr); // make sure this finds its way to the terminal! + + } + full_barrier + + if(cmd.type != CANCELLATION) { - SCHED_YIELD + if(useYield) + { + SCHED_YIELD + } + numwaits ++; // watchdog timeout + goto check_again; } - numwaits ++; // watchdog timeout - goto check_again; + full_barrier + + // we are running again for this process + // reaquire the suspend lock + OS_lockSuspendLock(currentClient); } } - diff --git a/shmserver/mod-core.h b/shmserver/mod-core.h index 37717cb6f..ad0765cb1 100644 --- a/shmserver/mod-core.h +++ b/shmserver/mod-core.h @@ -26,11 +26,11 @@ distribution. #define SHMS_CORE_H // increment on every core change -#define CORE_VERSION 7 +#define CORE_VERSION 8 typedef struct { - shm_cmd cmd; + shm_cmd cmd[SHM_MAX_CLIENTS]; // MANDATORY! uint32_t address; uint32_t value; uint32_t length; @@ -62,6 +62,8 @@ enum CORE_COMMAND { // basic states CORE_RUNNING = 0, // no command, normal server execution + CORE_RUN, // sent by the client to restart the server execution + CORE_STEP, // client suspend sets step CORE_SUSPEND, // client notifies server to wait for commands (server is stalled in busy wait) CORE_SUSPENDED, // response to WAIT, server is stalled in busy wait CORE_ERROR, // there was a server error @@ -72,7 +74,7 @@ enum CORE_COMMAND CORE_ACQUIRE_COMMAND, // get module::command callsign by module name, command name and module version // raw reads - CORE_DFPP_READ, // cl -> sv, read some data + CORE_READ, // cl -> sv, read some data CORE_READ_DWORD, // cl -> sv, read a dword CORE_READ_WORD, // cl -> sv, read a word CORE_READ_BYTE, // cl -> sv, read a byte diff --git a/shmserver/mod-maps.cpp b/shmserver/mod-maps.cpp index 54fe1cb57..f10629cdc 100644 --- a/shmserver/mod-maps.cpp +++ b/shmserver/mod-maps.cpp @@ -19,7 +19,7 @@ extern char *shm; #define SHMHDR ((shm_maps_hdr *)shm) #define SHMCMD ((shm_cmd *)shm)->pingpong -#define SHMDATA ((char *)(shm + SHM_HEADER)) +#define SHMDATA(type) ((type *)(shm + SHM_HEADER)) void NullCommand (void* data) { @@ -28,7 +28,7 @@ void NullCommand (void* data) void InitOffsets (void* data) { maps_modulestate * state = (maps_modulestate *) data; - memcpy((void *) &(state->offsets), SHMDATA, sizeof(maps_offsets)); + memcpy((void *) &(state->offsets), SHMDATA(void), sizeof(maps_offsets)); ((maps_modulestate *) data)->inited = true; } @@ -53,8 +53,6 @@ struct mblock uint32_t * ptr_to_dirty; }; -#define SHMBLOCK ((mapblock40d *)(shm + SHM_HEADER)) - inline void ReadBlockByAddress (void * data) { maps_modulestate * state = (maps_modulestate *) data; @@ -62,13 +60,13 @@ inline void ReadBlockByAddress (void * data) mblock * block = (mblock *) SHMHDR->address; if(block) { - memcpy(&(SHMBLOCK->tiletypes), ((char *) block) + offsets.tile_type_offset, sizeof(SHMBLOCK->tiletypes)); - memcpy(&(SHMBLOCK->designaton), ((char *) block) + offsets.designation_offset, sizeof(SHMBLOCK->designaton)); - memcpy(&(SHMBLOCK->occupancy), ((char *) block) + offsets.occupancy_offset, sizeof(SHMBLOCK->occupancy)); - memcpy(&(SHMBLOCK->biome_indices), ((char *) block) + offsets.biome_stuffs, sizeof(SHMBLOCK->biome_indices)); - SHMBLOCK->dirty_dword = *block->ptr_to_dirty; + memcpy(&(SHMDATA(mapblock40d)->tiletypes), ((char *) block) + offsets.tile_type_offset, sizeof(SHMDATA(mapblock40d)->tiletypes)); + memcpy(&(SHMDATA(mapblock40d)->designaton), ((char *) block) + offsets.designation_offset, sizeof(SHMDATA(mapblock40d)->designaton)); + memcpy(&(SHMDATA(mapblock40d)->occupancy), ((char *) block) + offsets.occupancy_offset, sizeof(SHMDATA(mapblock40d)->occupancy)); + memcpy(&(SHMDATA(mapblock40d)->biome_indices), ((char *) block) + offsets.biome_stuffs, sizeof(SHMDATA(mapblock40d)->biome_indices)); + SHMDATA(mapblock40d)->dirty_dword = *block->ptr_to_dirty; - SHMBLOCK->origin = (uint32_t)block; + SHMDATA(mapblock40d)->origin = (uint32_t)block; SHMHDR->error = false; } else diff --git a/shmserver/mod-maps.h b/shmserver/mod-maps.h index 5ff5f0f2e..e4c4ff664 100644 --- a/shmserver/mod-maps.h +++ b/shmserver/mod-maps.h @@ -71,7 +71,7 @@ typedef struct typedef struct { - shm_cmd cmd; + shm_cmd cmd[SHM_MAX_CLIENTS]; // MANDATORY! uint32_t x; uint32_t y; uint32_t z; diff --git a/shmserver/shms-linux.cpp b/shmserver/shms-linux.cpp index e79fef3bb..aa157c1be 100644 --- a/shmserver/shms-linux.cpp +++ b/shmserver/shms-linux.cpp @@ -51,38 +51,27 @@ char *shm = 0; int shmid = 0; bool inited = 0; -int fd_svlock = 0; -int fd_cllock = 0; - +int fd_svlock = -1; +int fd_cllock[SHM_MAX_CLIENTS]; +int fd_clSlock[SHM_MAX_CLIENTS]; +int held_clSlock[SHM_MAX_CLIENTS]; +int numheld = SHM_MAX_CLIENTS; /******************************************************************************* * SHM part starts here * *******************************************************************************/ -/* -// FIXME: add error checking? -bool isValidSHM() -{ - shmid_ds descriptor; - shmctl(shmid, IPC_STAT, &descriptor); - //fprintf(stderr,"ID %d, attached: %d\n",shmid, descriptor.shm_nattch); - return (descriptor.shm_nattch == 2); -} -*/ - // is the other side still there? -bool isValidSHM() +bool isValidSHM(int which) { - // try if CL lock file is free - int result = lockf(fd_cllock,F_TEST,0); - /* - F_TEST: Test the lock: - return 0 if the specified section is unlocked or locked by this process; - return -1, set errno to EAGAIN (EACCES on some other systems), if another process holds a lock. - */ - - // if nobody holds the lock, the SHM isn't valid. file locks are unlocked when the owner closes or crashes. - if(result == 0) return false; - + // if we get the client lock here, the client process failed and we need to reclaim our suspend lock + if(lockf(fd_cllock[which],F_TLOCK,0) == 0) + { + // we get back our suspend lock from the cold, dead hands of the former client :P + OS_lockSuspendLock(which); + // free the client lock again + lockf(fd_cllock[which],F_ULOCK,0); + return false; + } return true; } @@ -100,6 +89,45 @@ uint32_t OS_getAffinity() return affinity; } +void OS_lockSuspendLock(int which) +{ + if(numheld == SHM_MAX_CLIENTS) + return; + // lock not held by server and can be picked up. OK. + if(held_clSlock[which] == 0 && lockf(fd_clSlock[which],F_LOCK,0) == 0) + { + held_clSlock[which] = 1; + numheld++; + } + // lock couldn't be picked up! + else if (held_clSlock[which] == 0) + { + fprintf(stderr,"lock %d failed to lock\n", which); + } +} + +void OS_releaseSuspendLock(int which) +{ + /* + if(which >=0 && which < SHM_MAX_CLIENTS) + return; + */ + if(numheld != SHM_MAX_CLIENTS) + { + fprintf(stderr,"TOTAL FAILURE OF LOCKING SYSTEM\n"); + return; + } + // lock hel by server and can be released -> OK + if(held_clSlock[which] == 1 && lockf(fd_clSlock[which],F_ULOCK,0) == 0) + { + numheld--; + held_clSlock[which] = 0; + } + // locked and not can't be released? FAIL! + else if (held_clSlock[which] == 1) + fprintf(stderr,"lock %d failed to unlock\n", which); +} + void SHM_Init ( void ) { // check that we do this only once per process @@ -111,7 +139,6 @@ void SHM_Init ( void ) inited = true; char name[256]; char name2[256]; - // make folder structure for our lock files sprintf(name, "/tmp/DFHack/%d",OS_getPID()); mode_t createmode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH; @@ -123,9 +150,17 @@ void SHM_Init ( void ) fd_svlock = open(name2,O_WRONLY | O_CREAT, createmode); lockf( fd_svlock, F_LOCK, 0 ); - // create the client lock file - sprintf(name2, "%s/CLlock",name); - fd_cllock = open(name2,O_WRONLY | O_CREAT, createmode); + for(int i = 0; i < SHM_MAX_CLIENTS; i++) + { + // create the client lock file + sprintf(name2, "%s/CLlock%d",name,i); + fd_cllock[i] = open(name2,O_WRONLY | O_CREAT, createmode); + // get and lock the suspend locks + sprintf(name2, "%s/CLSlock%d",name,i); + fd_clSlock[i] = open(name2,O_WRONLY | O_CREAT, createmode); + lockf(fd_clSlock[i],F_LOCK,0); + held_clSlock[i] = 1; + } // name for the segment, an accident waiting to happen key_t key = SHM_KEY + OS_getPID(); @@ -159,7 +194,10 @@ void SHM_Init ( void ) } full_barrier // make sure we don't stall or do crazy stuff - ((shm_cmd *)shm)->pingpong = CORE_RUNNING; + for(int i = 0; i < SHM_MAX_CLIENTS;i++) + { + ((uint32_t *)shm)[i] = CORE_RUNNING; + } InitModules(); } @@ -177,21 +215,29 @@ void SHM_Destroy ( void ) } shmctl(shmid,IPC_RMID,NULL); - // unlock and close server lock, close client lock - lockf(fd_svlock,F_ULOCK,0); - close(fd_svlock); - close(fd_cllock); - fd_svlock = 0; - fd_cllock = 0; - - // destroy lock files char name[256]; char name2[256]; sprintf(name, "/tmp/DFHack/%d",OS_getPID()); + + // unlock and close server lock, close client lock, destroy files + lockf(fd_svlock,F_ULOCK,0); + for(int i = 0; i < SHM_MAX_CLIENTS; i++) + { + close(fd_cllock[i]); + fd_cllock[i] = 0; + close(fd_clSlock[i]); + fd_clSlock[i] = 0; + held_clSlock[i] = 0; + sprintf(name2, "%s/CLlock%d",name,i); + unlink(name2); + sprintf(name2, "%s/CLSlock%d",name,i); + unlink(name2); + } + close(fd_svlock); + fd_svlock = 0; sprintf(name2, "%s/SVlock",name); unlink(name2); - sprintf(name2, "%s/CLlock",name); - unlink(name2); + // remove the PID folder rmdir(name); fprintf(stderr,"dfhack: destroyed shared segment.\n"); inited = false; @@ -213,7 +259,7 @@ DFhackCExport void SDL_GL_SwapBuffers(void) { if(_SDL_GL_SwapBuffers) { - if(!errorstate && ((shm_cmd *)shm)->pingpong != CORE_RUNNING) + if(!errorstate) { SHM_Act(); } @@ -227,7 +273,7 @@ DFhackCExport int SDL_Flip(void * some_ptr) { if(_SDL_Flip) { - if(!errorstate && ((shm_cmd *)shm)->pingpong != CORE_RUNNING) + if(!errorstate) { SHM_Act(); } @@ -285,7 +331,7 @@ DFhackCExport int refresh (void) { if(_refresh) { - if(!errorstate && ((shm_cmd *)shm)->pingpong != CORE_RUNNING) + if(!errorstate) { SHM_Act(); } diff --git a/shmserver/shms-windows.cpp b/shmserver/shms-windows.cpp index 9581ad68f..0762e8599 100644 --- a/shmserver/shms-windows.cpp +++ b/shmserver/shms-windows.cpp @@ -44,8 +44,65 @@ char *shm = 0; int shmid = 0; bool inited = 0; HANDLE shmHandle = 0; + HANDLE DFSVMutex = 0; -HANDLE DFCLMutex = 0; +HANDLE DFCLMutex[SHM_MAX_CLIENTS]; +HANDLE DFCLSuspendMutex[SHM_MAX_CLIENTS]; +int held_DFCLSuspendMutex[SHM_MAX_CLIENTS]; +int numheld = SHM_MAX_CLIENTS; + + +void OS_lockSuspendLock(int which) +{ + if(numheld == SHM_MAX_CLIENTS) + return; + // lock not held by server and can be picked up. OK. + if(held_DFCLSuspendMutex[which] == 0) + { + uint32_t state = WaitForSingleObject(DFCLSuspendMutex[which],INFINITE); + if(state == WAIT_ABANDONED || state == WAIT_OBJECT_0) + { + held_DFCLSuspendMutex[which] = 1; + numheld++; + return; + } + // lock couldn't be picked up! + errorstate = 1; + MessageBox(0,"Suspend lock locking failed. Further communication disabled!","Error", MB_OK); + return; + } + errorstate = 1; + MessageBox(0,"Server tried to lock already locked suspend lock? Further communication disabled!","Error", MB_OK); + return; +} + +void OS_releaseSuspendLock(int which) +{ + /* + if(which >=0 && which < SHM_MAX_CLIENTS) + return; + */ + if(numheld != SHM_MAX_CLIENTS) + { + MessageBox(0,"Locking system failure. Further communication disabled!","Error", MB_OK); + errorstate = 1; + return; + } + // lock hel by server and can be released -> OK + if(held_DFCLSuspendMutex[which] == 1 && ReleaseMutex(DFCLSuspendMutex[which])) + { + numheld--; + held_DFCLSuspendMutex[which] = 0; + } + // locked and not can't be released? FAIL! + else if (held_DFCLSuspendMutex[which] == 1) + { + MessageBox(0,"Suspend lock failed to unlock. Further communication disabled!","Error", MB_OK); + return; + } +} + + void SHM_Init ( void ) { // check that we do this only once per process @@ -56,66 +113,48 @@ void SHM_Init ( void ) } inited = true; - char svmutexname [256]; - sprintf(svmutexname,"DFSVMutex-%d",OS_getPID()); + char clmutexname [256]; - sprintf(clmutexname,"DFCLMutex-%d",OS_getPID()); + char clsmutexname [256]; char shmname [256]; sprintf(shmname,"DFShm-%d",OS_getPID()); - // create or open mutexes + // create a locked server mutex + char svmutexname [256]; + sprintf(svmutexname,"DFSVMutex-%d",OS_getPID()); DFSVMutex = CreateMutex( 0, 1, svmutexname); if(DFSVMutex == 0) { - DFSVMutex = OpenMutex(SYNCHRONIZE,false, svmutexname); - if(DFSVMutex == 0) - { - errorstate = 1; - return; - } + MessageBox(0,"Server mutex creation failed. Further communication disabled!","Error", MB_OK); + errorstate = 1; + return; } - DFCLMutex = CreateMutex( 0, 0, clmutexname); - if(DFCLMutex == 0) + // the mutex already existed. we don't want to know. + if(GetLastError() == ERROR_ALREADY_EXISTS) { - DFCLMutex = OpenMutex(SYNCHRONIZE,false, clmutexname); - if(DFCLMutex == 0) - { - CloseHandle(DFSVMutex); - errorstate = 1; - return; - } + MessageBox(0,"Server mutex already existed. Further communication disabled!","Error", MB_OK); + errorstate = 1; + return; } - // try locking server mutex - uint32_t result; - result = WaitForSingleObject(DFSVMutex,0); - switch (result) + // create client and suspend mutexes + for(int i = 0; i < SHM_MAX_CLIENTS; i++) { - case WAIT_ABANDONED: + sprintf(clmutexname,"DFCLMutex-%d-%d",OS_getPID(),i); + sprintf(clsmutexname,"DFCLSuspendMutex-%d-%d",OS_getPID(),i); + + DFCLMutex[i] = CreateMutex( 0, 0, clmutexname); // client mutex, not held + DFCLSuspendMutex[i] = CreateMutex( 0, 1, clsmutexname); // suspend mutexes held on start + held_DFCLSuspendMutex[i] = 1; + + if(DFCLMutex[i] == 0 || DFCLSuspendMutex[i] == 0 || GetLastError() == ERROR_ALREADY_EXISTS) { - // picked up after a crashed DF process - // do some sanity checks on client attached - // otherwise the same thing as WAIT_OBJECT_0 - break; - } - case WAIT_OBJECT_0: - { - // all right, we have the mutex and are the one and only DF server - break; - } - case WAIT_TIMEOUT: - case WAIT_FAILED: - default: - { - // error, bail + MessageBox(0,"Client mutex creation failed. Close all tools before starting DF.","Error", MB_OK); errorstate = 1; - MessageBox(0,"Could not aquire mutex","Error", MB_OK); - CloseHandle(DFSVMutex); - CloseHandle(DFCLMutex); return; } } - + // create virtual memory mapping shmHandle = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,SHM_SIZE,shmname); // if can't create or already exists -> nothing happens @@ -123,35 +162,32 @@ void SHM_Init ( void ) { MessageBox(0,"SHM bridge already in use","Error", MB_OK); errorstate = 1; - ReleaseMutex(DFSVMutex); - CloseHandle(DFSVMutex); - CloseHandle(DFCLMutex); return; } if(!shmHandle) { MessageBox(0,"Couldn't create SHM bridge","Error", MB_OK); errorstate = 1; - ReleaseMutex(DFSVMutex); - CloseHandle(DFSVMutex); - CloseHandle(DFCLMutex); return; } // attempt to attach the created mapping shm = (char *) MapViewOfFile(shmHandle,FILE_MAP_ALL_ACCESS, 0,0, SHM_SIZE); if(shm) { - ((shm_cmd *)shm)->pingpong = CORE_RUNNING; + // make sure we don't stall or do crazy stuff + for(int i = 0; i < SHM_MAX_CLIENTS;i++) + { + ((uint32_t *)shm)[i] = CORE_RUNNING; + } + // init modules :) + InitModules(); } else { MessageBox(0,"Couldn't attach SHM bridge","Error", MB_OK); errorstate = 1; - ReleaseMutex(DFSVMutex); - CloseHandle(DFSVMutex); - CloseHandle(DFCLMutex); + return; } - InitModules(); } void SHM_Destroy ( void ) @@ -159,9 +195,13 @@ void SHM_Destroy ( void ) if(errorstate) return; KillModules(); - ReleaseMutex(DFSVMutex); + // get rid of all the locks CloseHandle(DFSVMutex); - CloseHandle(DFCLMutex); + for(int i=0; i < SHM_MAX_CLIENTS; i++) + { + CloseHandle(DFCLSuspendMutex[i]); + CloseHandle(DFCLMutex[i]); + } } uint32_t OS_getPID() @@ -181,17 +221,18 @@ uint32_t OS_getAffinity() // is the other side still there? -bool isValidSHM() +bool isValidSHM(int which) { - // try if CL mutex is free - uint32_t result = WaitForSingleObject(DFCLMutex,0); + // try if CL mutex is free (by locking client mutex) + uint32_t result = WaitForSingleObject(DFCLMutex[which],0); switch (result) { case WAIT_ABANDONED: case WAIT_OBJECT_0: { - ReleaseMutex(DFCLMutex); + OS_lockSuspendLock(which); + ReleaseMutex(DFCLMutex[which]); return false; } case WAIT_TIMEOUT: diff --git a/shmserver/shms.h b/shmserver/shms.h index 174614c8e..48fa2af5d 100644 --- a/shmserver/shms.h +++ b/shmserver/shms.h @@ -2,10 +2,12 @@ #define DFCONNECT_H #define SHM_KEY 123466 +#define SHM_MAX_CLIENTS 4 #define SHM_HEADER 1024 // 1kB reserved for a header -#define SHM_BODY 1024*1024 // 1MB reserved for bulk data transfer +#define SHM_BODY 1024*1024 // 4MB reserved for bulk data transfer #define SHM_SIZE SHM_HEADER+SHM_BODY - +//#define SHM_ALL_CLIENTS SHM_MAX_CLIENTS*(SHM_SIZE) +//#define SHM_CL(client_idx) client_idx*(SHM_SIZE) // FIXME: add YIELD for linux, add single-core and multi-core compile targets for optimal speed #ifdef LINUX_BUILD @@ -79,26 +81,28 @@ struct DFPP_module void * modulestate; }; -typedef union +union shm_cmd { struct - { - volatile uint16_t command; - volatile uint16_t module; - } parts; + { + volatile uint16_t command; + volatile uint16_t module; + } parts; volatile uint32_t pingpong; - inline void set(uint16_t module, uint16_t command) + shm_cmd(volatile uint32_t z) { - pingpong = module + command << 16; + pingpong = z; } -} shm_cmd; +}; void SHM_Act (void); void InitModules (void); void KillModules (void); -bool isValidSHM(); +bool isValidSHM(int current); uint32_t OS_getPID(); DFPP_module InitMaps(void); uint32_t OS_getAffinity(); // limited to 32 processors. Silly, eh? +void OS_releaseSuspendLock(int currentClient); +void OS_lockSuspendLock(int currentClient); #endif