summaryrefslogblamecommitdiff
path: root/src/common/thread.c
blob: 610ee394cbfa20f8e549927efca8b225766bf27e (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11






                                                                           



                                                                                                                                                        
                                     













                       
                                                                             

               

      



                         










                        


  
              
                                
      






                                                 







                                                                       
 
                                                                 
              
                      
      

                                                 




                       















                                                                                                                                            




                                              

                                                  
 
                                 
 



                                                  



                               

                                              
     


                                              
      


                                     
              
                                        
      

             










                                                                           


      
                                                     
 

                                        

      
                                       
            
                      
     
               








                                 



                                                                                   


                         

                                                                                                       
             
                        
      



























                                                                                                                  

            
                                                                                                    
     








                                                                                                

      


                                     


                         

                                      
            



                                                       
     








                                                                                                               


                          

                        
              



                                                              
     















                                                                           
      

                


                       

                      
 

                         
     






                                     
      
 


                          






                                                         
            

                                                   
     


                                                         




                       



                                                           


                           


                                                


                           



                     
     

                  
                        
//
// 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()