diff options
| -rw-r--r-- | docs/thread/async.md | 21 | ||||
| -rw-r--r-- | docs/thread/usleep.md | 5 | ||||
| -rw-r--r-- | library/lullaby/thread.lua | 27 | ||||
| -rw-r--r-- | readme.md | 2 | ||||
| -rw-r--r-- | src/thread.c | 80 | ||||
| -rw-r--r-- | src/thread.h | 6 |
6 files changed, 109 insertions, 32 deletions
diff --git a/docs/thread/async.md b/docs/thread/async.md index 0f04e12..98497ba 100644 --- a/docs/thread/async.md +++ b/docs/thread/async.md @@ -8,6 +8,21 @@ the argument function is executed by the thread until it has been completed. the the reason for the res function it because lua_call (in the c api) requires a number or return values before you run the function +the res function also provides some child methods for thread managment + +### res:testclose + +res:testclose() + +closes the thread if it is being requested to close from async:close + +### res:autoclose + +res:autoclose() + +calls res:testclose() every lua line, using a debug hook + +--- ### async:await @@ -25,12 +40,10 @@ kills the thread, this may close it in an unsafe way, and should be avoided async:close() -kills the thread in a more safe manor, should be preferred in most cases. however it is best to let the thread exit itself - -some systems may not support this (pthread_cancel) and so this function will call async:kill() in cases where it cant. android is one of the main ones +waits for the thread to internally call res:testclose or exit ### async:clean async:clean() -calls the __gc metamethod, will call async:close() if it is still running +calls the __gc metamethod, will call async:kill() if it is still running diff --git a/docs/thread/usleep.md b/docs/thread/usleep.md new file mode 100644 index 0000000..75208a9 --- /dev/null +++ b/docs/thread/usleep.md @@ -0,0 +1,5 @@ +## usleep + +thread.usleep(μN) + +puts the thread to sleep for N microseconds (duh) diff --git a/library/lullaby/thread.lua b/library/lullaby/thread.lua index 7a1888c..41444ff 100644 --- a/library/lullaby/thread.lua +++ b/library/lullaby/thread.lua @@ -16,7 +16,7 @@ function async.await(T) end ---@return nil function async.clean(T) end ----stops the thread, may be unavaliable on some systems (android) and will call async:kill instead +---waits for the thread to either call res:testcancel() or for it to exit ---@param T async-table ---@return nil function async.close(T) end @@ -31,8 +31,26 @@ function async.kill(T) end ---@type lightuserdata async._ = nil +---@class async-res-table +---@overload fun(a: any): nil +local asyncres = setmetatable({}, { + __call = function() end +}) + +---checks if the thread is being requested to close with thread:close() +---@param T async-res-table +---@return nil +function asyncres.testclose(T) end + +---calls res:testclose every line +---@param T async-res-table +---@return nil +function asyncres.autoclose(T) end +---@meta + ---@async ----@param fun fun(res: fun(...)): nil function to call, parameter will set a return value for the thread +---@nodiscard +---@param fun fun(res: async-res-table): nil function to call, parameter will set a return value for the thread, also contains methods for thread managment ---@return async-table function thread.async(fun) end @@ -56,6 +74,7 @@ function buffer.set(T, value) end ---@return nil function buffer.mod(T, fun) end +---@nodiscard ---puts a value into a atomic thread-safe buffer ---@param value any ---@return buffer-table @@ -86,4 +105,8 @@ function mutex.free(T) end ---@return mutex-table function thread.mutex() end +---puts the thread to sleep for N microseconds +---@param N integer +function thread.usleep(N) end + return thread @@ -58,8 +58,6 @@ for working on the code base, i recommend using bear to generate compile_command * better tests
* rewrite docs
-
- * net mostly complete
* portability (memmem)
diff --git a/src/thread.c b/src/thread.c index 2a2de39..8dcfe2a 100644 --- a/src/thread.c +++ b/src/thread.c @@ -15,9 +15,9 @@ struct thread_info {
str* function;
lua_State* L;
- int return_count, done;
+ int return_count, done, request_close;
pthread_t tid;
- pthread_mutex_t* lock, *ready_lock;
+ pthread_mutex_t* lock, *ready_lock, *close_lock;
pthread_cond_t* cond;
};
@@ -107,6 +107,37 @@ int l_res(lua_State* L){ return 1;
}
+int _res_testclose(lua_State* L){
+ lua_pushstring(L, "_");
+ lua_gettable(L, 1);
+ struct thread_info* info = lua_touserdata(L, -1);
+
+ pthread_mutex_lock(&*info->close_lock);
+ pthread_cond_signal(&*info->cond);
+
+ if(info->request_close){
+ info->done = 1;
+
+ pthread_mutex_unlock(&*info->lock);
+ pthread_mutex_unlock(&*info->close_lock);
+
+ pthread_exit(NULL);
+ }
+
+ pthread_mutex_unlock(&*info->close_lock);
+
+ return 0;
+}
+
+void _res_testclose_debug(lua_State* L, lua_Debug* d){
+ _res_testclose(L);
+}
+
+int _res_autoclose(lua_State* L){
+ lua_sethook(L, _res_testclose_debug, LUA_HOOKLINE, 1);
+ return 0;
+}
+
void _thread_exit_signal(int i){
pthread_exit(NULL);
}
@@ -116,10 +147,6 @@ void* handle_thread(void* _args){ lua_State* L = args->L;
pthread_mutex_lock(&*args->lock);
-#ifdef SUPPORTS_PTHREAD_CANCEL
- pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
-#endif
-
signal(SIGUSR1, _thread_exit_signal);
pthread_detach(args->tid);
@@ -130,6 +157,8 @@ void* handle_thread(void* _args){ lua_newtable(L);
int res_idx = lua_gettop(L);
+ luaI_tsetcf(L, res_idx, "testclose", _res_testclose);
+ luaI_tsetcf(L, res_idx, "autoclose", _res_autoclose);
luaI_tsetlud(L, res_idx, "_", args);
lua_newtable(L);
@@ -149,6 +178,10 @@ void* handle_thread(void* _args){ args->done = 1;
pthread_mutex_unlock(&*args->lock);
+ pthread_mutex_lock(&*args->close_lock);
+ pthread_cond_signal(&*args->cond);
+ pthread_mutex_unlock(&*args->close_lock);
+
return NULL;
}
@@ -159,7 +192,6 @@ int _thread_await(lua_State* L){ if(info->L == NULL) luaI_error(L, -1, "thread was already closed")
if(info->tid == 0) luaI_error(L, -2, "thread was killed early")
- //maybe error here if tid is zero
pthread_mutex_lock(&*info->lock);
env_table(info->L, 0);
@@ -202,11 +234,7 @@ int _thread_clean(lua_State* L){ luaI_tsetnil(L, 1, "_");
if(info->tid != 0 && !info->done){
-#ifdef SUPPORTS_PTHREAD_CANCEL
- pthread_cancel(info->tid);
-#else
pthread_kill(info->tid, SIGUSR1);
-#endif
}
//lua_gc(info->L, LUA_GCRESTART);
@@ -218,6 +246,11 @@ int _thread_clean(lua_State* L){ pthread_mutex_destroy(&*info->lock);
free(info->lock);
+ pthread_mutex_destroy(&*info->close_lock);
+ free(info->close_lock);
+
+ pthread_cond_destroy(&*info->cond);
+ free(info->cond);
free(info);
}
@@ -236,19 +269,22 @@ int _thread_kill(lua_State* L){ }
int _thread_close(lua_State* L){
-#ifdef SUPPORTS_PTHREAD_CANCEL
-
lua_pushstring(L, "_");
lua_gettable(L, 1);
struct thread_info* info = lua_touserdata(L, -1);
- if(info->tid != 0) pthread_cancel(info->tid);
+ if(info->tid == 0) return 0;
+
+ pthread_mutex_lock(&*info->close_lock);
+
+ info->request_close = 1;
+
+ pthread_cond_wait(&*info->cond, &*info->close_lock);
+ pthread_mutex_unlock(&*info->close_lock);
+
info->tid = 0;
return 0;
-#else
- return _thread_kill(L);
-#endif
}
int l_async(lua_State* oL){
@@ -264,6 +300,9 @@ int l_async(lua_State* oL){ pthread_mutex_init(&*args->lock, NULL);
args->ready_lock = malloc(sizeof * args->ready_lock);
pthread_mutex_init(&*args->ready_lock, NULL);
+ args->close_lock = malloc(sizeof * args->close_lock);
+ pthread_mutex_init(&*args->close_lock, NULL);
+
args->return_count = 0;
args->cond = malloc(sizeof * args->cond);
pthread_cond_init(&*args->cond, NULL);
@@ -279,8 +318,6 @@ int l_async(lua_State* oL){ pthread_cond_wait(&*args->cond, &*args->ready_lock);
pthread_mutex_unlock(&*args->ready_lock);
- pthread_cond_destroy(&*args->cond);
- free(args->cond);
pthread_mutex_destroy(&*args->ready_lock);
free(args->ready_lock);
@@ -508,8 +545,11 @@ int l_buffer(lua_State* L){ return 1;
}
-void _lua_getfenv(lua_State* L){
+int l_usleep(lua_State* L){
+ uint64_t n = lua_tonumber(L, 1);
+ usleep(n);
+ return 0;
}
int l_testcopy(lua_State* L){
diff --git a/src/thread.h b/src/thread.h index f0435eb..8061971 100644 --- a/src/thread.h +++ b/src/thread.h @@ -1,14 +1,11 @@ #include "lua.h"
#include "config.h"
-#ifndef __ANDROID__
-#define SUPPORTS_PTHREAD_CANCEL
-#endif
-
int l_async(lua_State*);
int l_buffer(lua_State*);
int l_testcopy(lua_State*);
int l_mutex(lua_State*);
+int l_usleep(lua_State*);
void lib_thread_clean();
@@ -19,6 +16,7 @@ static const luaL_Reg thread_function_list [] = { {"buffer",l_buffer},
{"testcopy",l_testcopy},
{"mutex", l_mutex},
+ {"usleep", l_usleep},
{NULL,NULL}
};
|
