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:
+8
-3
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user