aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO.md5
-rw-r--r--src/common.hh13
-rw-r--r--src/communication.cc58
-rw-r--r--src/connections.cc62
-rw-r--r--src/connections.hh7
-rw-r--r--src/crplayer.cc16
-rw-r--r--src/main.cc172
-rw-r--r--src/moveuse.cc2
-rw-r--r--src/reader.cc4
-rw-r--r--src/receiving.cc7
-rw-r--r--src/sending.cc11
-rw-r--r--src/shm.cc18
12 files changed, 248 insertions, 127 deletions
diff --git a/TODO.md b/TODO.md
index 165acd0..d1352f7 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1,11 +1,6 @@
## Synchronization
I'm not sure whether synchronization is done properly. The implementation of the first few `crplayer.cc` functions left me with a taste of race conditions although integer loads on x86 are generally atomic.
-## Threading
-After some initial dive into `communication.cc`, it has become clear that the original codebase relied on `LinuxThreads` which, among its quirks, assigns different PIDs to different threads, explaining the usage of `getpid()`. It also seems it could lack automatically managed thread stacks which explains `CommunicationThreadStacks`.
-
-We'll have to address this BEFORE trying to run the server.
-
## TODO (LEFTOVER)
- Include `x == 0xFFFF` inside `CheckVisibility`?
- The map container uses type id zero but it seems to also be used as a "no type"
diff --git a/src/common.hh b/src/common.hh
index b97de3c..971dca2 100644
--- a/src/common.hh
+++ b/src/common.hh
@@ -78,12 +78,16 @@ typedef size_t usize;
// Nevertheless, we should focus on getting it working as intended, on the target
// platform (which is Linux 32-bit) before attempting to refine it.
STATIC_ASSERT(OS_LINUX);
-STATIC_ASSERT(sizeof(bool) == 1);
-STATIC_ASSERT(sizeof(int) == 4);
-//STATIC_ASSERT(sizeof(void*) == 4);
#include <errno.h>
#include <unistd.h>
+// NOTE(fusion): This is the member name for the thread id in `struct sigevent`
+// when `sigev_notify` is `SIGEV_THREAD_ID` but for whatever reason glibc doesn't
+// define it.
+#ifndef sigev_notify_thread_id
+# define sigev_notify_thread_id _sigev_un._tid
+#endif
+
// shm.cc
// =============================================================================
void StartGame(void);
@@ -93,7 +97,8 @@ bool LoginAllowed(void);
bool GameRunning(void);
bool GameStarting(void);
bool GameEnding(void);
-pid_t GetGameThreadPID(void);
+pid_t GetGameProcessID(void);
+pid_t GetGameThreadID(void);
int GetPrintlogPosition(void);
char *GetPrintlogLine(int Line);
void IncrementObjectCounter(void);
diff --git a/src/communication.cc b/src/communication.cc
index ffaa3a8..cdfb6c7 100644
--- a/src/communication.cc
+++ b/src/communication.cc
@@ -26,7 +26,7 @@
static const int TERMINALVERSION[] = {770, 770, 770};
static int TCPSocket;
static ThreadHandle AcceptorThread;
-static pid_t AcceptorThreadPID;
+static pid_t AcceptorThreadID;
static int ActiveConnections;
static Semaphore RSAMutex(1);
@@ -65,7 +65,7 @@ void GetCommunicationThreadStack(int *StackNumber, void **Stack){
for(int i = 0; i < NumberOfFreeCommunicationThreadStacks; i += 1){
int FreeStack = FreeCommunicationThreadStacks[i];
if(LastUsingCommunicationThread[FreeStack] == 0
- || kill(LastUsingCommunicationThread[FreeStack], 0) == -1){
+ || tgkill(GetGameProcessID(), LastUsingCommunicationThread[FreeStack], 0) == -1){
// NOTE(fusion): A little swap and pop action.
NumberOfFreeCommunicationThreadStacks -= 1;
FreeCommunicationThreadStacks[i] = FreeCommunicationThreadStacks[NumberOfFreeCommunicationThreadStacks];
@@ -79,7 +79,7 @@ void GetCommunicationThreadStack(int *StackNumber, void **Stack){
}
void AttachCommunicationThreadStack(int StackNumber){
- LastUsingCommunicationThread[StackNumber] = getpid();
+ LastUsingCommunicationThread[StackNumber] = gettid();
}
void ReleaseCommunicationThreadStack(int StackNumber){
@@ -663,8 +663,9 @@ bool DrainSocket(TConnection *Connection, int Size){
bool CallGameThread(TConnection *Connection){
if(GameRunning()){
Connection->WaitingForACK = true;
- if(kill(GetGameThreadPID(), SIGUSR1) != 0){
- error("CallGameThread: Can't send SIGUSR1 to pid %d\n", GetGameThreadPID());
+ if(tgkill(GetGameProcessID(), GetGameThreadID(), SIGUSR1) == -1){
+ error("CallGameThread: Can't send SIGUSR1 to thread %d/%d: (%d) %s\n",
+ GetGameProcessID(), GetGameThreadID(), errno, strerrordesc_np(errno));
SendLoginMessage(Connection, 20,
"The server is not online.\nPlease try again later.", -1);
return false;
@@ -880,7 +881,7 @@ TPlayerData *PerformRegistration(TConnection *Connection, char *PlayerName,
return NULL;
}
- bool Locked = PlayerData->Locked == getpid();
+ bool Locked = (PlayerData->Locked == gettid());
// TODO(fusion): How come this isn't a race condition? Perhaps these are
// constant and can only change when loaded from the database?
@@ -1037,7 +1038,7 @@ bool HandleLogin(TConnection *Connection){
return false;
}
- bool SlotLocked = (Slot->Locked == getpid());
+ bool SlotLocked = (Slot->Locked == gettid());
// NOTE(fusion): These checks would have been already made if the player was
// in the waiting list so they'd be redundant.
@@ -1183,7 +1184,7 @@ bool ReceiveCommand(TConnection *Connection){
}
if(Connection->State == CONNECTION_CONNECTED){
- alarm(0);
+ Connection->StopLoginTimer();
Connection->InDataSize = Size;
if(!HandleLogin(Connection)){
return false;
@@ -1219,8 +1220,8 @@ bool ReceiveCommand(TConnection *Connection){
}
// NOTE(fusion): We set `SigIOPending` here to signal that there could be more
- // packets already queued up, so `CommunicationThread` can attempt to read them
- // without waiting for another `SIGIO`.
+ // packets already queued up, in which case `CommunicationThread` will attempt
+ // to read without waiting for another `SIGIO`.
// The only way to know there is no more data on a socket's receive buffer is
// when `read` returns `EAGAIN` which is handled when `ReadFromSocket` returns
// a negative value.
@@ -1252,10 +1253,15 @@ void CommunicationThread(int Socket){
return;
}
+ ASSERT(Connection->ThreadID == gettid());
Connection->Connect(Socket);
Connection->WaitingForACK = false;
- if(fcntl(Socket, F_SETOWN, getpid()) == -1){
- error("CommunicationThread: F_SETOWN fehlgeschlagen für Socket %d.\n", Socket);
+
+ struct f_owner_ex FOwnerEx = {};
+ FOwnerEx.type = F_OWNER_TID;
+ FOwnerEx.pid = Connection->ThreadID;
+ if(fcntl(Socket, F_SETOWN_EX, &FOwnerEx) == -1){
+ error("CommunicationThread: F_SETOWN_EX fehlgeschlagen für Socket %d.\n", Socket);
if(close(Socket) == -1){
error("CommunicationThread: Fehler %d beim Schließen der Socket (2).\n", errno);
}
@@ -1275,7 +1281,14 @@ void CommunicationThread(int Socket){
sigset_t SignalSet;
sigfillset(&SignalSet);
sigprocmask(SIG_SETMASK, &SignalSet, NULL);
- alarm(5);
+ if(!Connection->SetLoginTimer(5)){
+ error("CommunicationThread: Failed to set login timer.\n");
+ if(close(Socket) == -1){
+ error("CommunicationThread: Fehler %d beim Schließen der Socket (4).\n", errno);
+ }
+ Connection->Free();
+ return;
+ }
if(!ReceiveCommand(Connection)){
Connection->Close(true);
@@ -1315,7 +1328,8 @@ void CommunicationThread(int Socket){
}
case SIGALRM:{
- // NOTE(fusion): Login deadline.
+ // NOTE(fusion): Login timeout.
+ Connection->StopLoginTimer();
if(Connection->State == CONNECTION_CONNECTED){
print(2, "Login-TimeOut für Socket %d.\n", Socket);
Connection->Close(false);
@@ -1380,7 +1394,7 @@ int HandleConnection(void *Data){
// =============================================================================
bool OpenSocket(void){
print(1, "Starte Game-Server...\n");
- print(1, "Pid %d - horche an Port %d\n", getpid(), GamePort);
+ print(1, "Pid=%d, Tid=%d - horche an Port %d\n", getpid(), gettid(), GamePort);
TCPSocket = socket(AF_INET, SOCK_STREAM, 0);
if(TCPSocket == -1){
error("LaunchServer: Kann Socket nicht öffnen.\n");
@@ -1422,7 +1436,7 @@ bool OpenSocket(void){
}
int AcceptorThreadLoop(void *Unused){
- AcceptorThreadPID = getpid();
+ AcceptorThreadID = gettid();
print(1, "Warte auf Clients...\n");
while(GameRunning()){
int Socket = accept(TCPSocket, NULL, NULL);
@@ -1485,7 +1499,7 @@ int AcceptorThreadLoop(void *Unused){
}
}
- AcceptorThreadPID = 0;
+ AcceptorThreadID = 0;
if(ActiveConnections > 0){
print(3, "Warte auf Beendigung von %d Communication-Threads...\n", ActiveConnections);
while(ActiveConnections > 0){
@@ -1518,7 +1532,7 @@ void InitCommunication(void){
WaitinglistHead = NULL;
TCPSocket = -1;
AcceptorThread = INVALID_THREAD_HANDLE;
- AcceptorThreadPID = 0;
+ AcceptorThreadID = 0;
ActiveConnections = 0;
QueryManagerConnectionPool.init();
@@ -1539,10 +1553,12 @@ void InitCommunication(void){
}
void ExitCommunication(void){
+ // NOTE(fusion): `SIGHUP` is used to signal the connection thread to close
+ // the connection and terminate.
print(3, "Beende alle Verbindungen...\n");
TConnection *Connection = GetFirstConnection();
while(Connection != NULL){
- kill(Connection->GetPID(), SIGHUP);
+ tgkill(GetGameProcessID(), Connection->GetThreadID(), SIGHUP);
Connection = GetNextConnection();
}
@@ -1557,8 +1573,8 @@ void ExitCommunication(void){
}
if(AcceptorThread != INVALID_THREAD_HANDLE){
- if(AcceptorThreadPID != 0){
- kill(AcceptorThreadPID, SIGHUP);
+ if(AcceptorThreadID != 0){
+ tgkill(GetGameProcessID(), AcceptorThreadID, SIGHUP);
}
JoinThread(AcceptorThread);
AcceptorThread = INVALID_THREAD_HANDLE;
diff --git a/src/connections.cc b/src/connections.cc
index cef9fa6..84daec6 100644
--- a/src/connections.cc
+++ b/src/connections.cc
@@ -79,13 +79,64 @@ void TConnection::EmergencyPing(void){
}
}
-pid_t TConnection::GetPID(void){
+pid_t TConnection::GetThreadID(void){
if(this->State == CONNECTION_FREE){
- error("TConnection::GetPID: Verbindung ist nicht zugewiesen.\n");
+ error("TConnection::GetThreadID: Verbindung ist nicht zugewiesen.\n");
return 0;
}
- return this->PID;
+ return this->ThreadID;
+}
+
+bool TConnection::SetLoginTimer(int Timeout){
+ if(this->State == CONNECTION_FREE){
+ error("TConnection::SetLoginTimer: Verbindung ist nicht zugewiesen.\n");
+ return false;
+ }
+
+ if(this->LoginTimer != 0){
+ error("TConnection::SetLoginTimer: Timer already set.\n");
+ return false;
+ }
+
+ struct sigevent SigEvent = {};
+ SigEvent.sigev_notify = SIGEV_THREAD_ID;
+ SigEvent.sigev_signo = SIGALRM;
+ SigEvent.sigev_notify_thread_id = this->ThreadID;
+ if(timer_create(CLOCK_MONOTONIC, &SigEvent, &this->LoginTimer) == -1){
+ error("TConnection::SetLoginTimer: Failed to create timer: (%d) %s\n",
+ errno, strerrordesc_np(errno));
+ return false;
+ }
+
+ struct itimerspec TimerSpec = {};
+ TimerSpec.it_value.tv_sec = Timeout;
+ if(timer_settime(this->LoginTimer, 0, &TimerSpec, NULL) == -1){
+ error("TConnection::SetLoginTimer: Failed to start timer: (%d) %s\n",
+ errno, strerrordesc_np(errno));
+ return false;
+ }
+
+ return true;
+}
+
+void TConnection::StopLoginTimer(void){
+ if(this->State == CONNECTION_FREE){
+ error("TConnection::SetLoginTimer: Verbindung ist nicht zugewiesen.\n");
+ return;
+ }
+
+ if(this->LoginTimer == 0){
+ error("TConnection::StopLoginTimer: Timer not set.\n");
+ return;
+ }
+
+ if(timer_delete(this->LoginTimer) == -1){
+ error("TConnection::StopLoginTimer: Failed to delete timer: (%d) %s\n",
+ errno, strerrordesc_np(errno));
+ }
+
+ this->LoginTimer = 0;
}
int TConnection::GetSocket(void){
@@ -116,7 +167,8 @@ void TConnection::Assign(void){
}
this->State = CONNECTION_ASSIGNED;
- this->PID = getpid();
+ this->ThreadID = gettid();
+ this->LoginTimer = 0;
}
void TConnection::Connect(int Socket){
@@ -264,7 +316,7 @@ void TConnection::Disconnect(void){
this->ClearKnownCreatureTable(true);
this->ConnectionIsOk = false;
this->State = CONNECTION_DISCONNECTED;
- kill(this->PID, SIGHUP);
+ tgkill(GetGameProcessID(), this->ThreadID, SIGHUP);
}
TPlayer *TConnection::GetPlayer(void){
diff --git a/src/connections.hh b/src/connections.hh
index d4f0755..5489d18 100644
--- a/src/connections.hh
+++ b/src/connections.hh
@@ -154,7 +154,9 @@ struct TConnection {
void Process(void);
void ResetTimer(int Command);
void EmergencyPing(void);
- pid_t GetPID(void);
+ pid_t GetThreadID(void);
+ bool SetLoginTimer(int Timeout);
+ void StopLoginTimer(void);
int GetSocket(void);
const char *GetIPAddress(void);
void Free(void);
@@ -203,7 +205,8 @@ struct TConnection {
TConnection *NextSendingConnection;
uint32 RandomSeed;
CONNECTIONSTATE State;
- pid_t PID;
+ pid_t ThreadID;
+ timer_t LoginTimer;
int Socket;
char IPAddress[16];
TXTEASymmetricKey SymmetricKey;
diff --git a/src/crplayer.cc b/src/crplayer.cc
index dce7ad2..8ecc926 100644
--- a/src/crplayer.cc
+++ b/src/crplayer.cc
@@ -732,7 +732,7 @@ void TPlayer::TakeOver(TConnection *Connection){
return;
}
- if(PlayerData->Locked != getpid()){
+ if(PlayerData->Locked != gettid()){
error("TPlayer::TakeOver: PlayerData-Slot ist nicht korrekt gesperrt (%d).\n", PlayerData->Locked);
}
@@ -2639,7 +2639,7 @@ void FreePlayerPoolSlot(TPlayerData *Slot){
return;
}
- if(Slot->Locked != 0 && Slot->Locked != getpid()){
+ if(Slot->Locked != 0 && Slot->Locked != gettid()){
error("FreePlayerPoolSlot: Slot ist von einem anderen Thread gesperrt.\n");
return;
}
@@ -2693,7 +2693,7 @@ TPlayerData *AssignPlayerPoolSlot(uint32 CharacterID, bool DontWait){
}
if(Slot->Locked == 0){
- Slot->Locked = getpid();
+ Slot->Locked = gettid();
PlayerDataPoolMutex.up();
return Slot;
}
@@ -2753,7 +2753,7 @@ TPlayerData *AssignPlayerPoolSlot(uint32 CharacterID, bool DontWait){
memset(Slot, 0, sizeof(TPlayerData));
Slot->CharacterID = CharacterID;
- Slot->Locked = getpid();
+ Slot->Locked = gettid();
PlayerDataPoolMutex.up();
print(3, "Lade Daten für Spieler %u.\n", CharacterID);
@@ -2792,7 +2792,7 @@ TPlayerData *AttachPlayerPoolSlot(uint32 CharacterID, bool DontWait){
}
if(Slot->Locked == 0){
- Slot->Locked = getpid();
+ Slot->Locked = gettid();
PlayerDataPoolMutex.up();
return Slot;
}
@@ -2817,7 +2817,7 @@ void AttachPlayerPoolSlot(TPlayerData *Slot, bool DontWait){
while(true){
PlayerDataPoolMutex.down();
if(Slot->Locked == 0){
- Slot->Locked = getpid();
+ Slot->Locked = gettid();
PlayerDataPoolMutex.up();
return;
}
@@ -2882,7 +2882,7 @@ void ReleasePlayerPoolSlot(TPlayerData *Slot){
return;
}
- if(Slot->Locked != getpid()){
+ if(Slot->Locked != gettid()){
error("ReleasePlayerPoolSlot: Slot ist von einem anderen Thread gesperrt.\n");
return;
}
@@ -2908,7 +2908,7 @@ void SavePlayerPoolSlots(void){
}
AttachPlayerPoolSlot(Slot, true);
- if(Slot->Locked == getpid()){
+ if(Slot->Locked == gettid()){
SavePlayerPoolSlot(Slot);
ReleasePlayerPoolSlot(Slot);
}
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){
diff --git a/src/moveuse.cc b/src/moveuse.cc
index cd03bd8..81fa948 100644
--- a/src/moveuse.cc
+++ b/src/moveuse.cc
@@ -861,7 +861,7 @@ void SendMails(TPlayerData *PlayerData){
return;
}
- if(PlayerData->Locked != getpid()){
+ if(PlayerData->Locked != gettid()){
error("SendMails: Slot ist nicht vom GameThread gesperrt.\n");
return;
}
diff --git a/src/reader.cc b/src/reader.cc
index 09d739b..a29b35b 100644
--- a/src/reader.cc
+++ b/src/reader.cc
@@ -135,11 +135,11 @@ void ProcessLoadCharacterOrder(uint32 CharacterID){
break;
}
- if(Slot->Locked == GetGameThreadPID()){
+ if(Slot->Locked == GetGameThreadID()){
break;
}
- if(Slot->Locked == getpid()){
+ if(Slot->Locked == gettid()){
IncreasePlayerPoolSlotSticky(Slot);
ReleasePlayerPoolSlot(Slot);
CharacterReply(CharacterID);
diff --git a/src/receiving.cc b/src/receiving.cc
index 147f085..6cf6609 100644
--- a/src/receiving.cc
+++ b/src/receiving.cc
@@ -1800,9 +1800,12 @@ void ReceiveData(void){
if(Connection->Live() && Connection->WaitingForACK){
ReceiveData(Connection);
Connection->WaitingForACK = false;
- // TODO(fusion): It could have been disconnected inside `ReceiveData`?
+ // NOTE(fusion): `SIGUSR1` is used to signal the connection thread
+ // that we parsed all received data and that it may resume reading.
+ // We check if the connection is still live because it may have been
+ // disconnected inside `ReceiveData`.
if(Connection->Live()){
- kill(Connection->GetPID(), SIGUSR1);
+ tgkill(GetGameProcessID(), Connection->GetThreadID(), SIGUSR1);
}
}
Connection = GetNextConnection();
diff --git a/src/sending.cc b/src/sending.cc
index 970439f..3b22cc3 100644
--- a/src/sending.cc
+++ b/src/sending.cc
@@ -20,8 +20,10 @@ void SendAll(void){
while(Connection != NULL){
if(Connection->WillingToSend){
Connection->WillingToSend = false;
+ // NOTE(fusion): `SIGUSR2` is used to signal the connection thread
+ // that there is pending data in the connection's output buffer.
if(Connection->Live() && Connection->NextToCommit > Connection->NextToSend){
- kill(Connection->GetPID(), SIGUSR2);
+ tgkill(GetGameProcessID(), Connection->GetThreadID(), SIGUSR2);
}
}else{
error("SendAll: Verbindung ist nicht sendewillig.\n");
@@ -274,6 +276,7 @@ void SendMapPoint(TConnection *Connection, int x, int y, int z){
while(Obj != NONE && ObjCount < MAX_OBJECTS_PER_POINT){
SendMapObject(Connection, Obj);
Obj = Obj.getNextObject();
+ ObjCount += 1;
}
}
Skip += 1;
@@ -445,7 +448,7 @@ void SendFullScreen(TConnection *Connection){
SendByte(Connection, (uint8)PlayerZ);
Skip = -1;
- for(int PointZ = StartZ; PointZ != EndZ; EndZ += StepZ){
+ for(int PointZ = StartZ; PointZ != EndZ; PointZ += StepZ){
int ZOffset = (PlayerZ - PointZ);
for(int PointX = MinX; PointX <= MaxX; PointX += 1)
for(int PointY = MinY; PointY <= MaxY; PointY += 1){
@@ -499,7 +502,7 @@ void SendRow(TConnection *Connection, int Direction){
}
Skip = -1;
- for(int PointZ = StartZ; PointZ != EndZ; EndZ += StepZ){
+ for(int PointZ = StartZ; PointZ != EndZ; PointZ += StepZ){
int ZOffset = (PlayerZ - PointZ);
for(int PointX = MinX; PointX <= MaxX; PointX += 1)
for(int PointY = MinY; PointY <= MaxY; PointY += 1){
@@ -561,7 +564,7 @@ void SendFloors(TConnection *Connection, bool Up){
int MaxY = MinY + Connection->TerminalHeight - 1;
Skip = -1;
- for(int PointZ = StartZ; PointZ != EndZ; EndZ += StepZ){
+ for(int PointZ = StartZ; PointZ != EndZ; PointZ += StepZ){
int ZOffset = (PlayerZ - PointZ);
for(int PointX = MinX; PointX <= MaxX; PointX += 1)
for(int PointY = MinY; PointY <= MaxY; PointY += 1){
diff --git a/src/shm.cc b/src/shm.cc
index 7a63eae..98dd526 100644
--- a/src/shm.cc
+++ b/src/shm.cc
@@ -23,7 +23,8 @@ struct TSharedMemory {
int PrintBufferPosition;
char PrintBuffer[200][128];
GAMESTATE GameState;
- pid_t GameThreadPID;
+ pid_t GameProcessID;
+ pid_t GameThreadID;
};
static TSharedMemory *SHM = NULL;
@@ -101,10 +102,18 @@ bool GameEnding(void){
return Result;
}
-pid_t GetGameThreadPID(void){
+pid_t GetGameProcessID(void){
pid_t Pid = 0;
if(SHM != NULL){
- Pid = SHM->GameThreadPID;
+ Pid = SHM->GameProcessID;
+ }
+ return Pid;
+}
+
+pid_t GetGameThreadID(void){
+ pid_t Pid = 0;
+ if(SHM != NULL){
+ Pid = SHM->GameThreadID;
}
return Pid;
}
@@ -372,7 +381,8 @@ void InitSHM(bool Verbose){
SHM->PrintBufferPosition = 1;
SHM->GameState = GAME_STARTING;
- SHM->GameThreadPID = getpid();
+ SHM->GameProcessID = getpid();
+ SHM->GameThreadID = gettid();
}
void ExitSHM(void){