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 6e8f76cf5..f1f9b22e3 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..f8a83275d 100644 --- a/library/DFProcess-linux-SHM.cpp +++ b/library/DFProcess-linux-SHM.cpp @@ -43,27 +43,35 @@ 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 locked; bool attached; bool suspended; @@ -73,60 +81,36 @@ class SHMProcess::Private bool validate(char* exe_file, uint32_t pid, 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(!locked) 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); + attached = suspended = identified = false; // we aren't the current process anymore g_pProcess = NULL; FreeLocks(); @@ -145,12 +129,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,11 +141,19 @@ 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); } +/* +// set SHM command. +void SHMProcess::setCmd (uint32_t newstate) +{ + if(d->attached && d->suspended) + D_SHMCMD = newstate; +}; +*/ uint32_t OS_getAffinity() { cpu_set_t mask; @@ -174,15 +163,16 @@ uint32_t OS_getAffinity() return affinity; } - bool SHMProcess::Private::Aux_Core_Attach(bool & versionOK, pid_t & PID) { + if(!locked) return false; + SHMDATA(coreattach)->cl_affinity = OS_getAffinity(); - gcc_barrier - SHMCMD = CORE_ATTACH; - if(!waitWhile(CORE_ATTACH)) - return false; - gcc_barrier + 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; @@ -192,12 +182,13 @@ bool SHMProcess::Private::Aux_Core_Attach(bool & versionOK, pid_t & PID) 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 +199,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,36 +224,65 @@ 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) - { - close(my_SVfileLock); - return false; - } - if(lockf(my_CLfileLock,F_TLOCK, 0) == -1) + for(int i = 0; i < SHM_MAX_CLIENTS; i++) { - // 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) @@ -268,7 +295,7 @@ SHMProcess::SHMProcess(uint32_t PID, vector< memory_info* >& known_versions) /* * Locate the segment. */ - if ((d->my_shmid = shmget(SHM_KEY + PID, SHM_SIZE, 0666)) < 0) + if ((d->shm_ID = shmget(SHM_KEY + PID, /*SHM_ALL_CLIENTS*/SHM_SIZE, 0666)) < 0) { return; } @@ -276,68 +303,54 @@ SHMProcess::SHMProcess(uint32_t PID, vector< memory_info* >& known_versions) /* * 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) { 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(); + detach(); + throw Error::SHMAttachFailure(); return; } if(!bridgeOK) { - fprintf(stderr,"SHM bridge version mismatch\n"); - shmdt(d->my_shm); - // free locks - d->FreeLocks(); + detach(); + throw Error::SHMVersionMismatch(); return; } // find the binary - sprintf(exe_link_name,"/proc/%d/exe", d->my_pid); + sprintf(exe_link_name,"/proc/%d/exe", d->process_ID); 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(); + detach(); return; } - // 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 - 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 + // try to identify the DF version (md5 the binary, compare with known versions) + d->validate(target_name, d->process_ID, known_versions); + d->window = new DFWindow(this); - // free locks - d->FreeLocks(); + // detach + detach(); } bool SHMProcess::isSuspended() @@ -360,7 +373,7 @@ bool SHMProcess::Private::validate(char * exe_file, uint32_t pid, 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 +381,10 @@ 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; + process_ID = pid; identified = true; - cerr << "identified " << m->getVersion() << endl; + // cerr << "identified " << m->getVersion() << endl; return true; } } @@ -390,26 +403,26 @@ 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 @@ -424,7 +437,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; @@ -455,13 +468,27 @@ bool SHMProcess::suspend() { return true; } - D_SHMCMD = CORE_SUSPEND; - if(!waitWhile(CORE_SUSPEND)) + + // did we just resume a moment ago? + if(D_SHMCMD == CORE_RUN) { - return false; + //fprintf(stderr,"%d invokes step\n",d->attachmentIdx); + D_SHMCMD = CORE_STEP; } - d->suspended = true; - return true; + else + { + //fprintf(stderr,"%d invokes suspend\n",d->attachmentIdx); + D_SHMCMD = CORE_SUSPEND; + } + //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->suspended = true; + d->locked = true; + return true; + } + return false; } bool SHMProcess::asyncSuspend() @@ -474,14 +501,37 @@ bool SHMProcess::asyncSuspend() { return true; } + if(D_SHMCMD == 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; + d->suspended = true; + return true; + } + return false; } else { - D_SHMCMD = CORE_SUSPEND; + // did we just resume a moment ago? + if(D_SHMCMD == CORE_RUN) + { + D_SHMCMD = CORE_STEP; + } + else + { + D_SHMCMD = CORE_SUSPEND; + } + // try locking + if(lockf(d->suspend_lock,F_TLOCK,0) == 0) + { + d->locked = true; + d->suspended = true; + return true; + } + return false; } } @@ -491,21 +541,29 @@ 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) return true; - D_SHMCMD = CORE_RUNNING; + // set core to run + D_SHMCMD = CORE_RUN; d->suspended = false; - return true; + // unlock the suspend lock + if(lockf(d->suspend_lock,F_ULOCK,0) == 0) + { + d->locked = false; + return true; + } + throw Error::SHMLockingError("bool SHMProcess::resume()"); + return false; } bool SHMProcess::attach() { - int status; if(g_pProcess != 0) { // FIXME: throw exception here - programmer error @@ -521,7 +579,7 @@ bool SHMProcess::attach() /* * 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()) @@ -532,7 +590,7 @@ bool SHMProcess::attach() } d->attached = false; cerr << "unable to suspend" << endl; - shmdt(d->my_shm); + shmdt(d->shm_addr); d->FreeLocks(); return false; } @@ -552,11 +610,11 @@ bool SHMProcess::detach() resume(); } // detach segment - if(shmdt(d->my_shm) != -1) + if(shmdt(d->shm_addr) != -1) { d->attached = false; d->suspended = false; - d->my_shm = 0; + d->shm_addr = 0; g_pProcess = 0; d->FreeLocks(); return true; @@ -569,15 +627,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 +649,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 +664,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 +727,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 +779,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 +796,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 +815,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 +833,43 @@ 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); + d->SetAndWait(CORE_READ_STL_STRING); //int length = ((shm_retval *)d->my_shm)->value; - return(string( (char *)d->my_shm+SHM_HEADER)); + 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 +878,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 +904,7 @@ 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; } \ No newline at end of file diff --git a/library/DFProcess-windows-SHM.cpp b/library/DFProcess-windows-SHM.cpp index 8886c0e7c..115dc5180 100644 --- a/library/DFProcess-windows-SHM.cpp +++ b/library/DFProcess-windows-SHM.cpp @@ -32,10 +32,10 @@ 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; identified = false; @@ -44,10 +44,10 @@ class SHMProcess::Private DFCLMutex = 0; }; ~Private(){}; - memory_info * my_descriptor; - DFWindow * my_window; - uint32_t my_pid; - char *my_shm; + memory_info * memdescriptor; + DFWindow * window; + uint32_t process_ID; + char *shm_addr; HANDLE DFSVMutex; HANDLE DFCLMutex; @@ -120,7 +120,7 @@ bool SHMProcess::Private::waitWhile (uint32_t state) { attached = suspended = false; ReleaseMutex(DFCLMutex); - UnmapViewOfFile(my_shm); + UnmapViewOfFile(shm_addr); throw Error::SHMServerDisappeared(); return false; } @@ -185,7 +185,7 @@ SHMProcess::SHMProcess(uint32_t PID, vector & known_versions) { return; } - d->my_pid = PID; + d->process_ID = PID; // attach the SHM if(!attach()) @@ -196,7 +196,7 @@ SHMProcess::SHMProcess(uint32_t PID, vector & known_versions) // 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; @@ -209,7 +209,7 @@ SHMProcess::SHMProcess(uint32_t PID, vector & known_versions) if(error) { D_SHMCMD = CORE_RUNNING; - UnmapViewOfFile(d->my_shm); + UnmapViewOfFile(d->shm_addr); ReleaseMutex(d->DFCLMutex); CloseHandle(d->DFSVMutex); d->DFSVMutex = 0; @@ -229,7 +229,7 @@ SHMProcess::SHMProcess(uint32_t PID, vector & known_versions) bool found = false; d->identified = false; // open process, we only need the process open - hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, d->my_pid ); + hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, d->process_ID ); if (NULL == hProcess) break; @@ -265,7 +265,7 @@ SHMProcess::SHMProcess(uint32_t PID, vector & known_versions) { memory_info *m = new memory_info(**it); m->RebaseAll(base); - d->my_descriptor = m; + d->memdescriptor = m; d->identified = true; cerr << "identified " << m->getVersion() << endl; break; @@ -276,13 +276,13 @@ SHMProcess::SHMProcess(uint32_t PID, vector & known_versions) if(d->identified) { - d->my_window = new DFWindow(this); + d->window = new DFWindow(this); } else { D_SHMCMD = CORE_RUNNING; - UnmapViewOfFile(d->my_shm); - d->my_shm = 0; + UnmapViewOfFile(d->shm_addr); + d->shm_addr = 0; ReleaseMutex(d->DFCLMutex); CloseHandle(d->DFSVMutex); d->DFSVMutex = 0; @@ -316,13 +316,13 @@ SHMProcess::~SHMProcess() detach(); } // destroy data model. this is assigned by processmanager - if(d->my_descriptor) + if(d->memdescriptor) { - delete d->my_descriptor; + delete d->memdescriptor; } - if(d->my_window) + if(d->window) { - delete d->my_window; + delete d->window; } // release mutex handles we have if(d->DFCLMutex) @@ -338,17 +338,17 @@ SHMProcess::~SHMProcess() 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 @@ -363,7 +363,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; @@ -476,7 +476,7 @@ bool SHMProcess::attach() } char shmname [256]; - sprintf(shmname,"DFShm-%d",d->my_pid); + sprintf(shmname,"DFShm-%d",d->process_ID); // now try getting and attaching the shared memory HANDLE shmHandle = OpenFileMapping(FILE_MAP_ALL_ACCESS,false,shmname); @@ -487,8 +487,8 @@ bool SHMProcess::attach() } // attempt to attach the opened mapping - d->my_shm = (char *) MapViewOfFile(shmHandle,FILE_MAP_ALL_ACCESS, 0,0, SHM_SIZE); - if(!d->my_shm) + d->shm_addr = (char *) MapViewOfFile(shmHandle,FILE_MAP_ALL_ACCESS, 0,0, SHM_ALL_CLIENTS); + if(!d->shm_addr) { CloseHandle(shmHandle); ReleaseMutex(d->DFCLMutex); @@ -509,7 +509,7 @@ bool SHMProcess::detach() return false; } // detach segment - UnmapViewOfFile(d->my_shm); + UnmapViewOfFile(d->shm_addr); // release it for some other client ReleaseMutex(d->DFCLMutex); // we keep the mutex handles d->attached = false; @@ -528,7 +528,7 @@ void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buff full_barrier D_SHMCMD = CORE_DFPP_READ; d->waitWhile(CORE_DFPP_READ); - memcpy (target_buffer, d->my_shm + SHM_HEADER,size); + memcpy (target_buffer, d->shm_addr + SHM_HEADER,size); } // a big read, we pull data over the shm in iterations else @@ -543,7 +543,7 @@ void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buff full_barrier D_SHMCMD = CORE_DFPP_READ; d->waitWhile(CORE_DFPP_READ); - memcpy (target_buffer, d->my_shm + SHM_HEADER,size); + memcpy (target_buffer, d->shm_addr + SHM_HEADER,size); // decrease size by bytes read size -= to_read; // move the cursors @@ -647,7 +647,7 @@ void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buf { D_SHMHDR->address = dst_address; D_SHMHDR->length = size; - memcpy(d->my_shm+SHM_HEADER,source_buffer, size); + memcpy(d->shm_addr+SHM_HEADER,source_buffer, size); full_barrier D_SHMCMD = CORE_WRITE; d->waitWhile(CORE_WRITE); @@ -662,7 +662,7 @@ 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->shm_addr+SHM_HEADER,source_buffer, to_write); full_barrier D_SHMCMD = CORE_WRITE; d->waitWhile(CORE_WRITE); @@ -723,7 +723,7 @@ const std::string SHMProcess::readSTLString(uint32_t offset) 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)); + return(string(d->shm_addr+SHM_HEADER)); } size_t SHMProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity) @@ -735,7 +735,7 @@ size_t SHMProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapa d->waitWhile(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 + strncpy(buffer, d->shm_addr+SHM_HEADER,real); // length + 1 for the null terminator buffer[real] = 0; return real; } @@ -743,7 +743,7 @@ size_t SHMProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapa 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 + strncpy(d->shm_addr+SHM_HEADER,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); @@ -761,7 +761,7 @@ string SHMProcess::readClassName (uint32_t vptr) // get module index by name and version. bool 1 = error bool SHMProcess::getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT) { - modulelookup * payload = (modulelookup *) (d->my_shm + SHM_HEADER); + modulelookup * payload = (modulelookup *) (d->shm_addr + SHM_HEADER); payload->version = version; strcpy(payload->name,name); full_barrier @@ -774,5 +774,5 @@ bool SHMProcess::getModuleIndex (const char * name, const uint32_t version, uint char * SHMProcess::getSHMStart (void) { - return d->my_shm; + return d->shm_addr; } \ 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/shmserver/mod-core.cpp b/shmserver/mod-core.cpp index cd3137d62..7b5f0a1ab 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 SHMCMDPP ((shm_core_hdr *) shm)->cmd[currentClient].pingpong #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,11 @@ void FindCommand (void * data) SHMHDR->error = true; } +void ReleaseSuspendLock( void * data ) +{ + OS_releaseSuspendLock(currentClient); +} + DFPP_module InitCore(void) { DFPP_module core; @@ -185,7 +193,9 @@ 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!",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 +205,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 +243,84 @@ 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 + SHMCMDPP = 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 = *(uint32_t *) (shm + 4*currentClient); //SHMHDR->cmd[currentClient]; + 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) { - SCHED_YIELD + fprintf(stderr, "Client %d invoked %d:%d = %x = ", + currentClient,((shm_cmd)atomic).parts.module,((shm_cmd)atomic).parts.command, cmd._function); + fprintf(stderr, "%s\n",cmd.name.c_str()); + // FIXME: WHAT HAPPENS WHEN A 'NEXTSTATE' IS FROM A DIFFERENT MODULE THAN 'CORE'? Yeah. It doesn't work. + SHMCMDPP = cmd.nextState; + fprintf(stderr, "Server set %d\n",cmd.nextState); } - numwaits ++; // watchdog timeout - goto check_again; + full_barrier + + if(cmd.type != CANCELLATION) + { + if(useYield) + { + SCHED_YIELD + } + 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..3e79753bf 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++) + { + ((shm_cmd *)shm)[i].pingpong = 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..ee4fff0d5 100644 --- a/shmserver/shms-windows.cpp +++ b/shmserver/shms-windows.cpp @@ -117,7 +117,7 @@ void SHM_Init ( void ) } // create virtual memory mapping - shmHandle = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,SHM_SIZE,shmname); + shmHandle = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,SHM_ALL_CLIENTS,shmname); // if can't create or already exists -> nothing happens if(GetLastError() == ERROR_ALREADY_EXISTS) { @@ -138,7 +138,7 @@ void SHM_Init ( void ) return; } // attempt to attach the created mapping - shm = (char *) MapViewOfFile(shmHandle,FILE_MAP_ALL_ACCESS, 0,0, SHM_SIZE); + shm = (char *) MapViewOfFile(shmHandle,FILE_MAP_ALL_ACCESS, 0,0, SHM_ALL_CLIENTS); if(shm) { ((shm_cmd *)shm)->pingpong = CORE_RUNNING; 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