Merge branch 'shm-multi'

develop
Petr Mrázek 2010-03-14 23:17:54 +01:00
commit e4dc165954
14 changed files with 1219 additions and 770 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,96 +43,77 @@ class SHMProcess::Private
public:
Private()
{
my_descriptor = NULL;
my_pid = 0;
my_shm = 0;
my_shmid = -1;
my_window = NULL;
memdescriptor = NULL;
process_ID = 0;
shm_addr = 0;
//shm_addr_with_cl_idx = 0;
shm_ID = -1;
window = NULL;
attached = false;
suspended = false;
identified = false;
useYield = false;
my_SVfileLock = -1;
my_CLfileLock = -1;
server_lock = -1;
client_lock = -1;
suspend_lock = -1;
attachmentIdx = 0;
locked = false;
};
~Private(){};
memory_info * my_descriptor;
DFWindow * my_window;
pid_t my_pid;
char *my_shm;
int my_shmid;
memory_info * memdescriptor;
DFWindow * window;
pid_t process_ID;
char *shm_addr;
int shm_ID;
Process* q;
int my_SVfileLock;
int my_CLfileLock;
int server_lock;
int client_lock;
int suspend_lock;
int attachmentIdx;
bool attached;
bool suspended;
bool locked;
bool identified;
bool useYield;
bool validate(char* exe_file, uint32_t pid, std::vector< memory_info* >& known_versions);
bool validate(std::vector< memory_info* >& known_versions);
bool Aux_Core_Attach(bool & versionOK, pid_t & PID);
bool waitWhile (uint32_t state);
//bool waitWhile (uint32_t state);
bool SetAndWait (uint32_t state);
bool GetLocks();
bool AreLocksOk();
void FreeLocks();
};
// some helpful macros to keep the code bloat in check
#define SHMCMD ((shm_cmd *)my_shm)->pingpong
#define D_SHMCMD ((shm_cmd *)d->my_shm)->pingpong
#define SHMCMD ( (uint32_t *) shm_addr)[attachmentIdx]
#define D_SHMCMD ( (uint32_t *) (d->shm_addr))[d->attachmentIdx]
#define SHMHDR ((shm_core_hdr *)my_shm)
#define D_SHMHDR ((shm_core_hdr *)d->my_shm)
#define SHMHDR ((shm_core_hdr *)shm_addr)
#define D_SHMHDR ((shm_core_hdr *)(d->shm_addr))
#define SHMDATA(type) ((type *)(my_shm + SHM_HEADER))
#define D_SHMDATA(type) ((type *)(d->my_shm + SHM_HEADER))
#define SHMDATA(type) ((type *)(shm_addr + SHM_HEADER))
#define D_SHMDATA(type) ((type *)(d->shm_addr + SHM_HEADER))
/*
Yeah. with no way to synchronize things (locks are slow, the OS doesn't give us enough control over scheduling)
we end up with this silly thing
*/
bool SHMProcess::Private::waitWhile (uint32_t state)
bool SHMProcess::Private::SetAndWait (uint32_t state)
{
uint32_t cnt = 0;
struct shmid_ds descriptor;
if(!attached) return false;
SHMCMD = state;
while (SHMCMD == state)
{
if(cnt == 10000)// check if the other process is still there
if(cnt == 10000)// check if the other process is still there, don't hammer the kernel too much.
{
/*
shmctl(my_shmid, IPC_STAT, &descriptor);
if(descriptor.shm_nattch == 1)// DF crashed or exited - no way to tell?
{
//detach the shared memory
shmdt(my_shm);
attached = suspended = false;
// we aren't the current process anymore
g_pProcess = NULL;
throw Error::SHMServerDisappeared();
return false;
}
else
{
cnt = 0;
}
*/
if(!AreLocksOk())
{
//detach the shared memory
shmdt(my_shm);
attached = suspended = false;
shmdt(shm_addr);
FreeLocks();
attached = locked = identified = false;
// we aren't the current process anymore
g_pProcess = NULL;
FreeLocks();
throw Error::SHMServerDisappeared();
return false;
}
else
{
@ -145,12 +126,9 @@ bool SHMProcess::Private::waitWhile (uint32_t state)
}
cnt++;
}
// server returned a generic error
if(SHMCMD == CORE_ERROR)
{
SHMCMD = CORE_RUNNING;
attached = suspended = false;
cerr << "shm server error!" << endl;
assert (false);
return false;
}
return true;
@ -160,9 +138,9 @@ bool SHMProcess::Private::waitWhile (uint32_t state)
Yeah. with no way to synchronize things (locks are slow, the OS doesn't give us enough control over scheduling)
we end up with this silly thing
*/
bool SHMProcess::waitWhile (uint32_t state)
bool SHMProcess::SetAndWait (uint32_t state)
{
return d->waitWhile(state);
return d->SetAndWait(state);
}
uint32_t OS_getAffinity()
@ -174,30 +152,13 @@ uint32_t OS_getAffinity()
return affinity;
}
bool SHMProcess::Private::Aux_Core_Attach(bool & versionOK, pid_t & PID)
{
SHMDATA(coreattach)->cl_affinity = OS_getAffinity();
gcc_barrier
SHMCMD = CORE_ATTACH;
if(!waitWhile(CORE_ATTACH))
return false;
gcc_barrier
versionOK =( SHMDATA(coreattach)->sv_version == CORE_VERSION );
PID = SHMDATA(coreattach)->sv_PID;
useYield = SHMDATA(coreattach)->sv_useYield;
#ifdef DEBUG
if(useYield) cerr << "Using Yield!" << endl;
#endif
return true;
}
// test if we have client and server locks and the server is present
bool SHMProcess::Private::AreLocksOk()
{
// both locks are inited (we hold our lock)
if(my_CLfileLock != -1 && my_SVfileLock != -1)
if(client_lock != -1 && server_lock != -1)
{
if(lockf(my_SVfileLock,F_TEST,0) == -1) // and server holds its lock
if(lockf(server_lock,F_TEST,0) == -1) // and server holds its lock
{
return true; // OK, locks are good
}
@ -208,16 +169,23 @@ bool SHMProcess::Private::AreLocksOk()
void SHMProcess::Private::FreeLocks()
{
if(my_CLfileLock != -1)
attachmentIdx = -1;
if(client_lock != -1)
{
lockf(client_lock,F_ULOCK,0);
close(client_lock);
client_lock = -1;
}
if(server_lock != -1)
{
lockf(my_CLfileLock,F_ULOCK,0);
close(my_CLfileLock);
my_CLfileLock = -1;
close(server_lock);
server_lock = -1;
}
if(my_SVfileLock != -1)
if(suspend_lock != -1)
{
close(my_SVfileLock);
my_SVfileLock = -1;
close(suspend_lock);
locked = false;
suspend_lock = -1;
}
}
@ -226,123 +194,102 @@ bool SHMProcess::Private::GetLocks()
char name[256];
// try to acquire locks
// look at the server lock, if it's locked, the server is present
sprintf(name, "/tmp/DFHack/%d/SVlock",my_pid,name);
my_SVfileLock = open(name,O_WRONLY);
if(my_SVfileLock == -1)
sprintf(name, "/tmp/DFHack/%d/SVlock",process_ID);
server_lock = open(name,O_WRONLY);
if(server_lock == -1)
{
// cerr << "can't open sv lock" << endl;
return false;
}
if(lockf( my_SVfileLock, F_TEST, 0 ) != -1)
if(lockf( server_lock, F_TEST, 0 ) != -1)
{
close(my_SVfileLock);
cerr << "sv lock not locked" << endl;
close(server_lock);
server_lock = -1;
return false;
}
// open the client lock, try to lock it
sprintf(name, "/tmp/DFHack/%d/CLlock",my_pid,name);
my_CLfileLock = open(name,O_WRONLY);
if(my_CLfileLock == -1)
for(int i = 0; i < SHM_MAX_CLIENTS; i++)
{
close(my_SVfileLock);
return false;
}
if(lockf(my_CLfileLock,F_TLOCK, 0) == -1)
{
// couldn't acquire lock
close(my_SVfileLock);
close(my_CLfileLock);
return false;
// open the client suspend locked
sprintf(name, "/tmp/DFHack/%d/CLSlock%d",process_ID,i);
suspend_lock = open(name,O_WRONLY);
if(suspend_lock == -1)
{
cerr << "can't open cl S-lock " << i << endl;
// couldn't open lock
continue;
}
// open the client lock, try to lock it
sprintf(name, "/tmp/DFHack/%d/CLlock%d",process_ID,i);
client_lock = open(name,O_WRONLY);
if(client_lock == -1)
{
cerr << "can't open cl lock " << i << endl;
close(suspend_lock);
locked = false;
suspend_lock = -1;
// couldn't open lock
continue;
}
if(lockf(client_lock,F_TLOCK, 0) == -1)
{
// couldn't acquire lock
cerr << "can't acquire cl lock " << i << endl;
close(suspend_lock);
locked = false;
suspend_lock = -1;
close(client_lock);
client_lock = -1;
continue;
}
// ok, we have all the locks we need!
attachmentIdx = i;
return true;
}
// ok, we have all the locks!
return true;
close(server_lock);
server_lock = -1;
cerr << "can't get any client locks" << endl;
return false;
}
SHMProcess::SHMProcess(uint32_t PID, vector< memory_info* >& known_versions)
: d(new Private())
{
char exe_link_name [256];
char target_name[1024];
int target_result;
/*
* Locate the segment.
*/
if ((d->my_shmid = shmget(SHM_KEY + PID, SHM_SIZE, 0666)) < 0)
{
return;
}
/*
* Attach the segment
*/
if ((d->my_shm = (char *) shmat(d->my_shmid, NULL, 0)) == (char *) -1)
{
return;
}
// set pid and gets lock for it
d->my_pid = PID;
if(!d->GetLocks())
d->process_ID = PID;
if(!attach())
{
fprintf(stderr,"Couldn't get locks for PID %d'\n", PID);
shmdt(d->my_shm);
// couldn't attach to process
return;
}
/*
* Test bridge version, get PID, sync Yield
*/
bool bridgeOK;
if(!d->Aux_Core_Attach(bridgeOK,d->my_pid))
if(!d->Aux_Core_Attach(bridgeOK,d->process_ID))
{
fprintf(stderr,"DF terminated during reading\n");
shmdt(d->my_shm);
// free locks
d->FreeLocks();
return;
detach();
throw Error::SHMAttachFailure();
}
if(!bridgeOK)
{
fprintf(stderr,"SHM bridge version mismatch\n");
shmdt(d->my_shm);
// free locks
d->FreeLocks();
return;
}
// find the binary
sprintf(exe_link_name,"/proc/%d/exe", d->my_pid);
target_result = readlink(exe_link_name, target_name, sizeof(target_name)-1);
if (target_result == -1)
{
perror("readlink");
shmdt(d->my_shm);
// free locks
d->FreeLocks();
return;
detach();
throw Error::SHMVersionMismatch();
}
// make sure we have a null terminated string...
// see http://www.opengroup.org/onlinepubs/000095399/functions/readlink.html
target_name[target_result] = 0;
// try to identify the DF version (md5 the binary, compare with known versions)
d->validate(known_versions);
d->window = new DFWindow(this);
// try to identify the DF version
d->validate(target_name, d->my_pid, known_versions);
d->my_window = new DFWindow(this);
gcc_barrier
// at this point, DF is stopped and waiting for commands. make it run again
D_SHMCMD = CORE_RUNNING;
shmdt(d->my_shm); // detach so we don't attach twice when attach() is called
// free locks
d->FreeLocks();
// detach
detach();
}
bool SHMProcess::isSuspended()
{
return d->suspended;
return d->locked;
}
bool SHMProcess::isAttached()
{
@ -354,13 +301,28 @@ bool SHMProcess::isIdentified()
return d->identified;
}
bool SHMProcess::Private::validate(char * exe_file, uint32_t pid, vector <memory_info *> & known_versions)
bool SHMProcess::Private::validate(vector <memory_info *> & known_versions)
{
char exe_link_name [256];
char target_name[1024];
int target_result;
// find the binary
sprintf(exe_link_name,"/proc/%d/exe", process_ID);
target_result = readlink(exe_link_name, target_name, sizeof(target_name)-1);
if (target_result == -1)
{
perror("readlink");
return false;
}
// make sure we have a null terminated string...
// see http://www.opengroup.org/onlinepubs/000095399/functions/readlink.html
target_name[target_result] = 0;
md5wrapper md5;
// get hash of the running DF process
string hash = md5.getHashFromFile(exe_file);
string hash = md5.getHashFromFile(target_name);
vector<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 +330,9 @@ 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;
identified = true;
cerr << "identified " << m->getVersion() << endl;
// cerr << "identified " << m->getVersion() << endl;
return true;
}
}
@ -390,31 +351,37 @@ SHMProcess::~SHMProcess()
detach();
}
// destroy data model. this is assigned by processmanager
if(d->my_window)
if(d->window)
{
delete d->my_window;
delete d->window;
}
delete d;
}
memory_info * SHMProcess::getDescriptor()
{
return d->my_descriptor;
return d->memdescriptor;
}
DFWindow * SHMProcess::getWindow()
{
return d->my_window;
return d->window;
}
int SHMProcess::getPID()
{
return d->my_pid;
return d->process_ID;
}
//FIXME: implement
// there is only one we care about.
bool SHMProcess::getThreadIDs(vector<uint32_t> & threads )
{
if(d->attached)
{
threads.clear();
threads.push_back(d->process_ID);
return true;
}
return false;
}
@ -424,7 +391,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;
@ -451,37 +418,79 @@ bool SHMProcess::suspend()
{
return false;
}
if(d->suspended)
if(d->locked)
{
return true;
}
D_SHMCMD = CORE_SUSPEND;
if(!waitWhile(CORE_SUSPEND))
// FIXME: this should be controlled on the server side
// FIXME: IF server got CORE_RUN in this frame, interpret CORE_SUSPEND as CORE_STEP
// did we just resume a moment ago?
if(D_SHMCMD == CORE_RUN)
{
//fprintf(stderr,"%d invokes step\n",d->attachmentIdx);
// wait for the next window
if(!d->SetAndWait(CORE_STEP))
{
throw Error::SHMLockingError("if(!d->SetAndWait(CORE_STEP))");
}
}
else
{
return false;
//fprintf(stderr,"%d invokes suspend\n",d->attachmentIdx);
// lock now
if(!d->SetAndWait(CORE_SUSPEND))
{
throw Error::SHMLockingError("if(!d->SetAndWait(CORE_SUSPEND))");
}
}
d->suspended = true;
return true;
//fprintf(stderr,"waiting for lock\n");
// we wait for the server to give up our suspend lock (held by default)
if(lockf(d->suspend_lock,F_LOCK,0) == 0)
{
d->locked = true;
return true;
}
return false;
}
// FIXME: needs a good think-through
bool SHMProcess::asyncSuspend()
{
if(!d->attached)
{
return false;
}
if(d->suspended)
if(d->locked)
{
return true;
}
if(D_SHMCMD == CORE_SUSPENDED)
uint32_t cmd = D_SHMCMD;
if(cmd == CORE_SUSPENDED)
{
d->suspended = true;
return true;
// we have to hold the lock to be really suspended
if(lockf(d->suspend_lock,F_LOCK,0) == 0)
{
d->locked = true;
return true;
}
return false;
}
else
{
D_SHMCMD = CORE_SUSPEND;
// did we just resume a moment ago?
if(cmd == CORE_STEP)
{
return false;
}
else if(cmd == CORE_RUN)
{
D_SHMCMD = CORE_STEP;
}
else
{
D_SHMCMD = CORE_SUSPEND;
}
return false;
}
}
@ -491,21 +500,30 @@ bool SHMProcess::forceresume()
return resume();
}
// FIXME: wait for the server to advance a step!
bool SHMProcess::resume()
{
if(!d->attached)
return false;
if(!d->suspended)
if(!d->locked)
return true;
D_SHMCMD = CORE_RUNNING;
d->suspended = false;
return true;
// unlock the suspend lock
if(lockf(d->suspend_lock,F_ULOCK,0) == 0)
{
d->locked = false;
if(d->SetAndWait(CORE_RUN)) // we have to make sure the server responds!
{
return true;
}
throw Error::SHMLockingError("if(d->SetAndWait(CORE_RUN))");
}
throw Error::SHMLockingError("if(lockf(d->suspend_lock,F_ULOCK,0) == 0)");
return false;
}
bool SHMProcess::attach()
{
int status;
if(g_pProcess != 0)
{
// FIXME: throw exception here - programmer error
@ -514,31 +532,39 @@ bool SHMProcess::attach()
}
if(!d->GetLocks())
{
cerr << "server is full or not really there!" << endl;
//cerr << "server is full or not really there!" << endl;
return false;
}
/*
* Locate the segment.
*/
if ((d->shm_ID = shmget(SHM_KEY + d->process_ID, SHM_SIZE, 0666)) < 0)
{
d->FreeLocks();
cerr << "can't find segment" << endl; // FIXME: throw
return false;
}
/*
* Attach the segment
*/
if ((d->my_shm = (char *) shmat(d->my_shmid, NULL, 0)) != (char *) -1)
if ((d->shm_addr = (char *) shmat(d->shm_ID, NULL, 0)) == (char *) -1)
{
d->attached = true;
if(suspend())
{
d->suspended = true;
g_pProcess = this;
return true;
}
d->attached = false;
cerr << "unable to suspend" << endl;
shmdt(d->my_shm);
d->FreeLocks();
cerr << "can't attach segment" << endl; // FIXME: throw
return false;
}
cerr << "unable to attach" << endl;
d->FreeLocks();
return false;
d->attached = true;
if(!suspend())
{
shmdt(d->shm_addr);
d->FreeLocks();
cerr << "unable to suspend" << endl;
return false;
}
g_pProcess = this;
return true;
}
bool SHMProcess::detach()
@ -547,18 +573,18 @@ bool SHMProcess::detach()
{
return false;
}
if(d->suspended)
if(d->locked)
{
resume();
}
// detach segment
if(shmdt(d->my_shm) != -1)
if(shmdt(d->shm_addr) != -1)
{
d->FreeLocks();
d->locked = false;
d->attached = false;
d->suspended = false;
d->my_shm = 0;
d->shm_addr = 0;
g_pProcess = 0;
d->FreeLocks();
return true;
}
// fail if we can't detach
@ -569,15 +595,16 @@ bool SHMProcess::detach()
void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buffer)
{
if(!d->locked) throw Error::SHMAccessDenied();
// normal read under 1MB
if(size <= SHM_BODY)
{
D_SHMHDR->address = src_address;
D_SHMHDR->length = size;
gcc_barrier
D_SHMCMD = CORE_DFPP_READ;
waitWhile(CORE_DFPP_READ);
memcpy (target_buffer, d->my_shm + SHM_HEADER,size);
d->SetAndWait(CORE_READ);
memcpy (target_buffer, D_SHMDATA(void),size);
}
// a big read, we pull data over the shm in iterations
else
@ -590,9 +617,8 @@ void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buff
D_SHMHDR->address = src_address;
D_SHMHDR->length = to_read;
gcc_barrier
D_SHMCMD = CORE_DFPP_READ;
waitWhile(CORE_DFPP_READ);
memcpy (target_buffer, d->my_shm + SHM_HEADER,size);
d->SetAndWait(CORE_READ);
memcpy (target_buffer, D_SHMDATA(void) ,size);
// decrease size by bytes read
size -= to_read;
// move the cursors
@ -606,54 +632,60 @@ void SHMProcess::read (uint32_t src_address, uint32_t size, uint8_t *target_buff
uint8_t SHMProcess::readByte (const uint32_t offset)
{
if(!d->locked) throw Error::SHMAccessDenied();
D_SHMHDR->address = offset;
gcc_barrier
D_SHMCMD = CORE_READ_BYTE;
waitWhile(CORE_READ_BYTE);
d->SetAndWait(CORE_READ_BYTE);
return D_SHMHDR->value;
}
void SHMProcess::readByte (const uint32_t offset, uint8_t &val )
{
if(!d->locked) throw Error::SHMAccessDenied();
D_SHMHDR->address = offset;
gcc_barrier
D_SHMCMD = CORE_READ_BYTE;
waitWhile(CORE_READ_BYTE);
d->SetAndWait(CORE_READ_BYTE);
val = D_SHMHDR->value;
}
uint16_t SHMProcess::readWord (const uint32_t offset)
{
if(!d->locked) throw Error::SHMAccessDenied();
D_SHMHDR->address = offset;
gcc_barrier
D_SHMCMD = CORE_READ_WORD;
waitWhile(CORE_READ_WORD);
d->SetAndWait(CORE_READ_WORD);
return D_SHMHDR->value;
}
void SHMProcess::readWord (const uint32_t offset, uint16_t &val)
{
if(!d->locked) throw Error::SHMAccessDenied();
D_SHMHDR->address = offset;
gcc_barrier
D_SHMCMD = CORE_READ_WORD;
waitWhile(CORE_READ_WORD);
d->SetAndWait(CORE_READ_WORD);
val = D_SHMHDR->value;
}
uint32_t SHMProcess::readDWord (const uint32_t offset)
{
if(!d->locked) throw Error::SHMAccessDenied();
D_SHMHDR->address = offset;
gcc_barrier
D_SHMCMD = CORE_READ_DWORD;
waitWhile(CORE_READ_DWORD);
d->SetAndWait(CORE_READ_DWORD);
return D_SHMHDR->value;
}
void SHMProcess::readDWord (const uint32_t offset, uint32_t &val)
{
if(!d->locked) throw Error::SHMAccessDenied();
D_SHMHDR->address = offset;
gcc_barrier
D_SHMCMD = CORE_READ_DWORD;
waitWhile(CORE_READ_DWORD);
d->SetAndWait(CORE_READ_DWORD);
val = D_SHMHDR->value;
}
@ -663,43 +695,47 @@ void SHMProcess::readDWord (const uint32_t offset, uint32_t &val)
void SHMProcess::writeDWord (uint32_t offset, uint32_t data)
{
if(!d->locked) throw Error::SHMAccessDenied();
D_SHMHDR->address = offset;
D_SHMHDR->value = data;
gcc_barrier
D_SHMCMD = CORE_WRITE_DWORD;
waitWhile(CORE_WRITE_DWORD);
d->SetAndWait(CORE_WRITE_DWORD);
}
// using these is expensive.
void SHMProcess::writeWord (uint32_t offset, uint16_t data)
{
if(!d->locked) throw Error::SHMAccessDenied();
D_SHMHDR->address = offset;
D_SHMHDR->value = data;
gcc_barrier
D_SHMCMD = CORE_WRITE_WORD;
waitWhile(CORE_WRITE_WORD);
d->SetAndWait(CORE_WRITE_WORD);
}
void SHMProcess::writeByte (uint32_t offset, uint8_t data)
{
if(!d->locked) throw Error::SHMAccessDenied();
D_SHMHDR->address = offset;
D_SHMHDR->value = data;
gcc_barrier
D_SHMCMD = CORE_WRITE_BYTE;
waitWhile(CORE_WRITE_BYTE);
d->SetAndWait(CORE_WRITE_BYTE);
}
void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buffer)
{
if(!d->locked) throw Error::SHMAccessDenied();
// normal write under 1MB
if(size <= SHM_BODY)
{
D_SHMHDR->address = dst_address;
D_SHMHDR->length = size;
memcpy(d->my_shm+SHM_HEADER,source_buffer, size);
memcpy(D_SHMDATA(void),source_buffer, size);
gcc_barrier
D_SHMCMD = CORE_WRITE;
waitWhile(CORE_WRITE);
d->SetAndWait(CORE_WRITE);
}
// a big write, we push this over the shm in iterations
else
@ -711,10 +747,9 @@ void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buf
// write to_write bytes to dst_cursor
D_SHMHDR->address = dst_address;
D_SHMHDR->length = to_write;
memcpy(d->my_shm+SHM_HEADER,source_buffer, to_write);
memcpy(D_SHMDATA(void),source_buffer, to_write);
gcc_barrier
D_SHMCMD = CORE_WRITE;
waitWhile(CORE_WRITE);
d->SetAndWait(CORE_WRITE);
// decrease size by bytes written
size -= to_write;
// move the cursors
@ -729,6 +764,8 @@ void SHMProcess::write (uint32_t dst_address, uint32_t size, uint8_t *source_buf
// FIXME: butt-fugly
const std::string SHMProcess::readCString (uint32_t offset)
{
if(!d->locked) throw Error::SHMAccessDenied();
std::string temp;
char temp_c[256];
int counter = 0;
@ -746,6 +783,8 @@ const std::string SHMProcess::readCString (uint32_t offset)
DfVector SHMProcess::readVector (uint32_t offset, uint32_t item_size)
{
if(!d->locked) throw Error::SHMAccessDenied();
/*
GNU libstdc++ vector is three pointers long
ptr start
@ -762,38 +801,42 @@ DfVector SHMProcess::readVector (uint32_t offset, uint32_t item_size)
const std::string SHMProcess::readSTLString(uint32_t offset)
{
if(!d->locked) throw Error::SHMAccessDenied();
D_SHMHDR->address = offset;
full_barrier
D_SHMCMD = CORE_READ_STL_STRING;
waitWhile(CORE_READ_STL_STRING);
//int length = ((shm_retval *)d->my_shm)->value;
return(string( (char *)d->my_shm+SHM_HEADER));
d->SetAndWait(CORE_READ_STL_STRING);
return(string( D_SHMDATA(char) ));
}
size_t SHMProcess::readSTLString (uint32_t offset, char * buffer, size_t bufcapacity)
{
if(!d->locked) throw Error::SHMAccessDenied();
D_SHMHDR->address = offset;
full_barrier
D_SHMCMD = CORE_READ_STL_STRING;
waitWhile(CORE_READ_STL_STRING);
d->SetAndWait(CORE_READ_STL_STRING);
size_t length = D_SHMHDR->value;
size_t fit = min(bufcapacity - 1, length);
strncpy(buffer,(char *)d->my_shm+SHM_HEADER,fit);
strncpy(buffer,D_SHMDATA(char),fit);
buffer[fit] = 0;
return fit;
}
void SHMProcess::writeSTLString(const uint32_t address, const std::string writeString)
{
if(!d->locked) throw Error::SHMAccessDenied();
D_SHMHDR->address = address;
strncpy(d->my_shm+SHM_HEADER,writeString.c_str(),writeString.length()+1); // length + 1 for the null terminator
strncpy(D_SHMDATA(char),writeString.c_str(),writeString.length()+1); // length + 1 for the null terminator
full_barrier
D_SHMCMD = CORE_WRITE_STL_STRING;
waitWhile(CORE_WRITE_STL_STRING);
d->SetAndWait(CORE_WRITE_STL_STRING);
}
string SHMProcess::readClassName (uint32_t vptr)
{
if(!d->locked) throw Error::SHMAccessDenied();
int typeinfo = readDWord(vptr - 0x4);
int typestring = readDWord(typeinfo + 0x4);
string raw = readCString(typestring);
@ -802,22 +845,18 @@ string SHMProcess::readClassName (uint32_t vptr)
return raw.substr(start,end-start - 2); // trim the 'st' from the end
}
// FIXME: having this around could lead to bad things in the hands of unsuspecting fools
// *!!DON'T BE AN UNSUSPECTING FOOL!!*
// the whole SHM thing works only because copying DWORDS is an atomic operation on i386 and x86_64 archs
// get module index by name and version. bool 1 = error
// get module index by name and version. bool 0 = error
bool SHMProcess::getModuleIndex (const char * name, const uint32_t version, uint32_t & OUTPUT)
{
modulelookup * payload = (modulelookup *) (d->my_shm + SHM_HEADER);
if(!d->locked) throw Error::SHMAccessDenied();
modulelookup * payload = D_SHMDATA(modulelookup);
payload->version = version;
strncpy(payload->name,name,255);
payload->name[255] = 0;
full_barrier
D_SHMCMD = CORE_ACQUIRE_MODULE;
if(!waitWhile(CORE_ACQUIRE_MODULE))
if(!SetAndWait(CORE_ACQUIRE_MODULE))
{
return false; // FIXME: throw a fatal exception instead
}
@ -832,5 +871,26 @@ bool SHMProcess::getModuleIndex (const char * name, const uint32_t version, uint
char * SHMProcess::getSHMStart (void)
{
return d->my_shm;
if(!d->locked) return 0; //THROW HERE!
return /*d->shm_addr_with_cl_idx*/ d->shm_addr;
}
bool SHMProcess::Private::Aux_Core_Attach(bool & versionOK, pid_t & PID)
{
if(!locked) throw Error::SHMAccessDenied();
SHMDATA(coreattach)->cl_affinity = OS_getAffinity();
if(!SetAndWait(CORE_ATTACH)) return false;
/*
cerr <<"CORE_VERSION" << CORE_VERSION << endl;
cerr <<"server CORE_VERSION" << SHMDATA(coreattach)->sv_version << endl;
*/
versionOK =( SHMDATA(coreattach)->sv_version == CORE_VERSION );
PID = SHMDATA(coreattach)->sv_PID;
useYield = SHMDATA(coreattach)->sv_useYield;
#ifdef DEBUG
if(useYield) cerr << "Using Yield!" << endl;
#endif
return true;
}

File diff suppressed because it is too large Load Diff

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

Binary file not shown.

Binary file not shown.

@ -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 SHMCMD ((uint32_t *)shm )[currentClient]
#define SHMDATA(type) ((type *)(shm + SHM_HEADER))
void ReadRaw (void * data)
{
memcpy(shm + SHM_HEADER, (void *) SHMHDR->address,SHMHDR->length);
memcpy(SHMDATA(void), (void *) SHMHDR->address,SHMHDR->length);
}
void ReadDWord (void * data)
@ -73,7 +76,7 @@ void ReadByte (void * data)
void WriteRaw (void * data)
{
memcpy((void *)SHMHDR->address, shm + SHM_HEADER,SHMHDR->length);
memcpy((void *)SHMHDR->address, SHMDATA(void),SHMHDR->length);
}
void WriteDWord (void * data)
@ -97,14 +100,14 @@ void ReadSTLString (void * data)
unsigned int l = myStringPtr->length();
SHMHDR->value = l;
// FIXME: there doesn't have to be a null terminator!
strncpy(shm+SHM_HEADER,myStringPtr->c_str(),l+1);
strncpy( SHMDATA(char),myStringPtr->c_str(),l+1);
}
void WriteSTLString (void * data)
{
std::string * myStringPtr = (std::string *) SHMHDR->address;
// here we DO expect a 0 terminator
myStringPtr->assign((const char *) (shm + SHM_HEADER));
myStringPtr->assign( SHMDATA(const char) );
}
// MIT HAKMEM bitcount
@ -133,7 +136,7 @@ void CoreAttach (void * data)
void FindModule (void * data)
{
bool found = false;
modulelookup * payload = (modulelookup *) (shm + SHM_HEADER);
modulelookup * payload = SHMDATA(modulelookup);
std::string test = payload->name;
uint32_t version = payload->version;
for(unsigned int i = 0; i < module_registry.size();i++)
@ -175,6 +178,17 @@ void FindCommand (void * data)
SHMHDR->error = true;
}
void ReleaseSuspendLock( void * data )
{
OS_releaseSuspendLock(currentClient);
}
void AcquireSuspendLock( void * data )
{
OS_lockSuspendLock(currentClient);
}
DFPP_module InitCore(void)
{
DFPP_module core;
@ -185,7 +199,10 @@ DFPP_module InitCore(void)
core.reserve(NUM_CORE_CMDS);
// basic states
core.set_command(CORE_RUNNING, CANCELLATION, "Running");
core.set_command(CORE_SUSPEND, CLIENT_WAIT, "Suspend", 0 , CORE_SUSPENDED);
//core.set_command(CORE_RUN, FUNCTION, "Run!",AcquireSuspendLock,CORE_RUNNING);
core.set_command(CORE_RUN, CANCELLATION, "Run!",0,CORE_RUNNING);
core.set_command(CORE_STEP, CANCELLATION, "Suspend on next step",0,CORE_SUSPEND);// set command to CORE_SUSPEND, check next client
core.set_command(CORE_SUSPEND, FUNCTION, "Suspend", ReleaseSuspendLock , CORE_SUSPENDED);
core.set_command(CORE_SUSPENDED, CLIENT_WAIT, "Suspended");
core.set_command(CORE_ERROR, CANCELLATION, "Error");
@ -195,7 +212,7 @@ DFPP_module InitCore(void)
core.set_command(CORE_ACQUIRE_COMMAND, FUNCTION, "Command lookup", FindCommand, CORE_SUSPENDED);
// raw reads
core.set_command(CORE_DFPP_READ, FUNCTION,"Raw read",ReadRaw, CORE_SUSPENDED);
core.set_command(CORE_READ, FUNCTION,"Raw read",ReadRaw, CORE_SUSPENDED);
core.set_command(CORE_READ_DWORD, FUNCTION,"Read DWORD",ReadDWord, CORE_SUSPENDED);
core.set_command(CORE_READ_WORD, FUNCTION,"Read WORD",ReadWord, CORE_SUSPENDED);
core.set_command(CORE_READ_BYTE, FUNCTION,"Read BYTE",ReadByte, CORE_SUSPENDED);
@ -233,60 +250,90 @@ void KillModules (void)
void SHM_Act (void)
{
volatile uint32_t atomic = 0;
if(errorstate)
{
return;
}
uint32_t numwaits = 0;
check_again: // goto target!!!
if(numwaits == 10000)
//static uint oldcl = 88;
for(currentClient = 0; currentClient < SHM_MAX_CLIENTS;currentClient++)
{
// this tests if there's a process on the other side
if(isValidSHM())
// set the offset for the shared memory used for the client
uint32_t numwaits = 0;
check_again: // goto target!!!
if(numwaits == 10000)
{
numwaits = 0;
// this tests if there's a process on the other side
if(isValidSHM(currentClient))
{
numwaits = 0;
}
else
{
full_barrier
SHMCMD = CORE_RUNNING;
fprintf(stderr,"dfhack: Broke out of loop, other process disappeared.\n");
}
}
else
full_barrier // I don't want the compiler to reorder my code.
//fprintf(stderr,"%d: %x %x\n",currentClient, (uint) SHMHDR, (uint) &(SHMHDR->cmd[currentClient]));
// this is very important! copying two words separately from the command variable leads to inconsistency.
// Always copy the thing in one go.
// Also, this whole SHM thing probably only works on intel processors
atomic = SHMCMD;
full_barrier
DFPP_module & mod = module_registry[ ((shm_cmd)atomic).parts.module ];
DFPP_command & cmd = mod.commands[ ((shm_cmd)atomic).parts.command ];
/*
if(atomic == CORE_RUNNING)
{
full_barrier
SHMCMD = CORE_RUNNING;
fprintf(stderr,"dfhack: Broke out of loop, other process disappeared.\n");
// we are running again for this process
// reaquire the suspend lock
OS_lockSuspendLock(currentClient);
continue;
}
}
// this is very important! copying two words separately from the command variable leads to inconsistency.
// Always copy the thing in one go.
// Also, this whole SHM thing probably only works on intel processors
volatile shm_cmd atomic = SHMHDR->cmd;
full_barrier
DFPP_module & mod = module_registry[atomic.parts.module];
DFPP_command & cmd = mod.commands[atomic.parts.command];
full_barrier
/*
fprintf(stderr, "Called %x\0", cmd._function);
fprintf(stderr, "Client invoked %d:%d = ",atomic.parts.module,atomic.parts.command);
fprintf(stderr, "%s\n",cmd.name.c_str());
*/
full_barrier
if(cmd._function)
{
cmd._function(mod.modulestate);
}
full_barrier
if(cmd.nextState != -1)
{
SHMCMD = cmd.nextState;
}
*/
if(cmd._function)
{
cmd._function(mod.modulestate);
}
full_barrier
if(cmd.type != CANCELLATION)
{
if(useYield)
if(cmd.nextState != -1)
{
/*
char text [512];
char text2 [512];
sprintf (text,"Client %d invoked %d:%d = %x = %s\n",currentClient,((shm_cmd)atomic).parts.module,((shm_cmd)atomic).parts.command, cmd._function,cmd.name.c_str());
sprintf(text2, "Server set %d\n",cmd.nextState);
*/
// FIXME: WHAT HAPPENS WHEN A 'NEXTSTATE' IS FROM A DIFFERENT MODULE THAN 'CORE'? Yeah. It doesn't work.
SHMCMD = cmd.nextState;
//MessageBox(0,text,text2, MB_OK);
//fflush(stderr); // make sure this finds its way to the terminal!
}
full_barrier
if(cmd.type != CANCELLATION)
{
SCHED_YIELD
if(useYield)
{
SCHED_YIELD
}
numwaits ++; // watchdog timeout
goto check_again;
}
numwaits ++; // watchdog timeout
goto check_again;
full_barrier
// we are running again for this process
// reaquire the suspend lock
OS_lockSuspendLock(currentClient);
}
}

@ -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++)
{
((uint32_t *)shm)[i] = CORE_RUNNING;
}
InitModules();
}
@ -177,21 +215,29 @@ void SHM_Destroy ( void )
}
shmctl(shmid,IPC_RMID,NULL);
// unlock and close server lock, close client lock
lockf(fd_svlock,F_ULOCK,0);
close(fd_svlock);
close(fd_cllock);
fd_svlock = 0;
fd_cllock = 0;
// destroy lock files
char name[256];
char name2[256];
sprintf(name, "/tmp/DFHack/%d",OS_getPID());
// unlock and close server lock, close client lock, destroy files
lockf(fd_svlock,F_ULOCK,0);
for(int i = 0; i < SHM_MAX_CLIENTS; i++)
{
close(fd_cllock[i]);
fd_cllock[i] = 0;
close(fd_clSlock[i]);
fd_clSlock[i] = 0;
held_clSlock[i] = 0;
sprintf(name2, "%s/CLlock%d",name,i);
unlink(name2);
sprintf(name2, "%s/CLSlock%d",name,i);
unlink(name2);
}
close(fd_svlock);
fd_svlock = 0;
sprintf(name2, "%s/SVlock",name);
unlink(name2);
sprintf(name2, "%s/CLlock",name);
unlink(name2);
// remove the PID folder
rmdir(name);
fprintf(stderr,"dfhack: destroyed shared segment.\n");
inited = false;
@ -213,7 +259,7 @@ DFhackCExport void SDL_GL_SwapBuffers(void)
{
if(_SDL_GL_SwapBuffers)
{
if(!errorstate && ((shm_cmd *)shm)->pingpong != CORE_RUNNING)
if(!errorstate)
{
SHM_Act();
}
@ -227,7 +273,7 @@ DFhackCExport int SDL_Flip(void * some_ptr)
{
if(_SDL_Flip)
{
if(!errorstate && ((shm_cmd *)shm)->pingpong != CORE_RUNNING)
if(!errorstate)
{
SHM_Act();
}
@ -285,7 +331,7 @@ DFhackCExport int refresh (void)
{
if(_refresh)
{
if(!errorstate && ((shm_cmd *)shm)->pingpong != CORE_RUNNING)
if(!errorstate)
{
SHM_Act();
}

@ -44,8 +44,65 @@ char *shm = 0;
int shmid = 0;
bool inited = 0;
HANDLE shmHandle = 0;
HANDLE DFSVMutex = 0;
HANDLE DFCLMutex = 0;
HANDLE DFCLMutex[SHM_MAX_CLIENTS];
HANDLE DFCLSuspendMutex[SHM_MAX_CLIENTS];
int held_DFCLSuspendMutex[SHM_MAX_CLIENTS];
int numheld = SHM_MAX_CLIENTS;
void OS_lockSuspendLock(int which)
{
if(numheld == SHM_MAX_CLIENTS)
return;
// lock not held by server and can be picked up. OK.
if(held_DFCLSuspendMutex[which] == 0)
{
uint32_t state = WaitForSingleObject(DFCLSuspendMutex[which],INFINITE);
if(state == WAIT_ABANDONED || state == WAIT_OBJECT_0)
{
held_DFCLSuspendMutex[which] = 1;
numheld++;
return;
}
// lock couldn't be picked up!
errorstate = 1;
MessageBox(0,"Suspend lock locking failed. Further communication disabled!","Error", MB_OK);
return;
}
errorstate = 1;
MessageBox(0,"Server tried to lock already locked suspend lock? Further communication disabled!","Error", MB_OK);
return;
}
void OS_releaseSuspendLock(int which)
{
/*
if(which >=0 && which < SHM_MAX_CLIENTS)
return;
*/
if(numheld != SHM_MAX_CLIENTS)
{
MessageBox(0,"Locking system failure. Further communication disabled!","Error", MB_OK);
errorstate = 1;
return;
}
// lock hel by server and can be released -> OK
if(held_DFCLSuspendMutex[which] == 1 && ReleaseMutex(DFCLSuspendMutex[which]))
{
numheld--;
held_DFCLSuspendMutex[which] = 0;
}
// locked and not can't be released? FAIL!
else if (held_DFCLSuspendMutex[which] == 1)
{
MessageBox(0,"Suspend lock failed to unlock. Further communication disabled!","Error", MB_OK);
return;
}
}
void SHM_Init ( void )
{
// check that we do this only once per process
@ -56,66 +113,48 @@ void SHM_Init ( void )
}
inited = true;
char svmutexname [256];
sprintf(svmutexname,"DFSVMutex-%d",OS_getPID());
char clmutexname [256];
sprintf(clmutexname,"DFCLMutex-%d",OS_getPID());
char clsmutexname [256];
char shmname [256];
sprintf(shmname,"DFShm-%d",OS_getPID());
// create or open mutexes
// create a locked server mutex
char svmutexname [256];
sprintf(svmutexname,"DFSVMutex-%d",OS_getPID());
DFSVMutex = CreateMutex( 0, 1, svmutexname);
if(DFSVMutex == 0)
{
DFSVMutex = OpenMutex(SYNCHRONIZE,false, svmutexname);
if(DFSVMutex == 0)
{
errorstate = 1;
return;
}
MessageBox(0,"Server mutex creation failed. Further communication disabled!","Error", MB_OK);
errorstate = 1;
return;
}
DFCLMutex = CreateMutex( 0, 0, clmutexname);
if(DFCLMutex == 0)
// the mutex already existed. we don't want to know.
if(GetLastError() == ERROR_ALREADY_EXISTS)
{
DFCLMutex = OpenMutex(SYNCHRONIZE,false, clmutexname);
if(DFCLMutex == 0)
{
CloseHandle(DFSVMutex);
errorstate = 1;
return;
}
MessageBox(0,"Server mutex already existed. Further communication disabled!","Error", MB_OK);
errorstate = 1;
return;
}
// try locking server mutex
uint32_t result;
result = WaitForSingleObject(DFSVMutex,0);
switch (result)
// create client and suspend mutexes
for(int i = 0; i < SHM_MAX_CLIENTS; i++)
{
case WAIT_ABANDONED:
sprintf(clmutexname,"DFCLMutex-%d-%d",OS_getPID(),i);
sprintf(clsmutexname,"DFCLSuspendMutex-%d-%d",OS_getPID(),i);
DFCLMutex[i] = CreateMutex( 0, 0, clmutexname); // client mutex, not held
DFCLSuspendMutex[i] = CreateMutex( 0, 1, clsmutexname); // suspend mutexes held on start
held_DFCLSuspendMutex[i] = 1;
if(DFCLMutex[i] == 0 || DFCLSuspendMutex[i] == 0 || GetLastError() == ERROR_ALREADY_EXISTS)
{
// picked up after a crashed DF process
// do some sanity checks on client attached
// otherwise the same thing as WAIT_OBJECT_0
break;
}
case WAIT_OBJECT_0:
{
// all right, we have the mutex and are the one and only DF server
break;
}
case WAIT_TIMEOUT:
case WAIT_FAILED:
default:
{
// error, bail
MessageBox(0,"Client mutex creation failed. Close all tools before starting DF.","Error", MB_OK);
errorstate = 1;
MessageBox(0,"Could not aquire mutex","Error", MB_OK);
CloseHandle(DFSVMutex);
CloseHandle(DFCLMutex);
return;
}
}
// create virtual memory mapping
shmHandle = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,SHM_SIZE,shmname);
// if can't create or already exists -> nothing happens
@ -123,35 +162,32 @@ void SHM_Init ( void )
{
MessageBox(0,"SHM bridge already in use","Error", MB_OK);
errorstate = 1;
ReleaseMutex(DFSVMutex);
CloseHandle(DFSVMutex);
CloseHandle(DFCLMutex);
return;
}
if(!shmHandle)
{
MessageBox(0,"Couldn't create SHM bridge","Error", MB_OK);
errorstate = 1;
ReleaseMutex(DFSVMutex);
CloseHandle(DFSVMutex);
CloseHandle(DFCLMutex);
return;
}
// attempt to attach the created mapping
shm = (char *) MapViewOfFile(shmHandle,FILE_MAP_ALL_ACCESS, 0,0, SHM_SIZE);
if(shm)
{
((shm_cmd *)shm)->pingpong = CORE_RUNNING;
// make sure we don't stall or do crazy stuff
for(int i = 0; i < SHM_MAX_CLIENTS;i++)
{
((uint32_t *)shm)[i] = CORE_RUNNING;
}
// init modules :)
InitModules();
}
else
{
MessageBox(0,"Couldn't attach SHM bridge","Error", MB_OK);
errorstate = 1;
ReleaseMutex(DFSVMutex);
CloseHandle(DFSVMutex);
CloseHandle(DFCLMutex);
return;
}
InitModules();
}
void SHM_Destroy ( void )
@ -159,9 +195,13 @@ void SHM_Destroy ( void )
if(errorstate)
return;
KillModules();
ReleaseMutex(DFSVMutex);
// get rid of all the locks
CloseHandle(DFSVMutex);
CloseHandle(DFCLMutex);
for(int i=0; i < SHM_MAX_CLIENTS; i++)
{
CloseHandle(DFCLSuspendMutex[i]);
CloseHandle(DFCLMutex[i]);
}
}
uint32_t OS_getPID()
@ -181,17 +221,18 @@ uint32_t OS_getAffinity()
// is the other side still there?
bool isValidSHM()
bool isValidSHM(int which)
{
// try if CL mutex is free
uint32_t result = WaitForSingleObject(DFCLMutex,0);
// try if CL mutex is free (by locking client mutex)
uint32_t result = WaitForSingleObject(DFCLMutex[which],0);
switch (result)
{
case WAIT_ABANDONED:
case WAIT_OBJECT_0:
{
ReleaseMutex(DFCLMutex);
OS_lockSuspendLock(which);
ReleaseMutex(DFCLMutex[which]);
return false;
}
case WAIT_TIMEOUT:

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