Another try at locking

develop
Petr Mrázek 2010-03-12 00:13:50 +01:00
parent f72bb0373d
commit 9d503515dd
12 changed files with 599 additions and 370 deletions

@ -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";
}
};
}
}

@ -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;
}

@ -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 <memory
// get hash of the running DF process
string hash = md5.getHashFromFile(exe_file);
vector<memory_info *>::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 <memory
if(hash == (*it)->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<t_memrange> & 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;
}

@ -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 <memory_info *> & 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 <memory_info *> & 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 <memory_info *> & 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 <memory_info *> & 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 <memory_info *> & 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 <memory_info *> & 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<t_memrange> & 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;
}

@ -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
}

@ -41,19 +41,22 @@ distribution.
std::vector <DFPP_module> 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);
}
}

@ -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

@ -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

@ -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;

@ -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();
}

@ -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;

@ -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