/**
* This file is part of Hercules.
* http://herc.ws - http://github.com/HerculesWS/Hercules
*
* Copyright (C) 2012-2020 Hercules Dev Team
* Copyright (C) rAthena Project (www.rathena.org)
*
* Hercules is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define HERCULES_CORE
#include "thread.h"
#include "common/cbasetypes.h"
#include "common/memmgr.h"
#include "common/showmsg.h"
#include "common/sysinfo.h" // sysinfo->getpagesize()
#ifdef WIN32
# include "common/winapi.h"
# define __thread __declspec( thread )
#else
# include <pthread.h>
# include <sched.h>
# include <signal.h>
# include <stdlib.h>
# include <string.h>
# include <unistd.h>
#endif
// When Compiling using MSC (on win32..) we know we have support in any case!
#ifdef _MSC_VER
#define HAS_TLS
#endif
/** @file
* Thread interface implementation.
* @author Florian Wilkemeyer <fw@f-ws.de>
*/
static struct thread_interface thread_s;
struct thread_interface *thread;
/// The maximum amount of threads.
#define THREADS_MAX 64
struct thread_handle {
unsigned int myID;
enum thread_priority prio;
threadFunc proc;
void *param;
#ifdef WIN32
HANDLE hThread;
#else
pthread_t hThread;
#endif
};
#ifdef HAS_TLS
static __thread int g_rathread_ID = -1;
#endif
// Subystem Code
static struct thread_handle l_threads[THREADS_MAX];
/// @copydoc thread_interface::init()
static void thread_init(void)
{
register int i;
memset(&l_threads, 0x00, THREADS_MAX * sizeof(struct thread_handle));
for (i = 0; i < THREADS_MAX; i++) {
l_threads[i].myID = i;
}
// now lets init thread id 0, which represents the main thread
#ifdef HAS_TLS
g_rathread_ID = 0;
#endif
l_threads[0].prio = THREADPRIO_NORMAL;
l_threads[0].proc = (threadFunc)0xDEADCAFE;
}
/// @copydoc thread_interface::final()
static void thread_final(void)
{
register int i;
// Unterminated Threads Left?
// Shouldn't happen ... Kill 'em all!
for (i = 1; i < THREADS_MAX; i++) {
if (l_threads[i].proc != NULL){
ShowWarning("thread_final: unterminated Thread (tid %d entry_point %p) - forcing to terminate (kill)\n", i, l_threads[i].proc);
thread->destroy(&l_threads[i]);
}
}
}
/**
* Gets called whenever a thread terminated.
*
* @param handle The terminated thread's handle.
*/
static void thread_terminated(struct thread_handle *handle)
{
// Preserve handle->myID and handle->hThread, set everything else to its default value
handle->param = NULL;
handle->proc = NULL;
handle->prio = THREADPRIO_NORMAL;
}
#ifdef WIN32
static DWORD WINAPI thread_main_redirector(LPVOID p)
{
#else
static void *thread_main_redirector(void *p)
{
sigset_t set; // on Posix Thread platforms
#endif
void *ret;
struct thread_handle *self = p;
// Update myID @ TLS to right id.
#ifdef HAS_TLS
g_rathread_ID = self->myID;
#endif
#ifndef WIN32
// When using posix threads
// the threads inherits the Signal mask from the thread which spawned
// this thread
// so we've to block everything we don't care about.
(void)sigemptyset(&set);
(void)sigaddset(&set, SIGINT);
(void)sigaddset(&set, SIGTERM);
(void)sigaddset(&set, SIGPIPE);
pthread_sigmask(SIG_BLOCK, &set, NULL);
#endif
ret = self->proc(self->param);
#ifdef WIN32
CloseHandle(self->hThread);
#endif
thread_terminated(self);
#ifdef WIN32
return (DWORD)ret;
#else
return ret;
#endif
}
// API Level
/// @copydoc thread_interface::create()
static struct thread_handle *thread_create(threadFunc entry_point, void *param)
{
return thread->create_opt(entry_point, param, (1<<23) /*8MB*/, THREADPRIO_NORMAL);
}
/// @copydoc thread_interface::create_opt()
static struct thread_handle *thread_create_opt(threadFunc entry_point, void *param, size_t stack_size, enum thread_priority prio)
{
#ifndef WIN32
pthread_attr_t attr;
#endif
size_t tmp;
int i;
struct thread_handle *handle = NULL;
// given stacksize aligned to systems pagesize?
tmp = stack_size % sysinfo->getpagesize();
if (tmp != 0)
stack_size += tmp;
// Get a free Thread Slot.
for (i = 0; i < THREADS_MAX; i++) {
if(l_threads[i].proc == NULL){
handle = &l_threads[i];
break;
}
}
if (handle == NULL) {
ShowError("thread_create_opt: cannot create new thread (entry_point: %p) - no free thread slot found!", entry_point);
return NULL;
}
handle->proc = entry_point;
handle->param = param;
#ifdef WIN32
handle->hThread = CreateThread(NULL, stack_size, thread_main_redirector, handle, 0, NULL);
#else
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, stack_size);
if (pthread_create(&handle->hThread, &attr, thread_main_redirector, handle) != 0) {
handle->proc = NULL;
handle->param = NULL;
return NULL;
}
pthread_attr_destroy(&attr);
#endif
thread->prio_set(handle, prio);
return handle;
}
/// @copydoc thread_interface::destroy()
static void thread_destroy(struct thread_handle *handle)
{
#ifdef WIN32
if (TerminateThread(handle->hThread, 0) != FALSE) {
CloseHandle(handle->hThread);
thread_terminated(handle);
}
#else
if (pthread_cancel(handle->hThread) == 0) {
// We have to join it, otherwise pthread wont re-cycle its internal resources assoc. with this thread.
pthread_join(handle->hThread, NULL);
// Tell our manager to release resources ;)
thread_terminated(handle);
}
#endif
}
static struct thread_handle *thread_self(void)
{
#ifdef HAS_TLS
struct thread_handle *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 < THREADS_MAX; i++) {
if (l_threads[i].hThread == hSelf && l_threads[i].proc != NULL)
return &l_threads[i];
}
#endif
return NULL;
}
/// @copydoc thread_interface::get_tid()
static int thread_get_tid(void)
{
#if defined(HAS_TLS)
return g_rathread_ID;
#elif defined(WIN32)
return (int)GetCurrentThreadId();
#else
return (int)pthread_self();
#endif
}
/// @copydoc thread_interface::wait()
static bool thread_wait(struct thread_handle *handle, void **out_exit_code)
{
// 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_exit_code) == 0)
return true;
return false;
#endif
}
/// @copydoc thread_interface::prio_set()
static void thread_prio_set(struct thread_handle *handle, enum thread_priority prio)
{
handle->prio = THREADPRIO_NORMAL;
//@TODO
}
/// @copydoc thread_interface::prio_get()
static enum thread_priority thread_prio_get(struct thread_handle *handle)
{
return handle->prio;
}
/// @copydoc thread_interface::yield()
static void thread_yield(void)
{
#ifdef WIN32
SwitchToThread();
#else
sched_yield();
#endif
}
/// Interface base initialization.
void thread_defaults(void)
{
thread = &thread_s;
thread->init = thread_init;
thread->final = thread_final;
thread->create = thread_create;
thread->create_opt = thread_create_opt;
thread->destroy = thread_destroy;
thread->self = thread_self;
thread->get_tid = thread_get_tid;
thread->wait = thread_wait;
thread->prio_set = thread_prio_set;
thread->prio_get = thread_prio_get;
thread->yield = thread_yield;
}