summaryrefslogtreecommitdiff
path: root/src/common/thread.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/thread.c')
-rw-r--r--src/common/thread.c317
1 files changed, 317 insertions, 0 deletions
diff --git a/src/common/thread.c b/src/common/thread.c
new file mode 100644
index 000000000..315b310b2
--- /dev/null
+++ b/src/common/thread.c
@@ -0,0 +1,317 @@
+//
+// Basic Threading abstraction (for pthread / win32 based systems)
+//
+// Author: Florian Wilkemeyer <fw@f-ws.de>
+//
+// Copyright (c) rAthena Project (www.rathena.org) - Licensed under GNU GPL
+// For more information, see LICENCE in the main folder
+
+#ifdef WIN32
+#include "../common/winapi.h"
+#define getpagesize() 4096 // @TODO: implement this properly (GetSystemInfo .. dwPageSize..). (Atm as on all supported win platforms its 4k its static.)
+#define __thread __declspec( thread )
+#else
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <pthread.h>
+#include <sched.h>
+#endif
+
+#include "cbasetypes.h"
+#include "malloc.h"
+#include "showmsg.h"
+#include "thread.h"
+
+// When Compiling using MSC (on win32..) we know we have support in any case!
+#ifdef _MSC_VER
+#define HAS_TLS
+#endif
+
+
+#define RA_THREADS_MAX 64
+
+struct rAthread {
+ unsigned int myID;
+
+ RATHREAD_PRIO prio;
+ rAthreadProc proc;
+ void *param;
+
+ #ifdef WIN32
+ HANDLE hThread;
+ #else
+ pthread_t hThread;
+ #endif
+};
+
+
+#ifdef HAS_TLS
+__thread int g_rathread_ID = -1;
+#endif
+
+
+///
+/// Subystem Code
+///
+static struct rAthread l_threads[RA_THREADS_MAX];
+
+void rathread_init(){
+ register unsigned int i;
+ memset(&l_threads, 0x00, RA_THREADS_MAX * sizeof(struct rAthread) );
+
+ for(i = 0; i < RA_THREADS_MAX; i++){
+ l_threads[i].myID = i;
+ }
+
+ // now lets init thread id 0, which represnts the main thread
+#ifdef HAS_TLS
+ g_rathread_ID = 0;
+#endif
+ l_threads[0].prio = RAT_PRIO_NORMAL;
+ l_threads[0].proc = (rAthreadProc)0xDEADCAFE;
+
+}//end: rathread_init()
+
+
+
+void rathread_final(){
+ register unsigned int i;
+
+ // Unterminated Threads Left?
+ // Should'nt happen ..
+ // Kill 'em all!
+ //
+ for(i = 1; i < RA_THREADS_MAX; i++){
+ if(l_threads[i].proc != NULL){
+ ShowWarning("rAthread_final: unterminated Thread (tid %u entryPoint %p) - forcing to terminate (kill)\n", i, l_threads[i].proc);
+ rathread_destroy(&l_threads[i]);
+ }
+ }
+
+
+}//end: rathread_final()
+
+
+
+// gets called whenever a thread terminated ..
+static void rat_thread_terminated( rAthread handle ){
+
+ int id_backup = handle->myID;
+
+ // Simply set all members to 0 (except the id)
+ memset(handle, 0x00, sizeof(struct rAthread));
+
+ handle->myID = id_backup; // done ;)
+
+}//end: rat_thread_terminated()
+
+#ifdef WIN32
+DWORD WINAPI _raThreadMainRedirector(LPVOID p){
+#else
+static void *_raThreadMainRedirector( void *p ){
+ sigset_t set; // on Posix Thread platforms
+#endif
+ void *ret;
+
+ // Update myID @ TLS to right id.
+#ifdef HAS_TLS
+ g_rathread_ID = ((rAthread)p)->myID;
+#endif
+
+#ifndef WIN32
+ // When using posix threads
+ // the threads inherits the Signal mask from the thread which's spawned
+ // this thread
+ // so we've to block everything we dont care about.
+ sigemptyset(&set);
+ sigaddset(&set, SIGINT);
+ sigaddset(&set, SIGTERM);
+ sigaddset(&set, SIGPIPE);
+
+ pthread_sigmask(SIG_BLOCK, &set, NULL);
+
+#endif
+
+
+ ret = ((rAthread)p)->proc( ((rAthread)p)->param ) ;
+
+#ifdef WIN32
+ CloseHandle( ((rAthread)p)->hThread );
+#endif
+
+ rat_thread_terminated( (rAthread)p );
+#ifdef WIN32
+ return (DWORD)ret;
+#else
+ return ret;
+#endif
+}//end: _raThreadMainRedirector()
+
+
+
+
+
+///
+/// API Level
+///
+rAthread rathread_create( rAthreadProc entryPoint, void *param ){
+ return rathread_createEx( entryPoint, param, (1<<23) /*8MB*/, RAT_PRIO_NORMAL );
+}//end: rathread_create()
+
+
+rAthread rathread_createEx( rAthreadProc entryPoint, void *param, size_t szStack, RATHREAD_PRIO prio ){
+#ifndef WIN32
+ pthread_attr_t attr;
+#endif
+ size_t tmp;
+ unsigned int i;
+ rAthread handle = NULL;
+
+
+ // given stacksize aligned to systems pagesize?
+ tmp = szStack % getpagesize();
+ if(tmp != 0)
+ szStack += tmp;
+
+
+ // Get a free Thread Slot.
+ for(i = 0; i < RA_THREADS_MAX; i++){
+ if(l_threads[i].proc == NULL){
+ handle = &l_threads[i];
+ break;
+ }
+ }
+
+ if(handle == NULL){
+ ShowError("rAthread: cannot create new thread (entryPoint: %p) - no free thread slot found!", entryPoint);
+ return NULL;
+ }
+
+
+
+ handle->proc = entryPoint;
+ handle->param = param;
+
+#ifdef WIN32
+ handle->hThread = CreateThread(NULL, szStack, _raThreadMainRedirector, (void*)handle, 0, NULL);
+#else
+ pthread_attr_init(&attr);
+ pthread_attr_setstacksize(&attr, szStack);
+
+ if(pthread_create(&handle->hThread, &attr, _raThreadMainRedirector, (void*)handle) != 0){
+ handle->proc = NULL;
+ handle->param = NULL;
+ return NULL;
+ }
+ pthread_attr_destroy(&attr);
+#endif
+
+ rathread_prio_set( handle, prio );
+
+ return handle;
+}//end: rathread_createEx
+
+
+void rathread_destroy ( rAthread handle ){
+#ifdef WIN32
+ if( TerminateThread(handle->hThread, 0) != FALSE){
+ CloseHandle(handle->hThread);
+ rat_thread_terminated(handle);
+ }
+#else
+ if( pthread_cancel( handle->hThread ) == 0){
+
+ // We have to join it, otherwise pthread wont re-cycle its internal ressources assoc. with this thread.
+ //
+ pthread_join( handle->hThread, NULL );
+
+ // Tell our manager to release ressources ;)
+ rat_thread_terminated(handle);
+ }
+#endif
+}//end: rathread_destroy()
+
+rAthread rathread_self( ){
+#ifdef HAS_TLS
+ rAthread handle = &l_threads[g_rathread_ID];
+
+ if(handle->proc != NULL) // entry point set, so its used!
+ return handle;
+#else
+ // .. so no tls means we have to search the thread by its api-handle ..
+ int i;
+
+ #ifdef WIN32
+ HANDLE hSelf;
+ hSelf = GetCurrent = GetCurrentThread();
+ #else
+ pthread_t hSelf;
+ hSelf = pthread_self();
+ #endif
+
+ for(i = 0; i < RA_THREADS_MAX; i++){
+ if(l_threads[i].hThread == hSelf && l_threads[i].proc != NULL)
+ return &l_threads[i];
+ }
+
+#endif
+
+ return NULL;
+}//end: rathread_self()
+
+
+int rathread_get_tid(){
+
+#ifdef HAS_TLS
+ return g_rathread_ID;
+#else
+ // todo
+ #ifdef WIN32
+ return (int)GetCurrentThreadId();
+ #else
+ return (intptr_t)pthread_self();
+ #endif
+
+#endif
+
+}//end: rathread_get_tid()
+
+
+bool rathread_wait( rAthread handle, void* *out_exitCode ){
+
+ // Hint:
+ // no thread data cleanup routine call here!
+ // its managed by the callProxy itself..
+ //
+#ifdef WIN32
+ WaitForSingleObject(handle->hThread, INFINITE);
+ return true;
+#else
+ if(pthread_join(handle->hThread, out_exitCode) == 0)
+ return true;
+ return false;
+#endif
+
+}//end: rathread_wait()
+
+
+void rathread_prio_set( rAthread handle, RATHREAD_PRIO prio ){
+ handle->prio = RAT_PRIO_NORMAL;
+ //@TODO
+}//end: rathread_prio_set()
+
+
+RATHREAD_PRIO rathread_prio_get( rAthread handle){
+ return handle->prio;
+}//end: rathread_prio_get()
+
+
+void rathread_yield(){
+#ifdef WIN32
+ SwitchToThread();
+#else
+ sched_yield();
+#endif
+}//end: rathread_yield()