diff options
author | Ronan Collobert <ronan@collobert.com> | 2015-04-14 08:09:28 +0300 |
---|---|---|
committer | Ronan Collobert <ronan@collobert.com> | 2015-04-21 04:54:18 +0300 |
commit | 9665a4b99eed85ccfad1e03a581ff14428d6dace (patch) | |
tree | 23f2f0e068c3817227abb046456f2110b8d2b606 /lib | |
parent | d223e9d60ed04f91016867065a5ef7575d337dce (diff) |
moved away from FFI
- removed the ffi dependency... now runs under lua!
- support for low-level thread
- support for basic mutex and condition variables from lua
- better error messaging
- better reliability
- more doc
- no more addjobasync()
- no sdl
Diffstat (limited to 'lib')
-rw-r--r-- | lib/THThread.c | 241 | ||||
-rw-r--r-- | lib/THThread.h | 26 | ||||
-rw-r--r-- | lib/init.c | 10 | ||||
-rw-r--r-- | lib/luaTHRD.h | 84 | ||||
-rw-r--r-- | lib/queue.c | 383 | ||||
-rw-r--r-- | lib/threads.c | 263 |
6 files changed, 1007 insertions, 0 deletions
diff --git a/lib/THThread.c b/lib/THThread.c new file mode 100644 index 0000000..4e503fb --- /dev/null +++ b/lib/THThread.c @@ -0,0 +1,241 @@ +#ifndef TH_THREAD_INC +#define TH_THREAD_INC + +#include <stdlib.h> +#include <string.h> + +#include "TH.h" +#include "THThread.h" + +#if defined(USE_PTHREAD_THREADS) +#include <pthread.h> + +#elif defined(USE_WIN32_THREADS) + +/* very basic emulation to suit our needs */ + +#include <process.h> +#include <windows.h> + +typedef HANDLE pthread_t; +typedef DWORD pthread_attr_t; +typedef HANDLE pthread_mutex_t; +typedef HANDLE pthread_cond_t; + +static int pthread_create(pthread_t *restrict thread, + const pthread_attr_t *restrict attr, void *(*start_routine)(void *), + void *restrict arg) +{ + *thread = (HANDLE)_beginthreadex(NULL, 0, (THREAD_FUNCTION)start_routine, arg, 0, NULL); + return (int)(*thread == NULL); +} + +static int pthread_join(pthread_t thread, void **value_ptr) +{ + return ((WaitForSingleObject((thread), INFINITE) != WAIT_OBJECT_0) || !CloseHandle(thread)); +} + +static int pthread_mutex_init(pthread_mutex_t *restrict mutex, + const pthread_mutexattr_t *restrict attr) +{ + *mutex = CreateMutex(NULL, FALSE, NULL); + return (int)(*mutex == NULL); +} + +static int pthread_mutex_lock(pthread_mutex_t *mutex) +{ + return WaitForSingleObject(*mutex, INFINITE) == 0; +} + +static int pthread_mutex_unlock(pthread_mutex_t *mutex) +{ + return ReleaseMutex(*mutex) == 0; +} + +static int pthread_mutex_destroy(pthread_mutex_t *mutex) +{ + return CloseHandle(*mutex) == 0; +} + +static int pthread_cond_init(pthread_cond_t *restrict cond, + const pthread_condattr_t *restrict attr) +{ + *cond = CreateEvent(NULL, FALSE, FALSE, NULL); + return (int)(*cond == NULL); +} + +static int pthread_cond_wait(pthread_cond_t *restrict cond, + pthread_mutex_t *restrict mutex) +{ + SignalObjectAndWait(*mutex, *cond, INFINITE, FALSE); + return WaitForSingleObject(*mutex, INFINITE) == 0; +} + +static int pthread_cond_destroy(pthread_cond_t *cond) +{ + return CloseHandle(*cond) == 0; +} + +int pthread_cond_signal(pthread_cond_t *cond) +{ + return SetEvent(*cond) == 0; +} + +#else +#error no thread system available +#endif + +typedef struct THThread_ { + pthread_t id; + int (*func)(void*); + void* data; + int status; +} THThread; + +typedef struct THMutex_{ + pthread_mutex_t id; + int refcount; +} THMutex; + +typedef struct THCondition_ { + pthread_cond_t id; + int refcount; +} THCondition; + +static void* thread_closure(void *data) +{ + THThread *thread = data; + thread->status = thread->func(thread->data); + return NULL; +} + +THThread* THThread_new(int (*func)(void*), void *data) +{ + THThread *self = malloc(sizeof(THThread)); + self->func = func; + self->data = data; + self->status = 0; + if(!self) + return NULL; + if(pthread_create(&self->id, NULL, thread_closure, self)) { + free(self); + return NULL; + } + return self; +} + +long THThread_id(THThread *self) +{ + return (long)(self); +} + +int THThread_free(THThread *self) +{ + int status = 1; + if(self) { + if(pthread_join(self->id, NULL)) + return 1; + status = self->status; + free(self); + } + return status; +} + +THMutex* THMutex_new(void) +{ + THMutex *self = malloc(sizeof(THMutex)); + if(!self) + return NULL; + if(pthread_mutex_init(&self->id, NULL) != 0) { + free(self); + return NULL; + } + self->refcount = 1; + return self; +} + +THMutex* THMutex_newWithId(long id) +{ + THMutex *self = (THMutex*)id; + THAtomicIncrementRef(&self->refcount); + return self; +} + +long THMutex_id(THMutex *self) +{ + return (long)(self); +} + +int THMutex_lock(THMutex *self) +{ + if(pthread_mutex_lock(&self->id) != 0) + return 1; + return 0; +} + +int THMutex_unlock(THMutex *self) +{ + if(pthread_mutex_unlock(&self->id) != 0) + return 1; + return 0; +} + +void THMutex_free(THMutex *self) +{ + if(self) { + if(THAtomicDecrementRef(&self->refcount)) { + pthread_mutex_destroy(&self->id); + free(self); + } + } +} + +THCondition* THCondition_new(void) +{ + THCondition *self = malloc(sizeof(THCondition)); + if(!self) + return NULL; + if(pthread_cond_init(&self->id, NULL)) { + free(self); + return NULL; + } + self->refcount = 1; + return self; +} + +THCondition* THCondition_newWithId(long id) +{ + THCondition *self = (THCondition*)id; + THAtomicIncrementRef(&self->refcount); + return self; +} + +long THCondition_id(THCondition *self) +{ + return (long)(self); +} + +int THCondition_signal(THCondition *self) +{ + if(pthread_cond_signal(&self->id)) + return 1; + return 0; +} + +int THCondition_wait(THCondition *self, THMutex *mutex) +{ + if(pthread_cond_wait(&self->id, &mutex->id)) + return 1; + return 0; +} + +void THCondition_free(THCondition *self) +{ + if(self) { + if(THAtomicDecrementRef(&self->refcount)) { + pthread_cond_destroy(&self->id); + } + } +} + +#endif diff --git a/lib/THThread.h b/lib/THThread.h new file mode 100644 index 0000000..0b086fb --- /dev/null +++ b/lib/THThread.h @@ -0,0 +1,26 @@ +#ifndef TH_THREAD_INC +#define TH_THREAD_INC + +typedef struct THThread_ THThread; +typedef struct THMutex_ THMutex; +typedef struct THCondition_ THCondition; + +THThread* THThread_new(int (*closure)(void*), void *data); +long THThread_id(THThread *self); +int THThread_free(THThread *self); + +THMutex* THMutex_new(void); +THMutex* THMutex_newWithId(long id); +long THMutex_id(THMutex *self); +int THMutex_lock(THMutex *self); +int THMutex_unlock(THMutex *self); +void THMutex_free(THMutex *self); + +THCondition* THCondition_new(void); +THCondition* THCondition_newWithId(long id); +long THCondition_id(THCondition *self); +int THCondition_signal(THCondition *self); +int THCondition_wait(THCondition *self, THMutex *mutex); +void THCondition_free(THCondition *self); + +#endif diff --git a/lib/init.c b/lib/init.c new file mode 100644 index 0000000..5f7723a --- /dev/null +++ b/lib/init.c @@ -0,0 +1,10 @@ +#include "threads.c" +#include "queue.c" + +int luaopen_libthreads(lua_State *L) +{ + lua_newtable(L); + thread_init_pkg(L); + queue_init_pkg(L); + return 1; +} diff --git a/lib/luaTHRD.h b/lib/luaTHRD.h new file mode 100644 index 0000000..3760bdb --- /dev/null +++ b/lib/luaTHRD.h @@ -0,0 +1,84 @@ +#ifndef LUA_THRD_INC +#define LUA_THRD_INC + +static int luaTHRD_pushudata(lua_State *L, void *ptr, const char* typename) +{ + void **udata = lua_newuserdata(L, sizeof(void*)); + if(udata) { + *udata = ptr; + luaL_getmetatable(L, typename); + lua_setmetatable(L, -2); + return 1; + } + return 0; +} + +static void *luaTHRD_checkudata(lua_State *L, int narg, const char *typename) +{ + void **udata = luaL_checkudata(L, narg, typename); + if(udata) + return *udata; + else + return NULL; +} + +static void *luaTHRD_toudata(lua_State *L, int narg, const char *typename) +{ + void **udata = lua_touserdata(L, narg); + if(udata) { + if(lua_getmetatable(L, -1)) { + luaL_getmetatable(L, typename); + if(lua_equal(L, -1, -2)) { + lua_pop(L, 2); + return *udata; + } + else { + lua_pop(L, 2); + return NULL; + } + } + else + return NULL; + } + else + return NULL; +} + +static int luaTHRD_ctor(lua_State *L) +{ + if(!lua_istable(L, 1)) /* dummy ctor table */ + luaL_error(L, "ctor: table expected"); + lua_getmetatable(L, 1); + lua_remove(L, 1); /* dummy ctor table */ + if(!lua_istable(L, -1)) + luaL_error(L, "ctor: no metatable found"); + lua_pushstring(L, "__new"); + lua_rawget(L, -2); + lua_remove(L, -2); /* forget about metatable */ + if(!lua_isfunction(L, -1)) + luaL_error(L, "ctor: __new appears to be not a function"); + lua_insert(L, 1); /* ctor first, arguments follow */ + lua_call(L, lua_gettop(L)-1, LUA_MULTRET); + return lua_gettop(L); +} + +static void luaTHRD_pushctortable(lua_State *L, lua_CFunction ctor, const char* typename) +{ + lua_newtable(L); /* empty useless dude */ + lua_newtable(L); /* metatable of the dude */ + lua_pushstring(L, "__index"); + luaL_getmetatable(L, typename); + lua_rawset(L, -3); + lua_pushstring(L, "__newindex"); + luaL_getmetatable(L, typename); + lua_rawset(L, -3); + lua_pushstring(L, "__new"); /* __call will look into there */ + lua_pushcfunction(L, ctor); + lua_rawset(L, -3); + lua_pushstring(L, "__call"); /* pop the table and calls __new */ + lua_pushcfunction(L, luaTHRD_ctor); + lua_rawset(L, -3); + lua_setmetatable(L, -2); +} + +#endif diff --git a/lib/queue.c b/lib/queue.c new file mode 100644 index 0000000..9ed7fa3 --- /dev/null +++ b/lib/queue.c @@ -0,0 +1,383 @@ +#include "TH.h" /* for THCharStorage */ +#include "luaT.h" /* for handling THCHarStorage */ +#include "luaTHRD.h" +#include "THThread.h" +#include <lua.h> +#include <lualib.h> +#include <lualib.h> + + +typedef struct THQueue_ { + THMutex *mutex; + THCondition *notfull; + THCondition *notempty; + THCharStorage **callbacks; + THCharStorage **args; + char* serialize; + + int head; + int tail; + int isempty; + int isfull; + int size; + int refcount; +} THQueue; + +static int queue_new(lua_State *L) +{ + THQueue *queue = NULL; + THQueue **queue_udata = NULL; + + if(lua_gettop(L) == 1) { + + queue = (THQueue*)luaL_checkinteger(L, 1); + THMutex_lock(queue->mutex); + queue->refcount = queue->refcount + 1; + THMutex_unlock(queue->mutex); + + } else if(lua_gettop(L) == 2) { + + int size = luaL_checkint(L, 1); + const char *serialize = luaL_checkstring(L, 2); + size_t serialize_len; + lua_tolstring(L, 2, &serialize_len); + + queue = calloc(1, sizeof(THQueue)); /* zeroed */ + if(!queue) + goto outofmem; + + queue->mutex = THMutex_new(); + queue->notfull = THCondition_new(); + queue->notempty = THCondition_new(); + queue->callbacks = calloc(size, sizeof(THCharStorage*)); + queue->args = calloc(size, sizeof(THCharStorage*)); + queue->serialize = malloc(serialize_len+1); + if(queue->serialize) + memcpy(queue->serialize, serialize, serialize_len+1); + + queue->head = 0; + queue->tail = 0; + queue->isempty = 1; + queue->isfull = 0; + queue->size = size; + queue->refcount = 1; + + if(!queue->mutex || !queue->notfull || !queue->notempty + || !queue->callbacks || !queue->args || !queue->serialize) + goto outofmemfree; + + } else + luaL_error(L, "threads: queue new invalid arguments"); + + if(!luaTHRD_pushudata(L, queue, "threads.Queue")) + goto outofmemfree; + + return 1; + + + outofmemfree: + THMutex_free(queue->mutex); + THCondition_free(queue->notfull); + THCondition_free(queue->notempty); + free(queue->callbacks); + free(queue->args); + free(queue->serialize); + free(queue); + outofmem: + luaL_error(L, "threads: queue new out of memory"); + return 0; +} + +static int queue_free(lua_State *L) +{ + THQueue *queue = luaTHRD_checkudata(L, 1, "threads.Queue"); + if(THAtomicDecrementRef(&queue->refcount)) + { + int i; + THMutex_free(queue->mutex); + THCondition_free(queue->notfull); + THCondition_free(queue->notempty); + for(i = 0; i < queue->size; i++) { + if(queue->callbacks[i]) + THCharStorage_free(queue->callbacks[i]); + if(queue->args[i]) + THCharStorage_free(queue->args[i]); + } + free(queue->serialize); + free(queue->callbacks); + free(queue->args); + free(queue); + } + return 0; +} + +static int queue_retain(lua_State *L) +{ + THQueue *queue = luaTHRD_checkudata(L, 1, "threads.Queue"); + THAtomicIncrementRef(&queue->refcount); + return 0; +} + +static int queue_get_mutex(lua_State *L) +{ + THQueue *queue = luaTHRD_checkudata(L, 1, "threads.Queue"); + return luaTHRD_pushudata(L, queue->mutex, "threads.Mutex"); +} + +static int queue_get_notfull(lua_State *L) +{ + THQueue *queue = luaTHRD_checkudata(L, 1, "threads.Queue"); + return luaTHRD_pushudata(L, queue->notfull, "threads.Condition"); +} + +static int queue_get_notempty(lua_State *L) +{ + THQueue *queue = luaTHRD_checkudata(L, 1, "threads.Queue"); + return luaTHRD_pushudata(L, queue->notempty, "threads.Condition"); +} + +static int queue_get_serialize(lua_State *L) +{ + THQueue *queue = luaTHRD_checkudata(L, 1, "threads.Queue"); + lua_pushstring(L, queue->serialize); + return 1; +} + +static int queue_get_head(lua_State *L) +{ + THQueue *queue = luaTHRD_checkudata(L, 1, "threads.Queue"); + lua_pushnumber(L, queue->head); + return 1; +} + +static int queue_get_tail(lua_State *L) +{ + THQueue *queue = luaTHRD_checkudata(L, 1, "threads.Queue"); + lua_pushnumber(L, queue->tail); + return 1; +} + +static int queue_get_isempty(lua_State *L) +{ + THQueue *queue = luaTHRD_checkudata(L, 1, "threads.Queue"); + lua_pushnumber(L, queue->isempty); + return 1; +} + +static int queue_get_isfull(lua_State *L) +{ + THQueue *queue = luaTHRD_checkudata(L, 1, "threads.Queue"); + lua_pushnumber(L, queue->isfull); + return 1; +} + +static int queue_get_size(lua_State *L) +{ + THQueue *queue = luaTHRD_checkudata(L, 1, "threads.Queue"); + lua_pushnumber(L, queue->size); + return 1; +} + +static int queue__index(lua_State *L) +{ + THQueue *queue = luaTHRD_checkudata(L, 1, "threads.Queue"); + lua_getmetatable(L, 1); + if(lua_isstring(L, 2)) { + lua_pushstring(L, "__get"); + lua_rawget(L, -2); + lua_pushvalue(L, 2); + lua_rawget(L, -2); + if(lua_isfunction(L, -1)) { + lua_pushvalue(L, 1); + lua_call(L, 1, 1); + return 1; + } + else { + lua_pop(L, 2); + } + } + lua_insert(L, -2); + lua_rawget(L, -2); + return 1; +} + +static int queue_callback(lua_State *L) +{ + THQueue *queue = luaTHRD_checkudata(L, 1, "threads.Queue"); + int idx = luaL_checkint(L, 2); + luaL_argcheck(L, idx >= 0 && idx < queue->size, 2, "out of range"); + if(lua_gettop(L) == 2) { + THCharStorage *storage = NULL; + if((storage = queue->callbacks[idx])) { + THCharStorage_retain(storage); + luaT_pushudata(L, storage, "torch.CharStorage"); + return 1; + } + else + return 0; + } + else if(lua_gettop(L) == 3) { + THCharStorage *storage = luaT_checkudata(L, 3, "torch.CharStorage"); /* DEBUG: might be luaT for torch objects */ + if(queue->callbacks[idx]) { + THCharStorage_free(queue->callbacks[idx]); + } + queue->callbacks[idx] = storage; + THCharStorage_retain(storage); + return 0; + } + else + luaL_error(L, "invalid arguments"); + return 0; +} + +static int queue_arg(lua_State *L) +{ + THQueue *queue = luaTHRD_checkudata(L, 1, "threads.Queue"); + int idx = luaL_checkint(L, 2); + luaL_argcheck(L, idx >= 0 && idx < queue->size, 2, "out of range"); + if(lua_gettop(L) == 2) { + THCharStorage *storage = NULL; + if((storage = queue->args[idx])) { + THCharStorage_retain(storage); + luaT_pushudata(L, storage, "torch.CharStorage"); + return 1; + } + else + return 0; + } + else if(lua_gettop(L) == 3) { + THCharStorage *storage = luaT_checkudata(L, 3, "torch.CharStorage"); /* DEBUG: might be luaT for torch objects */ + if(queue->args[idx]) + THCharStorage_free(queue->args[idx]); + queue->args[idx] = storage; + THCharStorage_retain(storage); + return 0; + } + else + luaL_error(L, "invalid arguments"); + return 0; +} + + +/* */ + +static int queue_set_head(lua_State *L) +{ + THQueue *queue = luaTHRD_checkudata(L, 1, "threads.Queue"); + int value = luaL_checkint(L, 2); + queue->head = value; + return 0; +} + +static int queue_set_tail(lua_State *L) +{ + THQueue *queue = luaTHRD_checkudata(L, 1, "threads.Queue"); + int value = luaL_checkint(L, 2); + queue->tail = value; + return 0; +} + +static int queue_set_isempty(lua_State *L) +{ + THQueue *queue = luaTHRD_checkudata(L, 1, "threads.Queue"); + int value = luaL_checkint(L, 2); + queue->isempty = value; + return 0; +} + +static int queue_set_isfull(lua_State *L) +{ + THQueue *queue = luaTHRD_checkudata(L, 1, "threads.Queue"); + int value = luaL_checkint(L, 2); + queue->isfull = value; + return 0; +} + +static int queue__newindex(lua_State *L) +{ + THQueue *queue = luaTHRD_checkudata(L, 1, "threads.Queue"); + if(lua_gettop(L) != 3) + luaL_error(L, "invalid arguments"); + + lua_getmetatable(L, 1); + if(lua_isstring(L, 2)) { + lua_pushstring(L, "__set"); + lua_rawget(L, -2); + lua_pushvalue(L, 2); + lua_rawget(L, -2); + if(lua_isfunction(L, -1)) { + lua_pushvalue(L, 1); + lua_pushvalue(L, 3); + lua_call(L, 2, 0); + return 0; + } + else + luaL_error(L, "invalid argument"); + } + luaL_error(L, "invalid argument"); + return 0; +} + +static int queue_id(lua_State *L) +{ + THQueue *queue = luaTHRD_checkudata(L, 1, "threads.Queue"); + lua_pushinteger(L, (long)queue); + return 1; +} + +static const struct luaL_Reg queue__ [] = { + {"new", queue_new}, + {"id", queue_id}, + {"retain", queue_retain}, + {"free", queue_free}, + {"callback", queue_callback}, + {"arg", queue_arg}, + {"__gc", queue_free}, + {"__index", queue__index}, + {"__newindex", queue__newindex}, + {NULL, NULL} +}; + +static const struct luaL_Reg queue_get__ [] = { + {"mutex", queue_get_mutex}, + {"notfull", queue_get_notfull}, + {"notempty", queue_get_notempty}, + {"serialize", queue_get_serialize}, + {"head", queue_get_head}, + {"tail", queue_get_tail}, + {"isempty", queue_get_isempty}, + {"isfull", queue_get_isfull}, + {"size", queue_get_size}, + {NULL, NULL} +}; + +static const struct luaL_Reg queue_set__ [] = { + {"head", queue_set_head}, + {"tail", queue_set_tail}, + {"isempty", queue_set_isempty}, + {"isfull", queue_set_isfull}, + {NULL, NULL} +}; + +static void queue_init_pkg(lua_State *L) +{ + if(!luaL_newmetatable(L, "threads.Queue")) + luaL_error(L, "threads: threads.Queue type already exists"); + luaL_register(L, NULL, queue__); + + lua_pushstring(L, "__get"); + lua_newtable(L); + luaL_register(L, NULL, queue_get__); + lua_rawset(L, -3); + + lua_pushstring(L, "__set"); + lua_newtable(L); + luaL_register(L, NULL, queue_set__); + lua_rawset(L, -3); + + lua_pop(L, 1); + + lua_pushstring(L, "Queue"); + luaTHRD_pushctortable(L, queue_new, "threads.Queue"); + lua_rawset(L, -3); +} diff --git a/lib/threads.c b/lib/threads.c new file mode 100644 index 0000000..b670540 --- /dev/null +++ b/lib/threads.c @@ -0,0 +1,263 @@ +#include <stdio.h> +#include <stdlib.h> +#include <luaT.h> +#include <string.h> + +#include "THThread.h" +#include "luaTHRD.h" + +#include <lua.h> +#include <lualib.h> + +static int newthread(void *code_) +{ + char *code = code_; + lua_State *L = luaL_newstate(); + + if(!L) { + printf("THREAD FATAL ERROR: could not create lua state\n"); + return -1; + } + luaL_openlibs(L); + + long addr = (long)code; + if(luaL_loadstring(L, code)) { + printf("FATAL THREAD PANIC: (loadstring) %s\n", lua_tolstring(L, -1, NULL)); + free(code); + lua_close(L); + return -1; + } + free(code); + if(lua_pcall(L, 0, 0, 0)) { + printf("FATAL THREAD PANIC: (pcall) %s\n", lua_tolstring(L, -1, NULL)); + lua_close(L); + return -1; + } + + lua_close(L); + return 0; +} + +static int thread_new(lua_State *L) +{ + THThread *thread = NULL; + size_t len = 0; + const char *code = luaL_checklstring(L, 1, &len); + char *code_dup = malloc(len+1); + if(!code_dup) + luaL_error(L, "threads: out of memory"); + memcpy(code_dup, code, len+1); + + thread = THThread_new(newthread, (void*)code_dup); + if(!thread) + luaL_error(L, "threads: thread new failed"); + luaTHRD_pushudata(L, thread, "threads.Thread"); + + return 1; +} + +static int thread_tostring(lua_State *L) +{ + char str[128]; + THThread *thread = luaTHRD_checkudata(L, 1, "threads.Thread"); + snprintf(str, 128, "threads.Thread <%lx>", THThread_id(thread)); + lua_pushstring(L, str); + return 1; +} + +static int thread_id(lua_State *L) +{ + THThread *thread = luaTHRD_checkudata(L, 1, "threads.Thread"); + lua_pushinteger(L, THThread_id(thread)); + return 1; +} + +static int thread_free(lua_State *L) +{ + THThread *thread = luaTHRD_checkudata(L, 1, "threads.Thread"); + THThread_free(thread); + return 0; +} + +static int mutex_new(lua_State *L) +{ + THMutex *mutex = NULL; + if(lua_gettop(L) == 0) { + mutex = THMutex_new(); + } + else if(lua_gettop(L) == 1) { + long id = luaL_checklong(L, 1); + mutex = THMutex_newWithId(id); + } + else + luaL_error(L, "threads: mutex new invalid arguments"); + if(!mutex) + luaL_error(L, "threads: mutex new failed"); + luaTHRD_pushudata(L, mutex, "threads.Mutex"); + return 1; +} + +static int mutex_tostring(lua_State *L) +{ + char str[128]; + THMutex *mutex = luaTHRD_checkudata(L, 1, "threads.Mutex"); + snprintf(str, 128, "threads.Mutex <%lx>", THMutex_id(mutex)); + lua_pushstring(L, str); + return 1; +} + +static int mutex_id(lua_State *L) +{ + THMutex *mutex = luaTHRD_checkudata(L, 1, "threads.Mutex"); + lua_pushinteger(L, THMutex_id(mutex)); + return 1; +} + +static int mutex_lock(lua_State *L) +{ + THMutex *mutex = luaTHRD_checkudata(L, 1, "threads.Mutex"); + if(THMutex_lock(mutex)) + luaL_error(L, "threads: mutex lock failed"); + return 0; +} + +static int mutex_unlock(lua_State *L) +{ + THMutex *mutex = luaTHRD_checkudata(L, 1, "threads.Mutex"); + if(THMutex_unlock(mutex)) + luaL_error(L, "threads: mutex unlock failed"); + return 0; +} + +static int mutex_free(lua_State *L) +{ + THMutex *mutex = luaTHRD_checkudata(L, 1, "threads.Mutex"); + THMutex_free(mutex); + return 0; +} + +static int condition_new(lua_State *L) +{ + THCondition *condition = NULL; + if(lua_gettop(L) == 0) { + condition = THCondition_new(); + } + else if(lua_gettop(L) == 1) { + long id = luaL_checklong(L, 1); + condition = THCondition_newWithId(id); + } + else + luaL_error(L, "threads: condition new invalid arguments"); + if(!condition) + luaL_error(L, "threads: condition new failed"); + luaTHRD_pushudata(L, condition, "threads.Condition"); + return 1; +} + +static int condition_tostring(lua_State *L) +{ + char str[128]; + THCondition *condition = luaTHRD_checkudata(L, 1, "threads.Condition"); + snprintf(str, 128, "threads.Condition <%lx>", THCondition_id(condition)); + lua_pushstring(L, str); + return 1; +} + +static int condition_id(lua_State *L) +{ + THCondition *condition = luaTHRD_checkudata(L, 1, "threads.Condition"); + lua_pushinteger(L, THCondition_id(condition)); + return 1; +} + +static int condition_free(lua_State *L) +{ + THCondition *condition = luaTHRD_checkudata(L, 1, "threads.Condition"); + if(!condition) + luaL_error(L, "threads: condition free failed"); + return 0; +} + +static int condition_signal(lua_State *L) +{ + THCondition *condition = luaTHRD_checkudata(L, 1, "threads.Condition"); + if(THCondition_signal(condition)) + luaL_error(L, "threads: condition signal failed"); + return 0; +} + +static int condition_wait(lua_State *L) +{ + THCondition *condition = luaTHRD_checkudata(L, 1, "threads.Condition"); + THMutex *mutex = luaTHRD_checkudata(L, 2, "threads.Mutex"); + if(THCondition_wait(condition, mutex)) + luaL_error(L, "threads: condition wait failed"); + return 0; +} + +static const struct luaL_Reg thread__ [] = { + {"new", thread_new}, + {"__tostring", thread_tostring}, + {"id", thread_id}, + {"free", thread_free}, + {NULL, NULL} +}; + +static const struct luaL_Reg mutex__ [] = { + {"new", mutex_new}, + {"__tostring", mutex_tostring}, + {"id", mutex_id}, + {"lock", mutex_lock}, + {"unlock", mutex_unlock}, + {"free", mutex_free}, + {NULL, NULL} +}; + +static const struct luaL_Reg condition__ [] = { + {"new", condition_new}, + {"__tostring", condition_tostring}, + {"id", condition_id}, + {"signal", condition_signal}, + {"wait", condition_wait}, + {"free", condition_free}, + {NULL, NULL} +}; + +static void thread_init_pkg(lua_State *L) +{ + if(!luaL_newmetatable(L, "threads.Thread")) + luaL_error(L, "threads: threads.Thread type already exists"); + luaL_register(L, NULL, thread__); + lua_pushstring(L, "__index"); + lua_pushvalue(L, -2); + lua_rawset(L, -3); + lua_pop(L, 1); + + if(!luaL_newmetatable(L, "threads.Mutex")) + luaL_error(L, "threads: threads.Mutex type already exists"); + luaL_register(L, NULL, mutex__); + lua_pushstring(L, "__index"); + lua_pushvalue(L, -2); + lua_rawset(L, -3); + lua_pop(L, 1); + + if(!luaL_newmetatable(L, "threads.Condition")) + luaL_error(L, "threads: threads.Condition type already exists"); + luaL_register(L, NULL, condition__); + lua_pushstring(L, "__index"); + lua_pushvalue(L, -2); + lua_rawset(L, -3); + lua_pop(L, 1); + + lua_pushstring(L, "Thread"); + luaTHRD_pushctortable(L, thread_new, "threads.Thread"); + lua_rawset(L, -3); + + lua_pushstring(L, "Mutex"); + luaTHRD_pushctortable(L, mutex_new, "threads.Mutex"); + lua_rawset(L, -3); + + lua_pushstring(L, "Condition"); + luaTHRD_pushctortable(L, condition_new, "threads.Condition"); + lua_rawset(L, -3); +} |