From 5c694205b03bbe8e887a1d0c55d467844f4103ce Mon Sep 17 00:00:00 2001 From: Richard Ore Date: Tue, 15 Oct 2024 01:15:58 +0100 Subject: [PATCH] update thread --- libs/thread.b | 109 ++++++++++++++++++++++++++++++------------ src/standard/thread.c | 26 +++++++--- 2 files changed, 98 insertions(+), 37 deletions(-) diff --git a/libs/thread.b b/libs/thread.b index b9b26cfd..8c624b06 100644 --- a/libs/thread.b +++ b/libs/thread.b @@ -19,12 +19,15 @@ def _is_not_main_thread(id) {id } /** + * The thread class exposes methods to manage creating, running, + * and controlling threads. + * * @class */ class Thread { - var _fn - var _fn_arity + var _delegate + var _delegate_arity var _args var _name @@ -37,17 +40,17 @@ class Thread { var _detached = false /** - * The function passed to the constructor may accept zero or more - * parameters. When it accepts no parameter, the function will be - * called without any argument when run otherwise, it will be - * called with as many argument as it can receive. + * The delegate function passed to the constructor may accept zero + * or more parameters. When it accepts no parameter, the function + * will be called without any argument when run otherwise, it will + * be called with as many argument as it can receive. * - * When a function accepts arguments, the first argument passed + * When a delegate accepts arguments, the first argument passed * will always be the thread object itself followed by the arguments * it received from start. * * For example, in the following thread execution, the first - * parameter _t_ in the function will receive the thread object + * parameter _t_ in the delegate will receive the thread object * itself. * * ```blade @@ -58,10 +61,11 @@ class Thread { * th.start(21) * ``` * - * The function doesn't raise an exception because parameter _t_ never - * received the `start()` argument but the thread itself. In the next - * example, the function accepts the start argument. Note that the start - * argument was received starting from the second argument. + * The delegate function doesn't raise an exception because parameter + * _t_ never received the `start()` argument but the thread itself. + * In the next example, the function accepts the start argument. Note + * that the start argument was received starting from the second + * argument. * * ```blade * var th = Thread(@(t, balance) { @@ -71,15 +75,26 @@ class Thread { * th.start(21) * ``` * - * @params function fn + * The optional second parameter allows us to set the size of the stack + * used for the thread when started. + * + * @param function delegate + * @param number? stack_size * @constructor */ - Thread(fn) { - if !is_function(fn) - raise Exception('function(1..) expected, ${typeof(fn)} given') + Thread(delegate, stack_size) { + if !is_function(delegate) + raise Exception('function(1..) expected, ${typeof(delegate)} given') - self._fn = fn - self._fn_arity = reflect.get_function_metadata(fn).arity + self._delegate = delegate + self._delegate_arity = reflect.get_function_metadata(delegate).arity + + # set stack size if given. + # we're falling back on the type check done by set_stack_size + # here... + if stack_size != nil { + self.set_stack_size(stack_size) + } } /** @@ -92,7 +107,7 @@ class Thread { * * > **NOTE:** A thread can only be started once. * - * @params any... args + * @param any... args */ start(...) { self.start_from_list(__args__) @@ -101,7 +116,7 @@ class Thread { /** * Same as `start()` but takes the argument from a list instead. * - * @params list args + * @param list args */ start_from_list(args) { if !is_list(args) @@ -115,9 +130,9 @@ class Thread { args = [self] + args # only pass on arguments that the run function is able to accept. - args = args[0,self._fn_arity] + args = args[0,self._delegate_arity] - self._ptr = _thread.new(self._fn, args) + self._ptr = _thread.new(self._delegate, args) self._started = _thread.start(self._ptr, self._size) } @@ -133,7 +148,13 @@ class Thread { */ dispose() { assert self._ptr, 'thread not started' - return _thread.dispose(self._ptr) + + if _thread.dispose(self._ptr) { + self._ptr = nil + return true + } + + return false } /** @@ -202,9 +223,6 @@ class Thread { self._joined = false } - # TODO: Implement - try_await() {} - /** * Causes the current thread to sleep for the specified number of seconds. * @@ -331,19 +349,50 @@ class Thread { return _thread.get_id() } - # TODO: Implement - is_alive() {} + /** + * Returns true if the thread is started and alive (running) + * or false if not. + * + * @returns bool + */ + is_alive() { + if self._ptr { + return self._started and _thread.is_alive(self._ptr) + } + + return false + } +} + + +/** + * Returns a new instance of Thread. + * + * @param function delegate + * @param number? stack_size + * @see Constructor + * @returns Thread + */ +def thread(delegate, stack_size) { + return Thread(delegate, stack_size) } -def start(function, args) { +/** + * Creates a new thread and automatically starts the thread + * using the default options and arguments. + * + * @param function delegate + * @param list args + */ +def start(delegate, args) { if args == nil args = [] # we're deliberately not checking the arguments here # because the thread initializer and start_from_list function # will take care of that on their own. - var thread = Thread(function) + var thread = Thread(delegate) thread.start_from_list(args) return thread } diff --git a/src/standard/thread.c b/src/standard/thread.c index 97fccbb3..91f8ed5a 100644 --- a/src/standard/thread.c +++ b/src/standard/thread.c @@ -247,11 +247,14 @@ DECLARE_MODULE_METHOD(thread__set_name) { RETURN_BOOL(pthread_setname_np(AS_C_STRING(args[1])) == 0); #else b_thread_handle *thread = AS_PTR(args[0])->pointer; + if(thread != NULL && thread->vm != NULL) { # if defined(PTHREAD_MAX_NAMELEN_NP) && PTHREAD_MAX_NAMELEN_NP == 16 - RETURN_BOOL(pthread_setname_np(thread->thread, AS_C_STRING(args[1]), NULL) == 0); + RETURN_BOOL(pthread_setname_np(thread->thread, AS_C_STRING(args[1]), NULL) == 0); # else - RETURN_BOOL(pthread_setname_np(thread->thread, AS_C_STRING(args[1])) == 0); + RETURN_BOOL(pthread_setname_np(thread->thread, AS_C_STRING(args[1])) == 0); # endif + } + RETURN_FALSE; #endif } @@ -260,16 +263,17 @@ DECLARE_MODULE_METHOD(thread__get_name) { ENFORCE_ARG_TYPE(get_name, 0, IS_PTR); b_thread_handle *thread = AS_PTR(args[0])->pointer; - char buffer[255]; - if(pthread_getname_np(thread->thread, buffer, 255) == 0) { - RETURN_STRING(buffer); + if(thread != NULL && thread->vm != NULL) { + char buffer[255]; + if(pthread_getname_np(thread->thread, buffer, 255) == 0) { + RETURN_STRING(buffer); + } } RETURN_VALUE(EMPTY_STRING_VAL); } -uint64_t get_thread_id(void) -{ +uint64_t get_thread_id(void) { #if defined(__linux__) return syscall(SYS_gettid); #elif defined(__FreeBSD__) @@ -300,6 +304,13 @@ DECLARE_MODULE_METHOD(thread__yield) { RETURN_BOOL(sched_yield() == 0); } +DECLARE_MODULE_METHOD(thread__is_alive) { + ENFORCE_ARG_COUNT(is_alive, 1); + ENFORCE_ARG_TYPE(get_name, 0, IS_PTR); + b_thread_handle *thread = AS_PTR(args[0])->pointer; + RETURN_BOOL(thread != NULL && thread->vm != NULL); +} + CREATE_MODULE_LOADER(thread) { static b_func_reg module_functions[] = { {"new", false, GET_MODULE_METHOD(thread__new)}, @@ -311,6 +322,7 @@ CREATE_MODULE_LOADER(thread) { {"set_name", false, GET_MODULE_METHOD(thread__set_name)}, {"get_name", false, GET_MODULE_METHOD(thread__get_name)}, {"get_id", false, GET_MODULE_METHOD(thread__get_id)}, + {"is_alive", false, GET_MODULE_METHOD(thread__is_alive)}, {NULL, false, NULL}, };