voxelands/src/thread.c

292 lines
5.3 KiB
C

/************************************************************************
* file.c
* voxelands - 3d voxel world sandbox game
* Copyright (C) Lisa 'darkrose' Milne 2016 <lisa@ltmnet.com>
*
* This program 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/>
************************************************************************/
#include "common.h"
#include "thread.h"
#include "list.h"
#include "array.h"
#include <stdlib.h>
#ifndef WIN32
#include <signal.h>
#endif
#define IO_THREAD_STOPPED 0
#define IO_THREAD_RUNNING 1
static struct {
thread_t *threads;
mutex_t *mutexes;
threadid_t main;
} thread_data = {
NULL,
NULL
};
int thread_init()
{
thread_data.main = thread_get_current_id();
return 0;
}
int thread_equal(threadid_t t1, threadid_t t2)
{
#ifndef WIN32
return pthread_equal(t1,t2);
#else
if (t1 == t2)
return 1;
#endif
return 0;
}
int thread_is_main()
{
threadid_t self = thread_get_current_id();
return thread_equal(self,thread_data.main);
}
/* create a new thread */
thread_t *thread_create(void *(*func)(), array_t *args)
{
thread_t *t = malloc(sizeof(thread_t));
t->state = IO_THREAD_RUNNING;
t->func = func;
t->args = args;
t->exit = 0;
thread_data.threads = list_push((void**)&thread_data.threads,t);
#ifndef WIN32
pthread_attr_init(&t->attr);
pthread_attr_setdetachstate(&t->attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&t->thread, &t->attr, t->func, (void *)t);
#else
t->thread = CreateThread(NULL, 0, t->func, (void*)t, 0, NULL);
#endif
return t;
}
/* destroy a thread */
void thread_free(thread_t *t)
{
if (!t)
return;
thread_stop(t);
#ifndef WIN32
pthread_attr_destroy(&t->attr);
#endif
if (t->args)
array_free(t->args,1);
free(t);
}
/* exit from a thread */
void thread_exit(thread_t *t, int state)
{
if (!t)
return;
t->exit = state;
t->state = IO_THREAD_STOPPED;
#ifndef WIN32
pthread_exit(NULL);
#else
ExitThread(state);
#endif
}
/* stop a thread */
void thread_stop(thread_t *t)
{
if (!t)
return;
if (t->state == IO_THREAD_RUNNING) {
t->state = IO_THREAD_STOPPED;
#ifndef WIN32
pthread_kill(t->thread,SIGKILL);
#else
TerminateThread(t->thread,0);
CloseHandle(t->thread);
#endif
}
}
/* restart a thread */
int thread_wake(thread_t *t)
{
if (!t)
return 0;
if (t->state == IO_THREAD_RUNNING) {
#ifndef WIN32
pthread_kill(t->thread,SIGCONT);
#else
ResumeThread(t->thread);
#endif
}else{
t->state = IO_THREAD_RUNNING;
#ifndef WIN32
pthread_create(&t->thread, &t->attr, t->func, (void *)t);
#else
t->thread = CreateThread(NULL, 0, t->func, (void*)t, 0, NULL);
#endif
}
return 0;
}
/* wait for a thread to exit */
void thread_wait(thread_t *t)
{
if (!t)
return;
if (t->state == IO_THREAD_RUNNING) {
#ifndef WIN32
pthread_join(t->thread,NULL);
#else
WaitForSingleObject(t->thread, 2000);
CloseHandle(t->thread);
#endif
}
t->state = IO_THREAD_STOPPED;
}
threadid_t thread_get_current_id()
{
#ifdef WIN32
return GetCurrentThreadId();
#else
return pthread_self();
#endif
}
/* create a mutex */
mutex_t *mutex_create()
{
mutex_t *m = malloc(sizeof(mutex_t));
#ifndef WIN32
pthread_mutexattr_init(&m->attr);
pthread_mutexattr_settype(&m->attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&m->mut, &m->attr);
#else
InitializeCriticalSection(&m->mut);
#endif
m->id = thread_get_current_id();
m->count = 0;
thread_data.mutexes = list_push((void**)&thread_data.mutexes,m);
return m;
}
/* destroy a mutex */
void mutex_free(mutex_t *m)
{
mutex_unlock(m);
#ifndef WIN32
pthread_mutex_destroy(&m->mut);
pthread_mutexattr_destroy(&m->attr);
#else
DeleteCriticalSection(&m->mut);
#endif
thread_data.mutexes = list_remove((void**)&thread_data.mutexes,m);
free(m);
}
/* lock a mutex */
void mutex_lock(mutex_t *m)
{
if (!mutex_trylock(m))
return;
if (m->id == thread_get_current_id()) {
m->count++;
return;
}
#ifndef WIN32
pthread_mutex_lock(&m->mut);
#else
EnterCriticalSection(&m->mut);
#endif
m->id = thread_get_current_id();
m->count = 1;
}
/* try to lock a mutex - return non-zero if not locked */
int mutex_trylock(mutex_t *m)
{
#ifndef WIN32
if (pthread_mutex_trylock(&m->mut))
return 1;
#else
if (!TryEnterCriticalSection(&m->mut))
return 1;
#endif
m->id = thread_get_current_id();
m->count = 1;
return 0;
}
/* unlock a mutex */
void mutex_unlock(mutex_t *m)
{
m->count--;
if (m->count > 0)
return;
#ifndef WIN32
pthread_mutex_unlock(&m->mut);
#else
LeaveCriticalSection(&m->mut);
#endif
}
static void *mutex_test_lock_t(thread_t *t)
{
int r = mutex_trylock(((mutex_t**)(t->args->data))[0]);
thread_exit(t,r);
return NULL;
}
/* try to force a mutex to unlock */
void mutex_unlock_complete(mutex_t *m)
{
array_t *a;
thread_t *t;
a = array_create(ARRAY_TYPE_PTR);
array_push_ptr(a,m);
t = thread_create(mutex_test_lock_t,a);
thread_wait(t);
while (t->exit) {
thread_wake(t);
thread_wait(t);
}
}