delegate checking and loading server config files to own thread

"readserverconfigsthread" gets triggered by poll_serverthreads() once a
minute to check the filesizes of all server config files. Altered files
get loaded into memory.
poll_serverthreads() in the main thread then processes the buffered files
one at a time. During processing, a semaphore is locked, so that other
threads can use the data structures derived from config files - if they
use the semaphore to lock the ressource.
Using the data from main thread can be done without using the semaphore,
since _all_ changes are done in the main thread.
If the main thread wants to update data that is locked by a thread, the
main thread will defer the changes for a minute. The main thread will
never lock.
This commit is contained in:
ac-stef
2017-06-27 15:15:21 +02:00
parent 1c109bfa62
commit 5ec04b82e1
4 changed files with 98 additions and 64 deletions
+8 -3
View File
@@ -1119,12 +1119,17 @@ struct serverconfigfile
string filename;
int filelen;
char *buf;
serverconfigfile() : filelen(0), buf(NULL) { filename[0] = '\0'; }
sl_semaphore busy;
serverconfigfile() : filelen(0), buf(NULL), busy(1, NULL) { filename[0] = '\0'; }
virtual ~serverconfigfile() { DELETEA(buf); }
virtual void clear() {}
virtual void read() {}
void init(const char *name);
void init(const char *name, bool tracking = true);
bool load();
bool isbusy();
void trypreload();
void process();
};
// server commandline parsing
@@ -1268,7 +1273,7 @@ struct servercommandline
case 'C': if(*a && clfilenesting < 3)
{
serverconfigfile cfg;
cfg.init(a);
cfg.init(a, false);
cfg.load();
int line = 1;
clfilenesting++;
+19 -23
View File
@@ -108,7 +108,7 @@ int cn2boot;
void poll_serverthreads() // called once per mainloop-timeslice
{
static vector<servermap *> servermapstodelete;
static int stage = 0, lastworkerthreadstart = 0;
static int stage = 0, lastworkerthreadstart = 0, nextserverconfig = 0;
switch(stage)
{
@@ -158,24 +158,28 @@ void poll_serverthreads() // called once per mainloop-timeslice
}
break;
}
case 2: // wake readconfigsthread
#if 0
case 2: // wake readserverconfigsthread
{
readallconfigfiles = true;
readconfigsthread_sem.post();
recheckallserverconfigs = true;
nextserverconfig = 0;
readserverconfigsthread_sem->post();
stage++;
break;
}
case 3: // readconfigsthread
case 3: // readserverconfigsthread
{
stage++;
if(!recheckallserverconfigs)
{
if(serverconfigs.inrange(nextserverconfig))
{
serverconfigs[nextserverconfig]->process(); // processing of config files is done in mainloop, one at a time
nextserverconfig++;
}
else stage++;
}
break;
}
case 4: // pause worker threads for a while (restart once a minute)
#endif
{
if(servmillis - lastworkerthreadstart > 60 * 1000) stage = 0;
else if(numclients() == 0)
@@ -3842,16 +3846,6 @@ void sendworldstate()
if(demorecord) recordpackets = true; // enable after 'old' worldstate is sent
}
void rereadcfgs(void)
{
maprot.read();
ipblacklist.read();
geoiplist.read();
nickblacklist.read();
forbiddenlist.read();
passwords.read();
}
void loggamestatus(const char *reason)
{
int fragscore[2] = {0, 0}, flagscore[2] = {0, 0}, pnum[2] = {0, 0};
@@ -4049,7 +4043,7 @@ void serverslice(uint timeout) // main server update, called from cube main lo
if(!isdedicated) return; // below is network only
poll_serverthreads();
poll_serverthreads(); // read config and map files in the background, process the results in the main thread
serverms(sg->smode, numclients(), sg->minremain, sg->smapname, servmillis, serverhost->address, &mnum, &msend, &mrec, &cnum, &csend, &crec, SERVER_PROTOCOL_VERSION, sg->servdesc_current, sg->interm);
@@ -4065,7 +4059,6 @@ void serverslice(uint timeout) // main server update, called from cube main lo
if(servmillis - laststatus > 60 * 1000) // display bandwidth stats, useful for server ops
{
laststatus = servmillis;
if(!nonlocalclients) rereadcfgs(); // configs are read from actual files - only attempt to do that, when the server is empty
if(nonlocalclients || serverhost->totalSentData || serverhost->totalReceivedData)
{
if(nonlocalclients) loggamestatus(NULL);
@@ -4390,6 +4383,9 @@ void initserver(bool dedicated)
defformatstring(readmapslogfilename)("%sreadmaps_log.txt", scl.logfilepath);
sl_createthread(readmapsthread, (void *)path(readmapslogfilename));
readserverconfigsthread_sem = new sl_semaphore(0, NULL);
sl_createthread(readserverconfigsthread, NULL);
for(;;) serverslice(5);
}
}
+70 -37
View File
@@ -565,11 +565,16 @@ bool serverwritemap(const char *mapname, int mapsize, int cfgsize, int cfgsizegz
// server config files
void serverconfigfile::init(const char *name)
vector<serverconfigfile *> serverconfigs; // list of server config files, automatically polled in the background
volatile bool recheckallserverconfigs = false; // signal readserverconfigsthread to poll all files
sl_semaphore *readserverconfigsthread_sem = NULL; // sync readmapsthread with main thread
void serverconfigfile::init(const char *name, bool tracking)
{
copystring(filename, name);
path(filename);
read();
clear();
if(tracking) serverconfigs.add(this);
}
bool serverconfigfile::load()
@@ -581,6 +586,7 @@ bool serverconfigfile::load()
logline(ACLOG_INFO,"could not read config file '%s'", filename);
return false;
}
entropy_add_block((const uchar *)buf, filelen);
char *p;
if('\r' != '\n') // this is not a joke!
{
@@ -596,6 +602,41 @@ bool serverconfigfile::load()
return true;
}
bool serverconfigfile::isbusy()
{
return busy.getvalue() < 1;
}
void serverconfigfile::trypreload() // in readserverconfigsthread: load changed files into RAM
{
if(!buf && !isbusy() && getfilesize(filename) != filelen && !load()) buf = newstring("");
}
void serverconfigfile::process() // in main thread: refill data structures with preloaded file content
{
if(buf && !isbusy() && !busy.trywait())
{
clear();
if(strlen(buf)) read();
DELETEA(buf);
busy.post();
}
}
int readserverconfigsthread(void *data)
{
while(1)
{
while(!recheckallserverconfigs) readserverconfigsthread_sem->wait(); // wait without using the cpu
loopv(serverconfigs) serverconfigs[i]->trypreload();
entropy_save();
recheckallserverconfigs = false;
}
return 0;
}
// maprot.cfg
#define CONFIG_MAXPAR 6
@@ -619,12 +660,13 @@ struct servermaprot : serverconfigfile
servermaprot() : curcfgset(-1) {}
void clear()
{
configsets.shrink(0);
}
void read()
{
if(getfilesize(filename) == filelen) return;
configsets.shrink(0);
if(!load()) return;
const char *sep = ": ";
configset c;
int i, line = 0;
@@ -651,9 +693,7 @@ struct servermaprot : serverconfigfile
else logline(ACLOG_INFO," error in line %d, file %s", line, filename);
}
}
DELETEA(buf);
logline(ACLOG_INFO,"read %d map rotation entries from '%s'", configsets.length(), filename);
return;
}
int next(bool notify = true, bool nochange = false) // load next maprotation set
@@ -733,12 +773,13 @@ struct serveripcclist : serverconfigfile
return ipr1.ci - ipr2.ci;
}
void clear()
{
ipranges.shrink(0);
}
void read()
{
if(getfilesize(filename) == filelen) return;
ipranges.shrink(0);
if(!load()) return;
iprangecc ir;
int line = 0, errors = 0, concatd = 0;
char *l, *r, *p = buf;
@@ -760,7 +801,6 @@ struct serveripcclist : serverconfigfile
else
logline(ACLOG_INFO," error in line %d, file %s: failed to parse '%s'", line, filename, r ? r : l), errors++;
}
DELETEA(buf);
ipranges.sort(cmpiprange);
int orglength = ipranges.length();
loopv(ipranges)
@@ -806,13 +846,16 @@ struct serveripcclist : serverconfigfile
logline(ACLOG_INFO,"read %d (%d) IP list entries from '%s', %d errors, %d concatenated", ipranges.length(), orglength, filename, errors, concatd);
}
const char *check(enet_uint32 ip) // ip: host byte order
const char *check(enet_uint32 ip, bool lock = false) // ip: host byte order
{
if(lock) busy.wait();
iprangecc t, *res;
t.lr = ip;
t.ur = 0;
res = ipranges.search(&t, cmpipmatch);
return res ? printcomment(*res) : NULL;
const char *cc = res ? printcomment(*res) : NULL;
if(lock) busy.post();
return cc;
}
};
@@ -822,12 +865,13 @@ struct serveripblacklist : serverconfigfile
{
vector<iprange> ipranges;
void clear()
{
ipranges.shrink(0);
}
void read()
{
if(getfilesize(filename) == filelen) return;
ipranges.shrink(0);
if(!load()) return;
iprange ir;
int line = 0, errors = 0;
char *l, *r, *p = buf;
@@ -847,7 +891,6 @@ struct serveripblacklist : serverconfigfile
errors++;
}
}
DELETEA(buf);
ipranges.sort(cmpiprange);
int orglength = ipranges.length();
loopv(ipranges)
@@ -895,7 +938,7 @@ struct servernickblacklist : serverconfigfile
vector<blackline> blacklines;
vector<const char *> blfraglist;
void destroylists()
void clear()
{
whitelistranges.setsize(0);
enumeratek(whitelist, const char *, key, delete key);
@@ -906,10 +949,6 @@ struct servernickblacklist : serverconfigfile
void read()
{
if(getfilesize(filename) == filelen) return;
destroylists();
if(!load()) return;
const char *sep = " ";
int line = 1, errors = 0;
iprchain iprc;
@@ -972,7 +1011,6 @@ struct servernickblacklist : serverconfigfile
}
line++;
}
DELETEA(buf);
if(logcheck(ACLOG_VERBOSE))
{
logline(ACLOG_VERBOSE," nickname whitelist (%d entries):", whitelist.numelems);
@@ -1105,7 +1143,7 @@ struct serverforbiddenlist : serverconfigfile
int num;
char entries[100][2][FORBIDDENSIZE+1]; // 100 entries and 2 words per entry is more than enough
void initlist()
void clear()
{
num = 0;
memset(entries,'\0',2*100*(FORBIDDENSIZE+1));
@@ -1129,10 +1167,6 @@ struct serverforbiddenlist : serverconfigfile
void read()
{
if(getfilesize(filename) == filelen) return;
initlist();
if(!load()) return;
char *l, *p = buf;
logline(ACLOG_VERBOSE,"reading forbidden list '%s'", filename);
while(p < buf + filelen)
@@ -1140,7 +1174,6 @@ struct serverforbiddenlist : serverconfigfile
l = p; p += strlen(p) + 1;
addentry(l);
}
DELETEA(buf);
}
bool canspeech(char *s)
@@ -1187,12 +1220,13 @@ struct serverpasswords : serverconfigfile
serverconfigfile::init(name);
}
void clear()
{
adminpwds.shrink(staticpasses);
}
void read()
{
if(getfilesize(filename) == filelen) return;
adminpwds.shrink(staticpasses);
if(!load()) return;
pwddetail c;
const char *sep = " ";
int i, line = 1, par[ADMINPWD_MAXPAR];
@@ -1221,7 +1255,6 @@ struct serverpasswords : serverconfigfile
}
line++;
}
DELETEA(buf);
logline(ACLOG_INFO,"read %d admin passwords from '%s'", adminpwds.length() - staticpasses, filename);
}
+1 -1
View File
@@ -1018,7 +1018,7 @@ struct sl_semaphore
sl_semaphore(int init, int *errorcount); // init: initial semaphore value; errorcount: pointer to error counter for semaphore-related errors or NULL
~sl_semaphore();
void wait(); // blocks, until semaphore gets available
int trywait(); // returns 0, if semaphore was locked (like wait(), but returns !=0 instead of blocking)
int trywait(); // returns 0, if semaphore was successfully locked (like wait(), but returns !=0 instead of blocking)
int timedwait(int howlongmillis); // like trywait(), but waits for a litte before returning failure
int getvalue(); // returns current semaphore value
void post(); // increments (unlocks) semaphore