aboutsummaryrefslogtreecommitdiff
path: root/src/main.cc
diff options
context:
space:
mode:
authorfusion32 <marcopuzziello@gmail.com>2025-07-19 11:35:11 -0300
committerfusion32 <marcopuzziello@gmail.com>2025-07-19 11:35:11 -0300
commitd359c0af4874534efa810729d47f610a29687e93 (patch)
tree9a89b06444819e7e5cef84aa826682b3b01bfd03 /src/main.cc
parent638244fd078fe91971e57b1d06e4499268d99c42 (diff)
downloadgame-d359c0af4874534efa810729d47f610a29687e93.tar.gz
game-d359c0af4874534efa810729d47f610a29687e93.zip
THREADING: upgrade from LinuxThreads to NPTL
This was a required change to make the server run on a modern GLIBC version without resorting to shady shared libraries or compatibility tricks. It is very straightforward but we should always keep an eye out for possible bugs. There was also a problem with the Z loop on field sending functions that was causing the server to hang in an infinite loop and should now be fixed. With these changes it is now possible to login into the game for a split second before the client asserting.
Diffstat (limited to 'src/main.cc')
-rw-r--r--src/main.cc172
1 files changed, 103 insertions, 69 deletions
diff --git a/src/main.cc b/src/main.cc
index b838544..ddeda8b 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -14,39 +14,55 @@
#include <signal.h>
#include <sys/stat.h>
-#include <sys/time.h>
#include <fstream>
static bool BeADaemon = false;
static bool Reboot = false;
static bool SaveMapOn = false;
+static timer_t BeatTimer;
static int SigAlarmCounter = 0;
static int SigUsr1Counter = 0;
-static sighandler_t handler(int signr, sighandler_t sighandler){
- struct sigaction act;
- struct sigaction oldact;
+static sighandler_t SigHandler(int SigNr, sighandler_t Handler){
+ struct sigaction Action;
+ struct sigaction OldAction;
- act.sa_handler = sighandler;
+ Action.sa_handler = Handler;
// TODO(fusion): I feel we should probably use `sigfillset` specially for
// signals that share the same handler.
- sigemptyset(&act.sa_mask);
+ sigemptyset(&Action.sa_mask);
- if(signr == SIGALRM){
- act.sa_flags = SA_INTERRUPT;
+ if(SigNr == SIGALRM){
+ Action.sa_flags = SA_INTERRUPT;
}else{
- act.sa_flags = SA_RESTART;
+ Action.sa_flags = SA_RESTART;
}
- if(sigaction(signr, &act, &oldact) == 0){
- return oldact.sa_handler;
+ if(sigaction(SigNr, &Action, &OldAction) == 0){
+ return OldAction.sa_handler;
}else{
return SIG_ERR;
}
}
+static void SigBlock(int SigNr){
+ sigset_t Set;
+ sigemptyset(&Set);
+ sigaddset(&Set, SigNr);
+ if(sigprocmask(SIG_BLOCK, &Set, NULL) == -1){
+ error("SigBlock: Failed to block signal %d (%s): (%d) %s\n",
+ SigNr, sigdescr_np(SigNr), errno, strerrordesc_np(errno));
+ }
+}
+
+static void SigWaitAny(void){
+ sigset_t Set;
+ sigemptyset(&Set);
+ sigsuspend(&Set);
+}
+
static void SigHupHandler(int signr){
// no-op (?)
}
@@ -60,12 +76,12 @@ static void DefaultHandler(int signr){
print(1, "DefaultHandler: Beende Game-Server (SigNr. %d: %s).\n",
signr, sigdescr_np(signr));
- handler(SIGINT, SIG_IGN);
- handler(SIGQUIT, SIG_IGN);
- handler(SIGTERM, SIG_IGN);
- handler(SIGXCPU, SIG_IGN);
- handler(SIGXFSZ, SIG_IGN);
- handler(SIGPWR, SIG_IGN);
+ SigHandler(SIGINT, SIG_IGN);
+ SigHandler(SIGQUIT, SIG_IGN);
+ SigHandler(SIGTERM, SIG_IGN);
+ SigHandler(SIGXCPU, SIG_IGN);
+ SigHandler(SIGXFSZ, SIG_IGN);
+ SigHandler(SIGPWR, SIG_IGN);
SaveMapOn = (signr == SIGQUIT) || (signr == SIGTERM) || (signr == SIGPWR);
if(signr == SIGTERM){
@@ -91,55 +107,72 @@ static void ErrorHandler(int signr){
#endif
static void InitSignalHandler(void){
- int count = 0;
- count += (handler(SIGHUP, SigHupHandler) != SIG_ERR);
- count += (handler(SIGINT, DefaultHandler) != SIG_ERR);
- count += (handler(SIGQUIT, DefaultHandler) != SIG_ERR);
- count += (handler(SIGABRT, SigAbortHandler) != SIG_ERR);
- count += (handler(SIGUSR1, SIG_IGN) != SIG_ERR);
- count += (handler(SIGUSR2, SIG_IGN) != SIG_ERR);
- count += (handler(SIGPIPE, SIG_IGN) != SIG_ERR);
- count += (handler(SIGALRM, SIG_IGN) != SIG_ERR);
- count += (handler(SIGTERM, DefaultHandler) != SIG_ERR);
- count += (handler(SIGSTKFLT, SIG_IGN) != SIG_ERR);
- count += (handler(SIGCHLD, SIG_IGN) != SIG_ERR);
- count += (handler(SIGTSTP, SIG_IGN) != SIG_ERR);
- count += (handler(SIGTTIN, SIG_IGN) != SIG_ERR);
- count += (handler(SIGTTOU, SIG_IGN) != SIG_ERR);
- count += (handler(SIGURG, SIG_IGN) != SIG_ERR);
- count += (handler(SIGXCPU, DefaultHandler) != SIG_ERR);
- count += (handler(SIGXFSZ, DefaultHandler) != SIG_ERR);
- count += (handler(SIGVTALRM, SIG_IGN) != SIG_ERR);
- count += (handler(SIGWINCH, SIG_IGN) != SIG_ERR);
- count += (handler(SIGPOLL, SIG_IGN) != SIG_ERR);
- count += (handler(SIGPWR, DefaultHandler) != SIG_ERR);
- print(1, "InitSignalHandler: %d Signalhandler eingerichtet (Soll=%d)\n", count, 0x1c);
+ int Count = 0;
+ Count += (SigHandler(SIGHUP, SigHupHandler) != SIG_ERR);
+ Count += (SigHandler(SIGINT, DefaultHandler) != SIG_ERR);
+ Count += (SigHandler(SIGQUIT, DefaultHandler) != SIG_ERR);
+ Count += (SigHandler(SIGABRT, SigAbortHandler) != SIG_ERR);
+ Count += (SigHandler(SIGUSR1, SIG_IGN) != SIG_ERR);
+ Count += (SigHandler(SIGUSR2, SIG_IGN) != SIG_ERR);
+ Count += (SigHandler(SIGPIPE, SIG_IGN) != SIG_ERR);
+ Count += (SigHandler(SIGALRM, SIG_IGN) != SIG_ERR);
+ Count += (SigHandler(SIGTERM, DefaultHandler) != SIG_ERR);
+ Count += (SigHandler(SIGSTKFLT, SIG_IGN) != SIG_ERR);
+ Count += (SigHandler(SIGCHLD, SIG_IGN) != SIG_ERR);
+ Count += (SigHandler(SIGTSTP, SIG_IGN) != SIG_ERR);
+ Count += (SigHandler(SIGTTIN, SIG_IGN) != SIG_ERR);
+ Count += (SigHandler(SIGTTOU, SIG_IGN) != SIG_ERR);
+ Count += (SigHandler(SIGURG, SIG_IGN) != SIG_ERR);
+ Count += (SigHandler(SIGXCPU, DefaultHandler) != SIG_ERR);
+ Count += (SigHandler(SIGXFSZ, DefaultHandler) != SIG_ERR);
+ Count += (SigHandler(SIGVTALRM, SIG_IGN) != SIG_ERR);
+ Count += (SigHandler(SIGWINCH, SIG_IGN) != SIG_ERR);
+ Count += (SigHandler(SIGPOLL, SIG_IGN) != SIG_ERR);
+ Count += (SigHandler(SIGPWR, DefaultHandler) != SIG_ERR);
+ print(1, "InitSignalHandler: %d Signalhandler eingerichtet (Soll=%d)\n", Count, 0x1c);
}
static void ExitSignalHandler(void){
// no-op
}
-static void SigAlarmHandler(int signr){
- SigAlarmCounter += 1;
-
- struct itimerval new_timer = {};
- struct itimerval old_timer = {};
- new_timer.it_value.tv_usec = Beat * 1000;
- setitimer(ITIMER_REAL, &new_timer, &old_timer);
+static void SigAlarmHandler(int SigNr){
+ SigAlarmCounter += (1 + timer_getoverrun(BeatTimer));
}
static void InitTime(void){
+ ASSERT(Beat > 0);
SigAlarmCounter = 0;
- handler(SIGALRM, SigAlarmHandler);
- SigAlarmHandler(SIGALRM);
+ SigHandler(SIGALRM, SigAlarmHandler);
+
+ struct sigevent SigEvent = {};
+ SigEvent.sigev_notify = SIGEV_THREAD_ID;
+ SigEvent.sigev_signo = SIGALRM;
+ SigEvent.sigev_notify_thread_id = gettid();
+ if(timer_create(CLOCK_MONOTONIC, &SigEvent, &BeatTimer) == -1){
+ error("InitTime: Failed to create beat timer: (%d) %s\n",
+ errno, strerrordesc_np(errno));
+ throw "cannot create beat timer";
+ }
+
+ struct itimerspec TimerSpec = {};
+ TimerSpec.it_interval.tv_sec = Beat / 1000;
+ TimerSpec.it_interval.tv_nsec = (Beat % 1000) * 1000000;
+ TimerSpec.it_value = TimerSpec.it_interval;
+ if(timer_settime(BeatTimer, 0, &TimerSpec, NULL) == -1){
+ error("InitTime: Failed to start beat timer: (%d) %s\n",
+ errno, strerrordesc_np(errno));
+ throw "cannot start beat timer";
+ }
}
static void ExitTime(void){
- struct itimerval new_timer = {};
- struct itimerval old_timer = {};
- setitimer(ITIMER_REAL, &new_timer, &old_timer);
- handler(SIGALRM, SIG_IGN);
+ if(timer_delete(BeatTimer) == -1){
+ error("ExitTime: Failed to delete beat timer: (%d) %s\n",
+ errno, strerrordesc_np(errno));
+ }
+
+ SigHandler(SIGALRM, SIG_IGN);
}
static void UnlockGame(void){
@@ -178,7 +211,7 @@ static void LockGame(void){
{
std::ofstream OutputFile(FileName, std::ios_base::out | std::ios_base::trunc);
- OutputFile << (int)getpid();
+ OutputFile << getpid();
}
atexit(UnlockGame);
@@ -420,28 +453,29 @@ static void SigUsr1Handler(int signr){
}
static void LaunchGame(void){
+ SaveMapOn = true;
SigUsr1Counter = 0;
- handler(SIGUSR1, SigUsr1Handler);
+ SigAlarmCounter = 0;
+
+ SigBlock(SIGUSR1);
+ SigHandler(SIGUSR1, SigUsr1Handler);
StartGame();
- print(1, "LaunchGame: Game-Server ist bereit (Pid=%d).\n", getpid());
+ print(1, "LaunchGame: Game-Server ist bereit (Pid=%d, Tid=%d).\n", getpid(), gettid());
- // IMPORTANT(fusion): The whole design of the server is to run across a few
- // different processes and communicate via shared memory and signals. Each
- // of these processes are single threaded, meaning that signal handlers are
- // executed concurrently on the SAME thread. You'd still require to synchronize
- // access to large structures to avoid race conditions BUT accessing a few
- // independent integers and booleans like we're doing with `SigAlarmCounter`,
- // `SigUsr1Counter`, and `SaveMapOn` is perfectly safe.
+ // IMPORTANT(fusion): In general signal handlers can execute on any thread in
+ // the process group but the design of the server is to use signals directed
+ // at different threads to communicate certain events (see `CommunicationThread`
+ // for example).
+ // Even if that wasn't the case, loads/stores on x86 are always ATOMIC and when
+ // there is a single writer (signal handlers), even a read-modify-write will be
+ // atomic.
+ // This is to say, there should be no problem with reading from `SigUsr1Counter`,
+ // `SigAlarmCounter`, or `SaveMapOn`, which may be modified from signal handlers.
- SaveMapOn = true;
- SigAlarmCounter = 0;
while(GameRunning()){
- // TODO(fusion): `sigblock` and `sigpause` are deprecated in favour of
- // `sigprocmask` and `sigsuspend`.
- sigblock(sigmask(SIGUSR1));
while(SigUsr1Counter == 0 && SigAlarmCounter == 0){
- sigpause(0);
+ SigWaitAny();
}
if(SigUsr1Counter > 0){