aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/thread.md36
-rw-r--r--src/thread.c42
-rw-r--r--tests/h.lua22
3 files changed, 64 insertions, 36 deletions
diff --git a/docs/thread.md b/docs/thread.md
index 0c8de77..9f868cb 100644
--- a/docs/thread.md
+++ b/docs/thread.md
@@ -28,12 +28,14 @@ llib.thread.unlock(5)
t:await()
```
-## aync **
+## aync
'takes a function which will be ran in a separate thread with a single parameter with thread info
these have the same backend (and limitations) of network threads
+the rej (2nd param) is currently unused
+
```lua
local thread = llib.thread.async(function(res, rej)
local N = 0
@@ -42,7 +44,7 @@ local thread = llib.thread.async(function(res, rej)
end)
```
-### thread function parameters **
+### thread function parameters
as used with "res" above
@@ -52,17 +54,6 @@ as used with "res" above
send a value(s) to thread:await() call then stalls the thread until cleaned
-#### res:send() **
-
-'takes "any" value
-
-send a value which can be retrieved from outside the thread with thread:next()
-
-```lua
-res:send(5)
-res:send("hello")
-```
-
### thread return object **
#### thread:await() **
@@ -70,31 +61,20 @@ res:send("hello")
'optional timeout in ms and boolean whether to keep or not
waits for the thread to return, and returns whatever it returned then closes it, or nil if timeout was exceeded
-if the input is the boolean value true, it will keep the thread alive (otherwise await() can not be called again)
```lua
thread:await() -- value of N (above)
```
```lua
-thread:await(20) -- value of N (above) or nil
-```
-
-```lua
-thread:await(true) -- value of N (above)
-thread:await() -- same
-thread:await() -- error, function above performed cleanup
+thread:await(20) -- value of N (above) or nil and preserves the thread
```
-#### thread:next() **
+#### thread:clean() **
-gets the most oldest value sent using info:send() and pops it
+frees everything related to the thread (including userdata allocated in it!), thread:await() can not be called again, all lua values will still be usable
-```lua
---(continued from above)
-thread:next() -- 5
-thread:next() -- "hello"
-```
+not required to be called, lua gc should call it on its own via __gc
#### thread:kill() **
diff --git a/src/thread.c b/src/thread.c
index 441793b..393d3fb 100644
--- a/src/thread.c
+++ b/src/thread.c
@@ -13,14 +13,15 @@
struct thread_info {
str* function;
lua_State* L;
- int return_count;
+ int return_count, done;
pthread_t tid;
- pthread_cond_t cond;
pthread_mutex_t lock;
};
#include "io.h"
+//give the current thread priority to locking thread_lock_lock (fixes race conds)
+pthread_mutex_t thread_priority_lock = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t thread_lock_lock = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
larray_t* thread_locks = NULL;
@@ -28,6 +29,8 @@ int l_tlock(lua_State* L){
int idx = luaL_checkinteger(L, 1);
pthread_mutex_lock(&thread_lock_lock);
+ pthread_mutex_lock(&thread_priority_lock);
+ pthread_mutex_unlock(&thread_priority_lock);
pthread_mutex_t mutex;
if(thread_locks == NULL) thread_locks = larray_init();
int i = 0;
@@ -37,9 +40,13 @@ int l_tlock(lua_State* L){
larray_set(&thread_locks, idx, (void*)mutex);
} else {
pthread_mutex_t m = (pthread_mutex_t)thread_locks->arr[i].value;
+ pthread_mutex_lock(&thread_priority_lock);
+
pthread_mutex_unlock(&thread_lock_lock);
pthread_mutex_lock(&m);
pthread_mutex_lock(&thread_lock_lock);
+
+ pthread_mutex_unlock(&thread_priority_lock);
thread_locks->arr[i].value = (void*)m;
}
@@ -90,6 +97,7 @@ int l_res(lua_State* L){
void* handle_thread(void* _args){
struct thread_info* args = (struct thread_info*)_args;
lua_State* L = args->L;
+ lua_gc(L, LUA_GCSTOP);
lua_newtable(L);
int res_idx = lua_gettop(L);
@@ -117,20 +125,37 @@ int l_await(lua_State* L){
lua_gettable(L, 1);
struct thread_info* info = lua_touserdata(L, -1);
- pthread_mutex_lock(&info->lock);
+ if(info->L == NULL) p_fatal("thread has already been cleaned");
+ if(!info->done) pthread_mutex_lock(&info->lock);
+ info->done = 1;
for(int i = 0; i != info->return_count; i++){
int ot = lua_gettop(info->L);
lua_pushvalue(info->L, ot - info->return_count + i);
i_dcopy(info->L, L, NULL);
-
+
lua_settop(info->L, ot);
}
return info->return_count;
}
+int l_clean(lua_State* L){
+ lua_pushstring(L, "_");
+ lua_gettable(L, 1);
+ struct thread_info* info = lua_touserdata(L, -1);
+ if(info->L != NULL){
+ lua_gc(info->L, LUA_GCRESTART);
+ lua_gc(info->L, LUA_GCCOLLECT);
+ lua_close(info->L);
+ info->L = NULL;
+ pthread_mutex_destroy(&info->lock);
+ free(info);
+ }
+ return 0;
+}
+
int l_async(lua_State* oL){
lua_State* L = luaL_newstate();
@@ -142,7 +167,6 @@ int l_async(lua_State* oL){
struct thread_info* args = calloc(1, sizeof * args);
args->L = L;
- args->cond = (pthread_cond_t)PTHREAD_COND_INITIALIZER;
args->lock = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&args->lock);
args->return_count = 0;
@@ -157,7 +181,15 @@ int l_async(lua_State* oL){
lua_newtable(oL);
int res_idx = lua_gettop(oL);
luaI_tsetcf(oL, res_idx, "await", l_await);
+ luaI_tsetcf(oL, res_idx, "clean", l_clean);
luaI_tsetlud(oL, res_idx, "_", args);
+
+ lua_newtable(oL);
+ int meta_idx = lua_gettop(oL);
+ luaI_tsetcf(oL, meta_idx, "__gc", l_clean);
+
+ lua_pushvalue(oL, meta_idx);
+ lua_setmetatable(oL, res_idx);
lua_pushvalue(oL, res_idx);
return 1;
}
diff --git a/tests/h.lua b/tests/h.lua
index 9cddc29..b8ccd13 100644
--- a/tests/h.lua
+++ b/tests/h.lua
@@ -1,14 +1,30 @@
require "llib"
llib.thread.lock(1)
+llib.thread.lock(2)
+llib.thread.unlock(2)
local thread_a = llib.thread.async(function (res)
--os.execute("sleep 1")
--print((_G.ll + "hi"):final())
-
- res(_G.llib.crypto.md5("meow"))
+ print("waiting..")
+ llib.thread.lock(1)
+ print("signal!")
+ res(_G.llib.crypto.md5())
print("after")
end)
-print(thread_a:await())
+os.execute("sleep 1")
+llib.thread.unlock(1)
+
+awa = thread_a:await()
+
+os.execute("sleep 1")
+for i=1,999 do
+print((awa + "hi"):final())
+end
+
+thread_a:clean()
+
+print("clean exit")