From 7cdd7cf6eb29c40fd7fb6298ce561421d3e59b8a Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sun, 1 Oct 2023 10:09:23 -0500 Subject: [PATCH] Expose atomic refcounts to be easier to port. This code was duplicate in a few places. --- CHANGELOG.md | 1 + src/core/abstract.c | 20 ++------------------ src/core/capi.c | 34 ++++++++++++++++++++++++++++++++++ src/core/ev.c | 26 +++++--------------------- src/core/state.h | 2 +- src/include/janet.h | 16 +++++++++++++++- 6 files changed, 58 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ce8487b..69546d05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ All notable changes to this project will be documented in this file. ## Unreleased - ??? +- Expose atomic refcount abstraction in janet.h - Add `array/weak` for weak references in arrays - Add support for weak tables via `table/weak`, `table/weak-keys`, and `table/weak-values`. - Fix compiler bug with using the result of `(break x)` expression in some contexts. diff --git a/src/core/abstract.c b/src/core/abstract.c index 84879801..407e9ced 100644 --- a/src/core/abstract.c +++ b/src/core/abstract.c @@ -97,14 +97,6 @@ size_t janet_os_rwlock_size(void) { return sizeof(void *); } -static int32_t janet_incref(JanetAbstractHead *ab) { - return InterlockedIncrement((LONG volatile *) &ab->gc.data.refcount); -} - -static int32_t janet_decref(JanetAbstractHead *ab) { - return InterlockedDecrement((LONG volatile *) &ab->gc.data.refcount); -} - void janet_os_mutex_init(JanetOSMutex *mutex) { InitializeCriticalSection((CRITICAL_SECTION *) mutex); } @@ -157,14 +149,6 @@ size_t janet_os_rwlock_size(void) { return sizeof(pthread_rwlock_t); } -static int32_t janet_incref(JanetAbstractHead *ab) { - return __atomic_add_fetch(&ab->gc.data.refcount, 1, __ATOMIC_RELAXED); -} - -static int32_t janet_decref(JanetAbstractHead *ab) { - return __atomic_add_fetch(&ab->gc.data.refcount, -1, __ATOMIC_RELAXED); -} - void janet_os_mutex_init(JanetOSMutex *mutex) { pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); @@ -212,11 +196,11 @@ void janet_os_rwlock_wunlock(JanetOSRWLock *rwlock) { #endif int32_t janet_abstract_incref(void *abst) { - return janet_incref(janet_abstract_head(abst)); + return janet_atomic_inc(&janet_abstract_head(abst)->gc.data.refcount); } int32_t janet_abstract_decref(void *abst) { - return janet_decref(janet_abstract_head(abst)); + return janet_atomic_dec(&janet_abstract_head(abst)->gc.data.refcount); } #endif diff --git a/src/core/capi.c b/src/core/capi.c index f7500b72..7ca5577e 100644 --- a/src/core/capi.c +++ b/src/core/capi.c @@ -491,6 +491,40 @@ void *janet_optabstract(const Janet *argv, int32_t argc, int32_t n, const JanetA return janet_getabstract(argv, n, at); } +/* Atomic refcounts */ + +JanetAtomicInt janet_atomic_inc(JanetAtomicInt volatile *x) { +#ifdef JANET_WINDOWS + return InterlockedIncrement(x); +#else + return __atomic_add_fetch(x, 1, __ATOMIC_RELAXED); +#endif +} + +JanetAtomicInt janet_atomic_dec(JanetAtomicInt volatile *x) { +#ifdef JANET_WINDOWS + return InterlockedDecrement(x); +#else + return __atomic_add_fetch(x, -1, __ATOMIC_RELAXED); +#endif +} + +JanetAtomicInt64 janet_atomic64_inc(JanetAtomicInt64 volatile *x) { +#ifdef JANET_WINDOWS + return InterlockedDecrement(x); +#else + return __atomic_add_fetch(x, 1, __ATOMIC_RELAXED); +#endif +} + +JanetAtomicInt64 janet_atomic64_dec(JanetAtomicInt64 volatile *x) { +#ifdef JANET_WINDOWS + return InterlockedDecrement64(x); +#else + return __atomic_add_fetch(x, -1, __ATOMIC_RELAXED); +#endif +} + /* Some definitions for function-like macros */ JANET_API JanetStructHead *(janet_struct_head)(JanetStruct st) { diff --git a/src/core/ev.c b/src/core/ev.c index e651b497..c3b7cd0e 100644 --- a/src/core/ev.c +++ b/src/core/ev.c @@ -633,27 +633,11 @@ void janet_addtimeout(double sec) { } void janet_ev_inc_refcount(void) { -#ifdef JANET_WINDOWS -#ifdef JANET_64 - InterlockedIncrement64((int64_t volatile *) &janet_vm.extra_listeners); -#else - InterlockedIncrement((int32_t volatile *) &janet_vm.extra_listeners); -#endif -#else - __atomic_add_fetch(&janet_vm.extra_listeners, 1, __ATOMIC_RELAXED); -#endif + janet_atomic64_inc(&janet_vm.listener_count); } void janet_ev_dec_refcount(void) { -#ifdef JANET_WINDOWS -#ifdef JANET_64 - InterlockedDecrement64((int64_t volatile *) &janet_vm.extra_listeners); -#else - InterlockedDecrement((int32_t volatile *) &janet_vm.extra_listeners); -#endif -#else - __atomic_add_fetch(&janet_vm.extra_listeners, -1, __ATOMIC_RELAXED); -#endif + janet_atomic64_dec(&janet_vm.listener_count); } /* Channels */ @@ -1334,7 +1318,7 @@ void janet_loop1_impl(int has_timeout, JanetTimestamp timeout); int janet_loop_done(void) { return !((janet_vm.spawn.head != janet_vm.spawn.tail) || janet_vm.tq_count || - janet_vm.extra_listeners); + janet_vm.listener_count); } JanetFiber *janet_loop1(void) { @@ -1396,7 +1380,7 @@ JanetFiber *janet_loop1(void) { } /* Poll for events */ - if (janet_vm.tq_count || janet_vm.extra_listeners) { + if (janet_vm.tq_count || janet_vm.listener_count) { JanetTimeout to; memset(&to, 0, sizeof(to)); int has_timeout; @@ -1415,7 +1399,7 @@ JanetFiber *janet_loop1(void) { break; } /* Run polling implementation only if pending timeouts or pending events */ - if (janet_vm.tq_count || janet_vm.extra_listeners) { + if (janet_vm.tq_count || janet_vm.listener_count) { janet_loop1_impl(has_timeout, to.when); } } diff --git a/src/core/state.h b/src/core/state.h index 892a25c8..97de0b46 100644 --- a/src/core/state.h +++ b/src/core/state.h @@ -156,7 +156,7 @@ struct JanetVM { JanetQueue spawn; JanetTimeout *tq; JanetRNG ev_rng; - volatile size_t extra_listeners; /* used in signal handler, must be volatile */ + volatile JanetAtomicInt64 listener_count; /* used in signal handler, must be volatile */ JanetTable threaded_abstracts; /* All abstract types that can be shared between threads (used in this thread) */ JanetTable active_tasks; /* All possibly live task fibers - used just for tracking */ JanetArray *listeners; /* For GC */ diff --git a/src/include/janet.h b/src/include/janet.h index 24e3b25d..4cc8eacc 100644 --- a/src/include/janet.h +++ b/src/include/janet.h @@ -633,6 +633,20 @@ struct JanetListenerState { }; #endif +/* Janet uses atomic integers in several places for synchronization between threads and + * signals. Define them here */ +#ifdef JANET_WINDOWS +typedef long JanetAtomicInt; +typedef long long JanetAtomicInt64; +#else +typedef int32_t JanetAtomicInt; +typedef int64_t JanetAtomicInt64; +#endif +JANET_API JanetAtomicInt janet_atomic_inc(JanetAtomicInt volatile *x); +JANET_API JanetAtomicInt janet_atomic_dec(JanetAtomicInt volatile *x); +JANET_API JanetAtomicInt64 janet_atomic64_inc(JanetAtomicInt64 volatile *x); +JANET_API JanetAtomicInt64 janet_atomic64_dec(JanetAtomicInt64 volatile *x); + /* We provide three possible implementations of Janets. The preferred * nanboxing approach, for 32 or 64 bits, and the standard C version. Code in the rest of the * application must interact through exposed interface. */ @@ -896,7 +910,7 @@ struct JanetGCObject { int32_t flags; union { JanetGCObject *next; - int32_t refcount; /* For threaded abstract types */ + volatile JanetAtomicInt refcount; /* For threaded abstract types */ } data; };