diff --git a/lib/uv/event-loop.kk b/lib/uv/event-loop.kk index 0f3dbaa69..9aba80f1e 100644 --- a/lib/uv/event-loop.kk +++ b/lib/uv/event-loop.kk @@ -26,7 +26,9 @@ pub extern clear-timeout( tid : any) : io-noexn () // Runs an async action on the default uv event loop, and exceptions at the top level exit the async loop. pub fun default-async-uv(action: () -> a): io a - val result = ref(Error(Exception("Unreachable", ExnInternal("Unreachable")))) + val result = ref(Error(Exception( + "uv event loop returned without fully executing koka code (missing callback or deadlock?)" + , ExnInternal("Unreachable")))) val _ = with default-event-loop with @default-async diff --git a/lib/uv/inline/event-loop.c b/lib/uv/inline/event-loop.c index 9d2bab390..6bd988575 100644 --- a/lib/uv/inline/event-loop.c +++ b/lib/uv/inline/event-loop.c @@ -57,10 +57,45 @@ void kk_uv_loop_run(kk_context_t* _ctx){ } } +static char* kk_uv_handle_type_str(uv_handle_t* handle) { + switch (handle->type) { + case UV_UNKNOWN_HANDLE: return "UNKNOWN"; + case UV_ASYNC: return "ASYNC"; + case UV_CHECK: return "CHECK"; + case UV_FS_EVENT: return "FS_EVENT"; + case UV_FS_POLL: return "FS_POLL"; + case UV_HANDLE: return "HANDLE"; + case UV_IDLE: return "IDLE"; + case UV_NAMED_PIPE: return "NAMED_PIPE"; + case UV_POLL: return "POLL"; + case UV_PREPARE: return "PREPARE"; + case UV_PROCESS: return "PROCESS"; + case UV_STREAM: return "STREAM"; + case UV_TCP: return "TCP"; + case UV_TIMER: return "TIMER"; + case UV_TTY: return "TTY"; + case UV_UDP: return "UDP"; + case UV_SIGNAL: return "SIGNAL"; + case UV_FILE: return "FILE"; + default: return "INVALID"; + } +} + +static void kk_uv_loop_walk_cb(uv_handle_t* handle, void* arg) { + const char* closing_msg = uv_is_closing(handle) ? " [CLOSING]" : ""; + const char* active_msg = uv_is_active(handle) ? " [ACTIVE]" : ""; + kk_warning_message(" - %s handle%s%s\n", kk_uv_handle_type_str(handle), active_msg, closing_msg); +} + static void kk_uv_loop_close(kk_context_t* _ctx) { int ret = uv_loop_close(uvloop()); if (ret != 0) { - kk_warning_message("Event loop closed %s\n", uv_err_name(ret)); + if (ret == UV_EBUSY) { + kk_warning_message("Event loop closed with open child handles:\n"); + uv_walk(uvloop(), kk_uv_loop_walk_cb, NULL); + } else { + kk_warning_message("Event loop close returned error: %s\n", uv_err_name(ret)); + } } kk_free(uvloop(), _ctx); } @@ -118,6 +153,5 @@ kk_box_t kk_set_timeout(kk_function_t cb, int64_t time, kk_context_t* _ctx) { kk_unit_t kk_clear_timeout(kk_box_t boxed_timer, kk_context_t* _ctx) { kk_uv_timer__timer timer = kk_uv_timer__timer_unbox(boxed_timer, KK_OWNED, _ctx); kk_uv_timer_stop(timer, _ctx); - kk_uv_timer_release_callback(timer, _ctx); return kk_Unit; } diff --git a/lib/uv/inline/timer.c b/lib/uv/inline/timer.c index 73933da38..69d322295 100644 --- a/lib/uv/inline/timer.c +++ b/lib/uv/inline/timer.c @@ -7,12 +7,13 @@ void kk_handle_free(void *p, kk_block_t *block, kk_context_t *_ctx) { kk_free(block, kk_context()); // Free the block memory } -#define kk_tm_to_uv(hnd) kk_owned_handle_to_uv_handle(wasm_timer, hnd) +#define kk_tm_borrow_internal(hnd) kk_borrow_internal_as(wasm_timer, hnd) EMSCRIPTEN_KEEPALIVE void wasm_timer_callback(kk_wasm_timer_t* timer_info){ kk_context_t* _ctx = kk_get_context(); kk_function_t callback = timer_info->callback; if (timer_info->repeat_ms == 0) { + timer_info->callback = kk_function_null(kk_context()); kk_unit_t res = kk_unit_callback(callback, kk_context()); return; } else { @@ -47,30 +48,31 @@ EM_JS(void, stop_timer, (int timer, bool repeating), { kk_uv_timer__timer kk_wasm_timer_init(kk_context_t* _ctx) { kk_wasm_timer_t* timer_info = kk_malloc(sizeof(kk_wasm_timer_t), kk_context()); - kk_uv_timer__timer t = uv_handle_to_owned_kk_handle(timer_info, kk_handle_free, timer, Timer); + kk_box_t timer_box = kk_cptr_raw_box(&kk_handle_free, (void*)timer_info, kk_context()); + kk_uv_timer__timer t = kk_uv_timer__new_Timer(timer_box, kk_context()); timer_info->callback = kk_function_null(kk_context()); return t; } -kk_unit_t kk_wasm_timer_finish(kk_uv_timer__timer timer, kk_context_t* _ctx) { - kk_wasm_timer_t* timer_info = kk_tm_to_uv(timer); - if (kk_likely(!kk_function_is_null(timer_info->callback, kk_context()))) { - kk_function_drop(timer_info->callback, kk_context()); - } - kk_uv_timer__timer_drop(timer, kk_context()); - return kk_Unit; -} - kk_unit_t kk_wasm_timer_stop(kk_uv_timer__timer timer, kk_context_t* _ctx) { - kk_wasm_timer_t* timer_info = kk_tm_to_uv(timer); + kk_wasm_timer_t* timer_info = kk_tm_borrow_internal(timer); if (kk_likely(timer_info->timer != 0)) { stop_timer(timer_info->timer, timer_info->repeat_ms != 0); } + if (kk_likely(!kk_function_is_null(timer_info->callback, kk_context()))) { + kk_function_drop(timer_info->callback, kk_context()); + timer_info->callback = kk_function_null(kk_context()); + } return kk_Unit; } kk_std_core_exn__error kk_wasm_timer_start(kk_uv_timer__timer timer, int64_t timeout, int64_t repeat, kk_function_t callback, kk_context_t* _ctx) { - kk_wasm_timer_t* timer_info = kk_tm_to_uv(timer); + kk_wasm_timer_t* timer_info = kk_tm_borrow_internal(timer); + if (kk_unlikely(!kk_function_is_null(timer_info->callback, kk_context()))) { + // If there's already a callback, the timer is still busy on a previous request + kk_function_drop(callback, kk_context()); + return kk_uv_error_from_errno(UV_EBUSY, kk_context()); + } timer_info->callback = callback; timer_info->repeat_ms = repeat; timer_info->timer = start_timer(timer_info, timeout, repeat); @@ -79,33 +81,26 @@ kk_std_core_exn__error kk_wasm_timer_start(kk_uv_timer__timer timer, int64_t tim #else -#define kk_tm_to_uv(hnd) kk_owned_handle_to_uv_handle(timer, hnd) +#define kk_tm_borrow_internal(hnd) kk_borrow_internal_as(timer, hnd) // Initialize the timer handle kk_uv_timer__timer kk_libuv_timer_init(kk_context_t* _ctx) { kk_timer_t* handle = kk_malloc(sizeof(kk_timer_t), kk_context()); handle->callback = kk_function_null(kk_context()); // Wrap the uv / kk struct in a reference counted box value type - kk_uv_timer__timer t = uv_handle_to_owned_kk_handle(handle, kk_timer_free, timer, Timer); - uv_timer_init(uvloop(), (uv_timer_t*)handle); // Timer initialization never fails + kk_uv_timer__timer t = kk_uv_timer__new_Timer(kk_timer_box(handle, _ctx), _ctx); + uv_timer_init(uvloop(), &handle->uv); // Timer initialization never fails return t; } -// Stop / pause the timer (doesn't clean up) - the timer can be restarted with the same callback with kk_libuv_timer_again +// Stop timer and remove callback - the timer can be reused with kk_libuv_timer_start kk_unit_t kk_libuv_timer_stop(kk_uv_timer__timer timer, kk_context_t* _ctx) { - uv_timer_t* uv_timer = (uv_timer_t*)kk_tm_to_uv(timer); - uv_timer_stop(uv_timer); - return kk_Unit; -} - -// Actually clean up the timer -// This drops the callback first in case it is holding onto the timer - as it does for uv/timer/timer() -kk_unit_t kk_libuv_timer_finish(kk_uv_timer__timer timer, kk_context_t* _ctx) { - kk_timer_t* kk_timer = kk_tm_to_uv(timer); + kk_timer_t* kk_timer = kk_tm_borrow_internal(timer); if (kk_likely(!kk_function_is_null(kk_timer->callback, kk_context()))) { kk_function_drop(kk_timer->callback, kk_context()); + kk_timer->callback = kk_function_null(kk_context()); } - kk_uv_timer__timer_drop(timer, kk_context()); + uv_timer_stop(&kk_timer->uv); return kk_Unit; } @@ -114,47 +109,35 @@ void kk_uv_timer_unit_callback(uv_timer_t* uv_timer) { kk_context_t* _ctx = kk_get_context(); kk_timer_t* kk_timer = (kk_timer_t*)uv_timer; kk_function_t callback = kk_timer->callback; // Get the callback - if (uv_timer_get_repeat(uv_timer) == 0) { // If this is a one-shot timer, just call the callback - kk_unit_callback(callback, kk_context()); - return; + if (uv_timer_get_repeat(uv_timer) == 0) { // If this is a one-shot timer, remove it + kk_timer->callback = kk_function_null(kk_context()); } else { // Otherwise, we need to dup the callback, as it will be called again callback = kk_function_dup(callback, kk_context()); - kk_unit_callback(callback, kk_context()); - return; } + kk_unit_callback(callback, kk_context()); } kk_std_core_exn__error kk_libuv_timer_start(kk_uv_timer__timer timer, int64_t timeout, int64_t repeat, kk_function_t callback, kk_context_t* _ctx) { - kk_timer_t* uv_timer = kk_tm_to_uv(timer); - // TODO: Drop previous callback if any? - uv_timer->callback = callback; - int status = uv_timer_start((uv_timer_t*)uv_timer, kk_uv_timer_unit_callback, timeout, repeat); - // On error, report, and drop callback - kk_uv_check_err_drops(status, { - uv_timer->callback = kk_function_null(kk_context()); - kk_function_drop(callback, kk_context()); - }) -} - -kk_std_core_exn__error kk_libuv_timer_again(kk_uv_timer__timer timer, kk_context_t* _ctx) { - int status = uv_timer_again((uv_timer_t*)kk_tm_to_uv(timer)); - kk_uv_check(status) -} + kk_timer_t* uv_timer = kk_tm_borrow_internal(timer); + int status = UV_OK; -kk_unit_t kk_libuv_timer_set_repeat(kk_uv_timer__timer timer, int64_t repeat, kk_context_t* _ctx) { - uv_timer_set_repeat((uv_timer_t*)kk_tm_to_uv(timer), repeat); - return kk_Unit; -} + if (kk_unlikely(!kk_function_is_null(uv_timer->callback, kk_context()))) { + // If there's already a callback, the timer is still busy with a previous request + status = UV_EBUSY; + } else { + uv_timer->callback = callback; + status = uv_timer_start((uv_timer_t*)uv_timer, kk_uv_timer_unit_callback, timeout, repeat); + } -int64_t kk_libuv_timer_get_repeat(kk_uv_timer__timer timer, kk_context_t* _ctx) { - uint64_t repeat = uv_timer_get_repeat((uv_timer_t*)kk_tm_to_uv(timer)); - return repeat; + if (status != UV_OK) { + uv_timer->callback = kk_function_null(kk_context()); + kk_function_drop(callback, kk_context()); + return kk_uv_error_from_errno(status, kk_context()); + } else { + return kk_std_core_exn__new_Ok(kk_unit_box(kk_Unit), kk_context()); + } } -int64_t kk_libuv_timer_get_due_in(kk_uv_timer__timer timer, kk_context_t* _ctx) { - uint64_t due_in = uv_timer_get_due_in((uv_timer_t*)kk_tm_to_uv(timer)); - return due_in; -} #endif ////////////////////////////////////////////////////// @@ -177,14 +160,6 @@ kk_unit_t kk_timer_stop(kk_uv_timer__timer timer, kk_context_t* _ctx) { #endif } -kk_unit_t kk_timer_finish(kk_uv_timer__timer timer, kk_context_t* _ctx) { - #ifdef __EMSCRIPTEN__ - return kk_wasm_timer_finish(timer, kk_context()); - #else - return kk_libuv_timer_finish(timer, kk_context()); - #endif -} - kk_std_core_exn__error kk_timer_start(kk_uv_timer__timer timer, int64_t timeout, int64_t repeat, kk_function_t callback, kk_context_t* _ctx) { #ifdef __EMSCRIPTEN__ return kk_wasm_timer_start(timer, timeout, repeat, callback, kk_context()); @@ -192,35 +167,3 @@ kk_std_core_exn__error kk_timer_start(kk_uv_timer__timer timer, int64_t timeout, return kk_libuv_timer_start(timer, timeout, repeat, callback, kk_context()); #endif } - -kk_std_core_exn__error kk_timer_again(kk_uv_timer__timer timer, kk_context_t* _ctx) { - #ifdef __EMSCRIPTEN__ - return kk_std_core_exn__new_Ok(kk_unit_box(kk_Unit), kk_context()); - #else - return kk_libuv_timer_again(timer, kk_context()); - #endif -} - -kk_unit_t kk_timer_set_repeat(kk_uv_timer__timer timer, int64_t repeat, kk_context_t* _ctx) { - #ifdef __EMSCRIPTEN__ - return kk_Unit; - #else - return kk_libuv_timer_set_repeat(timer, repeat, kk_context()); - #endif -} - -int64_t kk_timer_get_repeat(kk_uv_timer__timer timer, kk_context_t* _ctx) { - #ifdef __EMSCRIPTEN__ - return -1; - #else - return kk_libuv_timer_get_repeat(timer, kk_context()); - #endif -} - -int64_t kk_timer_get_due_in(kk_uv_timer__timer timer, kk_context_t* _ctx) { - #ifdef __EMSCRIPTEN__ - return -1; - #else - return kk_libuv_timer_get_due_in(timer, kk_context()); - #endif -} \ No newline at end of file diff --git a/lib/uv/inline/utils.c b/lib/uv/inline/utils.c index 903f49472..b08a86c8f 100644 --- a/lib/uv/inline/utils.c +++ b/lib/uv/inline/utils.c @@ -187,7 +187,7 @@ static kk_uv_utils__uv_status_code kk_uv_status_to_status_code(int32_t status, k } // Map a libuv status code to a Koka Error value with uv status code enum -kk_std_core_exn__error kk_uv_async_error_from_errno( int err, kk_context_t* _ctx ) { +kk_std_core_exn__error kk_uv_error_from_errno( int err, kk_context_t* _ctx ) { kk_uv_utils__uv_status_code code = kk_uv_status_to_status_code(err, _ctx); kk_string_t msg = kk_uv_utils_message(code, _ctx); return kk_std_core_exn__new_Error( kk_std_core_exn__new_Exception( msg, kk_uv_utils__new_AsyncExn(kk_reuse_null, 0, code, _ctx), _ctx), _ctx ); diff --git a/lib/uv/inline/utils.h b/lib/uv/inline/utils.h index a9a4ebb31..4f46ec25e 100644 --- a/lib/uv/inline/utils.h +++ b/lib/uv/inline/utils.h @@ -1,16 +1,7 @@ -// Derive a handle type from a uv type name -#define kk_uv_handle_tp(uv_tp) kk_##uv_tp##_t - -// Convert to/from Koka boxed wrapper types, where the `internal` field is an `any` boxed & reference counted C pointer -#define kk_owned_handle_to_uv_handle(uv_tp, hndl) \ - ((kk_uv_handle_tp(uv_tp)*)kk_cptr_unbox_borrowed(hndl.internal, kk_context())) -// `mod` is the module name, `uv_tp` is the uv handle type -// `free_fn` is the function to handle freeing the uv handle. -#define uv_handle_to_owned_kk_handle(hndl, free_fn, mod, Kk_tp) \ - kk_uv_##mod##__new_##Kk_tp(kk_cptr_raw_box(&free_fn, (void*)hndl, kk_context()), kk_context()) -// Since the wrapper type is a value struct, we sometimes need to `box/unbox` them as well. -#define uv_handle_to_owned_kk_handle_box(hndl, free_fn, mod, kk_tp, Kk_tp) \ - kk_uv_##mod##__##kk_tp##_box(uv_handle_to_owned_kk_handle(hndl,free_fn,mod,Kk_tp), kk_context()) +// Borrow the `internal` struct of a koka wrapper, +// which should be a pointer to a kk_uv_* wrapper struct +#define kk_borrow_internal_as(uv_tp, hndl) \ + uv_##uv_tp##_as_kk((uv_##uv_tp##_t*)kk_cptr_unbox_borrowed(hndl.internal, kk_context()), kk_context()) // Call a unit callback static inline kk_unit_t kk_unit_callback(kk_function_t callback, kk_context_t* _ctx) { @@ -30,44 +21,39 @@ uv_loop_t* uvloop(); // UV Okay status code #define UV_OK 0 // Map a libuv status code to a Koka Error value with uv status code enum -kk_std_core_exn__error kk_uv_async_error_from_errno( int err, kk_context_t* ctx ); -// Call a callback with a uv status code wrapped in an Error or Ok (can't be a function due to the type of result not being known) -#define kk_uv_exn_callback(callback, result) \ - kk_function_call(kk_unit_t, (kk_function_t, kk_std_core_exn__error, kk_context_t*), callback, (callback, result, kk_context()), kk_context()); -// Call a callback with a uv ok wrapped in an Ok (can't be function due to the type of result not being known) -#define kk_uv_okay_callback(callback, result) \ - kk_uv_exn_callback(callback, kk_std_core_exn__new_Ok(result, kk_context())) -// Call a callback with a uv status code enum value (can't be static inline function due to kk_uv_utils__uv_status_code not being defined yet) -#define kk_uv_status_code_callback(callback, status) \ - kk_function_call(kk_unit_t, (kk_function_t, kk_uv_utils__uv_status_code, kk_context_t*), callback, (callback, kk_uv_utils_int_fs_status_code(status, _ctx), kk_context()), kk_context()); -// Call a callback with a uv error wrapped in an Error -static inline void kk_uv_error_callback(kk_function_t callback, int result, kk_context_t* _ctx) { - kk_uv_exn_callback(callback, kk_uv_async_error_from_errno(result, kk_context())); -} +kk_std_core_exn__error kk_uv_error_from_errno( int err, kk_context_t* ctx ); // Invariant: handles only have a single outstanding callback // multiple concurrent calls using the same handle not supported // TODO: Relax this restriction? #define kk_uv_handle(uv_hnd_tp) \ - typedef struct { \ + typedef struct kk_##uv_hnd_tp##_s { \ /* The uv handle struct (embedded as first member) */ \ - uv_##uv_hnd_tp##_t handle; \ + uv_##uv_hnd_tp##_t uv; \ /* The Koka callback function Needs to be dupped every time it is called, so that it always can be called again by libuv */ \ kk_function_t callback; \ - } kk_uv_handle_tp(uv_hnd_tp); \ + } kk_##uv_hnd_tp##_t; \ + /* get a pointer to the kk_* wrapper containing the given uv struct */ \ + static inline kk_##uv_hnd_tp##_t* uv_##uv_hnd_tp##_as_kk(void *p, kk_context_t *_ctx) { \ + return (kk_##uv_hnd_tp##_t *) (((char*)p) - offsetof(kk_##uv_hnd_tp##_t, uv)); \ + } \ /* Handles freeing a kk_uv_handle struct (see definition below) */ \ static inline void kk_##uv_hnd_tp##_free(void *p, kk_block_t *block, kk_context_t *_ctx) { \ - uv_handle_t *handle = (uv_handle_t *)p; \ - kk_uv_handle_tp(uv_hnd_tp)* hndcb = (kk_uv_handle_tp(uv_hnd_tp)*)handle; \ + kk_##uv_hnd_tp##_t* kk_handle = (kk_##uv_hnd_tp##_t*)p; \ + uv_handle_t *uv_handle = (uv_handle_t *)(&kk_handle->uv); \ /* the callback should have been cleaned up prior to this point */ \ - kk_assert_internal(kk_function_is_null(hndcb->callback, kk_context())); \ + kk_assert_internal(kk_function_is_null(kk_handle->callback, kk_context())); \ /* block will be freed by kk_uv_handle_close_callback after uv has cleaned up its state */ \ - handle->data = block; \ - uv_close(handle, &kk_uv_handle_close_callback); \ - } // define the free function + uv_handle->data = block; \ + uv_close(uv_handle, &kk_uv_handle_close_callback); \ + } \ + /* box a C struct into an `any` koka type */ \ + static inline kk_box_t kk_##uv_hnd_tp##_box(kk_##uv_hnd_tp##_t * hnd, kk_context_t *_ctx) { \ + return kk_cptr_raw_box(&kk_##uv_hnd_tp##_free, (void*)hnd, _ctx); \ + } // This handles actually freeing the memory when the uv_handle is closed @@ -79,53 +65,4 @@ static inline void kk_uv_handle_close_callback(uv_handle_t* handle) { kk_free(handle, kk_context()); // Free the struct memory } -// TODO: Change all apis to return status code or return error, not a mix -// Decide which to use for sync errors versus callbacks - -// If the status is not OK, drop before returning the status code -#define kk_uv_check_status_drops(status, drops) \ - if (kk_unlikely(status < UV_OK)) { \ - do drops while (0); \ - } \ - return kk_uv_utils_int_fs_status_code(status, kk_context()); \ - -// Sometimes the return value is a file descriptor which is why this is a < UV_OK check instead of == UV_OK -#define kk_uv_check_return(err, result) \ - if (kk_unlikely(err < UV_OK)) { \ - return kk_uv_async_error_from_errno(err, kk_context()); \ - } else { \ - return kk_std_core_exn__new_Ok(result, kk_context()); \ - } - -// Typically used to clean up when an error occurs -#define kk_uv_check_return_err_drops(err, result, drops) \ - if (kk_unlikely(err < UV_OK)) { \ - do drops while (0); \ - return kk_uv_async_error_from_errno(err, kk_context()); \ - } else { \ - return kk_std_core_exn__new_Ok(result, kk_context()); \ - } - -// Typically used when cleaning up a handle -#define kk_uv_check_return_ok_drops(err, result, drops) \ - if (kk_unlikely(err < UV_OK)) { \ - return kk_uv_async_error_from_errno(err, kk_context()); \ - } else { \ - do drops while (0); \ - return kk_std_core_exn__new_Ok(result, kk_context()); \ - } - -// Check the uv status code and return a kk_std_core_exn__error Ok or Error -#define kk_uv_check(err) kk_uv_check_return(err, kk_unit_box(kk_Unit)) - -// Check the uv status code and return a kk_std_core_exn__error Ok or Error -// Dropping the references if it was an error -#define kk_uv_check_err_drops(err, drops) \ - kk_uv_check_return_err_drops(err, kk_unit_box(kk_Unit), drops) - -// Check the uv status code and return a kk_std_core_exn__error Ok or Error -// Dropping the references if the result is Okay -#define kk_uv_check_ok_drops(err, drops) \ - kk_uv_check_return_ok_drops(err, kk_unit_box(kk_Unit), drops) - #endif diff --git a/lib/uv/timer.kk b/lib/uv/timer.kk index 9247fe1bc..21846a641 100644 --- a/lib/uv/timer.kk +++ b/lib/uv/timer.kk @@ -34,45 +34,6 @@ pub extern stop(^t: timer): io-noexn () c "kk_timer_stop" js inline "_stop_timer(#1)" -// Release the callback, which if it references the timer will allow it to be garbage collected. -pub extern release-callback(t: timer): io-noexn () - c "kk_timer_finish" - js inline "" - -// Stop the timer, and if it is repeating restart it using the repeat value as the timeout. -// If the timer has never been started before it returns UV_EINVAL -extern again(^t: timer): io-noexn error<()> - c "kk_timer_again" - js inline "" - -// Set the repeat interval value in milliseconds. -// -// The timer will be scheduled to run on the given interval, -// regardless of the callback execution duration, and will follow -// normal timer semantics in the case of a time-slice overrun. -// -// For example, if a 50ms repeating timer first runs for 17ms, -// it will be scheduled to run again 33ms later. If other tasks -// consume more than the 33ms following the first timer callback, -// then the next timer callback will run as soon as possible. -// -// NOTE: If the repeat value is set from a timer callback it does not immediately take effect. -// If the timer was non-repeating before, it will have been stopped. If it was repeating, -// then the old repeat value will have been used to schedule the next timeout -extern set-repeat(^t: timer, repeat: int64): io-noexn () - c "kk_timer_set_repeat" - js inline "" - -extern get-repeat(^t: timer): io-noexn int64 - c "kk_timer_get_repeat" - js inline "" - -// Get the timer due value or 0 if it has expired. -1 is returned on unsupported platforms -// The time is relative to uv_now() -extern get-due-in(^t: timer): io-noexn int64 - c "kk_timer_get_due_in" - js inline "" - // Creates a timer that repeats every `d` duration and calls `f` with the timer as argument. // // The timer stops repeating when `f` returns `False`. @@ -82,7 +43,6 @@ pub fun timer(d: duration, f: (timer) -> io-noexn bool): io timer val res = t.start(ms, ms) fn() if !f(t) then t.stop() - t.release-callback() () match res Ok() -> t diff --git a/lib/uv/utils.kk b/lib/uv/utils.kk index 27f145276..fe968ab3e 100644 --- a/lib/uv/utils.kk +++ b/lib/uv/utils.kk @@ -201,3 +201,88 @@ pub fun message(code: uv-status-code): string UV_EILSEQ -> "illegal byte sequence" UV_ESOCKTNOSUPPORT -> "socket type not supported" UV_EUNATCH -> "protocol driver not attached" + +pub fun show(code: uv-status-code): string + match code + UV_OK -> "UV_OK" + UV_E2BIG -> "UV_E2BIG" + UV_EACCES -> "UV_EACCES" + UV_EADDRINUSE -> "UV_EADDRINUSE" + UV_EADDRNOTAVAIL -> "UV_EADDRNOTAVAIL" + UV_EAFNOSUPPORT -> "UV_EAFNOSUPPORT" + UV_EAGAIN -> "UV_EAGAIN" + UV_EAI_ADDRFAMILY -> "UV_EAI_ADDRFAMILY" + UV_EAI_AGAIN -> "UV_EAI_AGAIN" + UV_EAI_BADFLAGS -> "UV_EAI_BADFLAGS" + UV_EAI_BADHINTS -> "UV_EAI_BADHINTS" + UV_EAI_CANCELED -> "UV_EAI_CANCELED" + UV_EAI_FAIL -> "UV_EAI_FAIL" + UV_EAI_FAMILY -> "UV_EAI_FAMILY" + UV_EAI_MEMORY -> "UV_EAI_MEMORY" + UV_EAI_NODATA -> "UV_EAI_NODATA" + UV_EAI_NONAME -> "UV_EAI_NONAME" + UV_EAI_OVERFLOW -> "UV_EAI_OVERFLOW" + UV_EAI_PROTOCOL -> "UV_EAI_PROTOCOL" + UV_EAI_SERVICE -> "UV_EAI_SERVICE" + UV_EAI_SOCKTYPE -> "UV_EAI_SOCKTYPE" + UV_EALREADY -> "UV_EALREADY" + UV_EBADF -> "UV_EBADF" + UV_EBUSY -> "UV_EBUSY" + UV_ECANCELED -> "UV_ECANCELED" + UV_ECHARSET -> "UV_ECHARSET" + UV_ECONNABORTED -> "UV_ECONNABORTED" + UV_ECONNREFUSED -> "UV_ECONNREFUSED" + UV_ECONNRESET -> "UV_ECONNRESET" + UV_EDESTADDRREQ -> "UV_EDESTADDRREQ" + UV_EEXIST -> "UV_EEXIST" + UV_EFAULT -> "UV_EFAULT" + UV_EFBIG -> "UV_EFBIG" + UV_EHOSTUNREACH -> "UV_EHOSTUNREACH" + UV_EINTR -> "UV_EINTR" + UV_EINVAL -> "UV_EINVAL" + UV_EIO -> "UV_EIO" + UV_EISCONN -> "UV_EISCONN" + UV_EISDIR -> "UV_EISDIR" + UV_ELOOP -> "UV_ELOOP" + UV_EMFILE -> "UV_EMFILE" + UV_EMSGSIZE -> "UV_EMSGSIZE" + UV_ENAMETOOLONG -> "UV_ENAMETOOLONG" + UV_ENETDOWN -> "UV_ENETDOWN" + UV_ENETUNREACH -> "UV_ENETUNREACH" + UV_ENFILE -> "UV_ENFILE" + UV_ENOBUFS -> "UV_ENOBUFS" + UV_ENODEV -> "UV_ENODEV" + UV_ENOENT -> "UV_ENOENT" + UV_ENOMEM -> "UV_ENOMEM" + UV_ENONET -> "UV_ENONET" + UV_ENOPROTOOPT -> "UV_ENOPROTOOPT" + UV_ENOSPC -> "UV_ENOSPC" + UV_ENOSYS -> "UV_ENOSYS" + UV_ENOTCONN -> "UV_ENOTCONN" + UV_ENOTDIR -> "UV_ENOTDIR" + UV_ENOTEMPTY -> "UV_ENOTEMPTY" + UV_ENOTSOCK -> "UV_ENOTSOCK" + UV_ENOTSUP -> "UV_ENOTSUP" + UV_EOVERFLOW -> "UV_EOVERFLOW" + UV_EPERM -> "UV_EPERM" + UV_EPIPE -> "UV_EPIPE" + UV_EPROTO -> "UV_EPROTO" + UV_EPROTONOSUPPORT -> "UV_EPROTONOSUPPORT" + UV_EPROTOTYPE -> "UV_EPROTOTYPE" + UV_ERANGE -> "UV_ERANGE" + UV_EROFS -> "UV_EROFS" + UV_ESHUTDOWN -> "UV_ESHUTDOWN" + UV_ESPIPE -> "UV_ESPIPE" + UV_ESRCH -> "UV_ESRCH" + UV_ETIMEDOUT -> "UV_ETIMEDOUT" + UV_ETXTBSY -> "UV_ETXTBSY" + UV_EXDEV -> "UV_EXDEV" + UV_UNKNOWN -> "UV_UNKNOWN" + UV_EOF -> "UV_EOF" + UV_ENXIO -> "UV_ENXIO" + UV_EMLINK -> "UV_EMLINK" + UV_ENOTTY -> "UV_ENOTTY" + UV_EFTYPE -> "UV_EFTYPE" + UV_EILSEQ -> "UV_EILSEQ" + UV_ESOCKTNOSUPPORT -> "UV_ESOCKTNOSUPPORT" + UV_EUNATCH -> "UV_EUNATCH"