lovr/src/api/l_thread.c

118 lines
3.0 KiB
C

#include "api.h"
#include "event/event.h"
#include "thread/thread.h"
#include "thread/channel.h"
#include "util.h"
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <stdlib.h>
#include <string.h>
static int threadRunner(void* data) {
Thread* thread = (Thread*) data;
lovrRetain(thread);
mtx_lock(&thread->lock);
thread->running = true;
mtx_unlock(&thread->lock);
lua_State* L = luaL_newstate();
luaL_openlibs(L);
luax_preload(L);
lovrSetErrorCallback((errorFn*) luax_vthrow, L);
lua_pushcfunction(L, luax_getstack);
int errhandler = lua_gettop(L);
if (!luaL_loadbuffer(L, thread->body->data, thread->body->size, "thread")) {
for (uint32_t i = 0; i < thread->argumentCount; i++) {
luax_pushvariant(L, &thread->arguments[i]);
}
if (!lua_pcall(L, thread->argumentCount, 0, errhandler)) {
mtx_lock(&thread->lock);
thread->running = false;
mtx_unlock(&thread->lock);
lovrRelease(thread, lovrThreadDestroy);
lua_close(L);
return 0;
}
}
mtx_lock(&thread->lock);
// Error handling
size_t length;
const char* error = lua_tolstring(L, -1, &length);
if (error) {
thread->error = malloc(length + 1);
if (thread->error) {
memcpy(thread->error, error, length + 1);
lovrEventPush((Event) {
.type = EVENT_THREAD_ERROR,
.data.thread = { thread, thread->error }
});
}
}
thread->running = false;
mtx_unlock(&thread->lock);
lovrRelease(thread, lovrThreadDestroy);
lua_close(L);
return 1;
}
static int l_lovrThreadNewThread(lua_State* L) {
Blob* blob = luax_totype(L, 1, Blob);
if (!blob) {
size_t length;
const char* str = luaL_checklstring(L, 1, &length);
if (memchr(str, '\n', MIN(1024, length))) {
void* data = malloc(length + 1);
lovrAssert(data, "Out of memory");
memcpy(data, str, length + 1);
blob = lovrBlobCreate(data, length, "thread code");
} else {
void* code = luax_readfile(str, &length);
lovrAssert(code, "Could not read thread code from file '%s'", str);
blob = lovrBlobCreate(code, length, str);
}
} else {
lovrRetain(blob);
}
Thread* thread = lovrThreadCreate(threadRunner, blob);
luax_pushtype(L, Thread, thread);
lovrRelease(thread, lovrThreadDestroy);
lovrRelease(blob, lovrBlobDestroy);
return 1;
}
static int l_lovrThreadGetChannel(lua_State* L) {
const char* name = luaL_checkstring(L, 1);
Channel* channel = lovrThreadGetChannel(name);
luax_pushtype(L, Channel, channel);
// Note: Channels are intentionally not released here (see thread.h)
return 1;
}
static const luaL_Reg lovrThreadModule[] = {
{ "newThread", l_lovrThreadNewThread },
{ "getChannel", l_lovrThreadGetChannel },
{ NULL, NULL }
};
extern const luaL_Reg lovrThread[];
extern const luaL_Reg lovrChannel[];
int luaopen_lovr_thread(lua_State* L) {
lua_newtable(L);
luax_register(L, lovrThreadModule);
luax_registertype(L, Thread);
luax_registertype(L, Channel);
if (lovrThreadModuleInit()) {
luax_atexit(L, lovrThreadModuleDestroy);
}
return 1;
}