From 011617ee1aeca976c8bd614ac0e60b5c4a40a13d Mon Sep 17 00:00:00 2001 From: Vicente Eduardo Ferrer Garcia Date: Fri, 27 Jan 2023 17:18:24 +0100 Subject: [PATCH] Remove atomics for thread sanitizedr, add mutex alternate option for reference counting. --- source/reflect/source/reflect_class.c | 4 +- source/reflect/source/reflect_exception.c | 6 +- source/reflect/source/reflect_function.c | 4 +- source/reflect/source/reflect_object.c | 4 +- source/tests/CMakeLists.txt | 2 +- source/threading/CMakeLists.txt | 18 ++++ .../threading/threading_atomic_ref_count.h | 59 ++++++++++++ .../include/threading/threading_mutex.h | 94 +++++++++++++++++++ .../threading/source/threading_mutex_macos.c | 60 ++++++++++++ .../source/threading_mutex_pthread.c | 48 ++++++++++ .../threading/source/threading_mutex_win32.c | 63 +++++++++++++ 11 files changed, 356 insertions(+), 6 deletions(-) create mode 100644 source/threading/include/threading/threading_mutex.h create mode 100644 source/threading/source/threading_mutex_macos.c create mode 100644 source/threading/source/threading_mutex_pthread.c create mode 100644 source/threading/source/threading_mutex_win32.c diff --git a/source/reflect/source/reflect_class.c b/source/reflect/source/reflect_class.c index a12fbec40..a4bfb6545 100644 --- a/source/reflect/source/reflect_class.c +++ b/source/reflect/source/reflect_class.c @@ -108,7 +108,7 @@ klass class_create(const char *name, enum accessor_type_id accessor, class_impl cls->impl = impl; cls->accessor = accessor; - threading_atomic_ref_count_store(&cls->ref, 0); + threading_atomic_ref_count_initialize(&cls->ref); cls->interface = singleton ? singleton() : NULL; cls->constructors = vector_create_type(constructor); cls->methods = map_create(&hash_callback_str, &comparable_callback_str); @@ -901,6 +901,8 @@ void class_destroy(klass cls) set_destroy(cls->static_attributes); } + threading_atomic_ref_count_destroy(&cls->ref); + free(cls); reflect_memory_tracker_deallocation(class_stats); diff --git a/source/reflect/source/reflect_exception.c b/source/reflect/source/reflect_exception.c index e20e66b82..8913e341c 100644 --- a/source/reflect/source/reflect_exception.c +++ b/source/reflect/source/reflect_exception.c @@ -58,7 +58,7 @@ exception exception_create(char *message, char *label, int64_t code, char *stack ex->stacktrace = stacktrace; ex->id = thread_id_get_current(); - threading_atomic_ref_count_store(&ex->ref, 0); + threading_atomic_ref_count_initialize(&ex->ref); reflect_memory_tracker_allocation(exception_stats); @@ -131,7 +131,7 @@ exception exception_create_const(const char *message, const char *label, int64_t ex->code = code; ex->id = thread_id_get_current(); - threading_atomic_ref_count_store(&ex->ref, 0); + threading_atomic_ref_count_initialize(&ex->ref); reflect_memory_tracker_allocation(exception_stats); @@ -252,6 +252,8 @@ void exception_destroy(exception ex) free(ex->stacktrace); } + threading_atomic_ref_count_destroy(&ex->ref); + free(ex); reflect_memory_tracker_deallocation(exception_stats); diff --git a/source/reflect/source/reflect_function.c b/source/reflect/source/reflect_function.c index 2229511cb..cd047381a 100644 --- a/source/reflect/source/reflect_function.c +++ b/source/reflect/source/reflect_function.c @@ -91,7 +91,7 @@ function function_create(const char *name, size_t args_count, function_impl impl goto function_create_error; } - threading_atomic_ref_count_store(&func->ref, 0); + threading_atomic_ref_count_initialize(&func->ref); func->interface = singleton ? singleton() : NULL; @@ -731,6 +731,8 @@ void function_destroy(function func) free(func->name); } + threading_atomic_ref_count_destroy(&func->ref); + free(func); reflect_memory_tracker_deallocation(function_stats); diff --git a/source/reflect/source/reflect_object.c b/source/reflect/source/reflect_object.c index 6fd6bbbda..6e0aa35b6 100644 --- a/source/reflect/source/reflect_object.c +++ b/source/reflect/source/reflect_object.c @@ -81,7 +81,7 @@ object object_create(const char *name, enum accessor_type_id accessor, object_im obj->impl = impl; obj->accessor = accessor; - threading_atomic_ref_count_store(&obj->ref, 0); + threading_atomic_ref_count_initialize(&obj->ref); obj->interface = singleton ? singleton() : NULL; @@ -419,6 +419,8 @@ void object_destroy(object obj) free(obj->name); } + threading_atomic_ref_count_destroy(&obj->ref); + free(obj); reflect_memory_tracker_deallocation(object_stats); diff --git a/source/tests/CMakeLists.txt b/source/tests/CMakeLists.txt index fd216cf2c..7cf0841ba 100644 --- a/source/tests/CMakeLists.txt +++ b/source/tests/CMakeLists.txt @@ -146,7 +146,7 @@ add_subdirectory(metacall_node_async_resources_test) add_subdirectory(metacall_node_await_chain_test) add_subdirectory(metacall_node_exception_test) add_subdirectory(metacall_node_python_deadlock_test) -add_subdirectory(metacall_node_signal_handler_test) +# add_subdirectory(metacall_node_signal_handler_test) # Note: Not used anymore but leaving it here for reference to solve this: https://github.com/metacall/core/issues/121 add_subdirectory(metacall_node_native_code_test) add_subdirectory(metacall_node_extension_test) add_subdirectory(metacall_distributable_test) diff --git a/source/threading/CMakeLists.txt b/source/threading/CMakeLists.txt index e3c9a9fb0..e8ec03da7 100644 --- a/source/threading/CMakeLists.txt +++ b/source/threading/CMakeLists.txt @@ -38,6 +38,7 @@ set(headers ${include_path}/threading_atomic.h ${include_path}/threading_thread_id.h ${include_path}/threading_atomic_ref_count.h + ${include_path}/threading_mutex.h ) set(sources @@ -56,6 +57,23 @@ if( ) endif() +if(WIN32) + set(sources + ${sources} + ${source_path}/threading_mutex_win32.c + ) +elseif(APPLE) + set(sources + ${sources} + ${source_path}/threading_mutex_macos.c + ) +else() + set(sources + ${sources} + ${source_path}/threading_mutex_pthread.c + ) +endif() + # Group source files set(header_group "Header Files (API)") set(source_group "Source Files") diff --git a/source/threading/include/threading/threading_atomic_ref_count.h b/source/threading/include/threading/threading_atomic_ref_count.h index 04b12b247..96b0b4dfa 100644 --- a/source/threading/include/threading/threading_atomic_ref_count.h +++ b/source/threading/include/threading/threading_atomic_ref_count.h @@ -27,6 +27,10 @@ #include +#if defined(__THREAD_SANITIZER__) + #include +#endif + #ifdef __cplusplus extern "C" { #endif @@ -44,7 +48,12 @@ extern "C" { struct threading_atomic_ref_count_type { +#if defined(__THREAD_SANITIZER__) + uintmax_t count; + struct threading_mutex_type m; +#else atomic_uintmax_t count; +#endif }; /* -- Type Definitions -- */ @@ -55,28 +64,68 @@ typedef struct threading_atomic_ref_count_type *threading_atomic_ref_count; inline void threading_atomic_ref_count_store(threading_atomic_ref_count ref, uintmax_t v) { +#if defined(__THREAD_SANITIZER__) + threading_mutex_store(&ref->m, &ref->count, &v, sizeof(uintmax_t)); +#else atomic_store(&ref->count, v); +#endif +} + +inline void threading_atomic_ref_count_initialize(threading_atomic_ref_count ref) +{ +#if defined(__THREAD_SANITIZER__) + uintmax_t init = THREADING_ATOMIC_REF_COUNT_MIN; + + threading_mutex_initialize(&ref->m); + + threading_mutex_store(&ref->m, &ref->count, &init, sizeof(uintmax_t)); +#else + threading_atomic_ref_count_store(ref, THREADING_ATOMIC_REF_COUNT_MIN); +#endif } inline uintmax_t threading_atomic_ref_count_load(threading_atomic_ref_count ref) { +#if defined(__THREAD_SANITIZER__) + uintmax_t result = 0; + + threading_mutex_store(&ref->m, &result, &ref->count, sizeof(uintmax_t)); + + return result; +#else return atomic_load_explicit(&ref->count, memory_order_relaxed); +#endif } inline int threading_atomic_ref_count_increment(threading_atomic_ref_count ref) { +#if defined(__THREAD_SANITIZER__) + threading_mutex_lock(&ref->m); + { + ++ref->count; + } + threading_mutex_unlock(&ref->m); +#else if (atomic_load_explicit(&ref->count, memory_order_relaxed) == THREADING_ATOMIC_REF_COUNT_MAX) { return 1; } atomic_fetch_add_explicit(&ref->count, 1, memory_order_relaxed); +#endif return 0; } inline int threading_atomic_ref_count_decrement(threading_atomic_ref_count ref) { +#if defined(__THREAD_SANITIZER__) + threading_mutex_lock(&ref->m); + { + --ref->count; + } + threading_mutex_unlock(&ref->m); +#else if (atomic_load_explicit(&ref->count, memory_order_relaxed) == THREADING_ATOMIC_REF_COUNT_MIN) { return 1; @@ -88,10 +137,20 @@ inline int threading_atomic_ref_count_decrement(threading_atomic_ref_count ref) { atomic_thread_fence(memory_order_acquire); } +#endif return 0; } +inline void threading_atomic_ref_count_destroy(threading_atomic_ref_count ref) +{ +#if defined(__THREAD_SANITIZER__) + threading_mutex_destroy(&ref->m); +#else + (void)ref; +#endif +} + #ifdef __cplusplus } #endif diff --git a/source/threading/include/threading/threading_mutex.h b/source/threading/include/threading/threading_mutex.h new file mode 100644 index 000000000..134debfc5 --- /dev/null +++ b/source/threading/include/threading/threading_mutex.h @@ -0,0 +1,94 @@ +/* + * Thrading Library by Parra Studios + * A threading library providing utilities for lock-free data structures and more. + * + * Copyright (C) 2016 - 2022 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef THREADING_MUTEX_H +#define THREADING_MUTEX_H 1 + +/* -- Headers -- */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* -- Type Definitions -- */ + +#if defined(_WIN32) || defined(__WIN32__) || defined(_WIN64) + #include +typedef CRITICAL_SECTION threading_mutex_impl_type; +#elif (defined(linux) || defined(__linux) || defined(__linux__) || defined(__gnu_linux) || defined(__gnu_linux__) || defined(__TOS_LINUX__)) || \ + defined(__FreeBSD__) || \ + defined(__NetBSD__) || \ + defined(__OpenBSD__) || \ + (defined(bsdi) || defined(__bsdi__)) || \ + defined(__DragonFly__) + #include +typedef pthread_mutex_t threading_mutex_impl_type; +#elif (defined(__MACOS__) || defined(macintosh) || defined(Macintosh) || defined(__TOS_MACOS__)) || \ + (defined(__APPLE__) && defined(__MACH__)) || defined(__MACOSX__) + #include +typedef os_unfair_lock threading_mutex_impl_type; +#else + #error "Platform not supported for mutex implementation" +#endif + +#include + +/* -- Member Data -- */ + +struct threading_mutex_type +{ + threading_mutex_impl_type impl; +}; + +/* -- Type Definitions -- */ + +typedef struct threading_mutex_type *threading_mutex; + +/* -- Methods -- */ + +int threading_mutex_initialize(threading_mutex m); + +int threading_mutex_lock(threading_mutex m); + +int threading_mutex_try_lock(threading_mutex m); + +int threading_mutex_unlock(threading_mutex m); + +int threading_mutex_destroy(threading_mutex m); + +inline int threading_mutex_store(threading_mutex m, void *dest, void *src, size_t size) +{ + if (threading_mutex_lock(m) != 0) + { + return 1; + } + + memcpy(dest, src, size); + + return threading_mutex_unlock(m); +} + +#ifdef __cplusplus +} +#endif + +#endif /* THREADING_MUTEX_H */ diff --git a/source/threading/source/threading_mutex_macos.c b/source/threading/source/threading_mutex_macos.c new file mode 100644 index 000000000..66f060f3b --- /dev/null +++ b/source/threading/source/threading_mutex_macos.c @@ -0,0 +1,60 @@ +/* + * Abstract Data Type Library by Parra Studios + * A abstract data type library providing generic containers. + * + * Copyright (C) 2016 - 2022 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* -- Headers -- */ + +#include + +int threading_mutex_initialize(threading_mutex m) +{ + m->impl = 0; + + return 0; +} + +int threading_mutex_lock(threading_mutex m) +{ + os_unfair_lock_lock(&m->impl); + + return 0; +} + +int threading_mutex_try_lock(threading_mutex m) +{ + if (os_unfair_lock_trylock(&m->impl) == false) + { + return 1; + } + + return 0; +} + +int threading_mutex_unlock(threading_mutex m) +{ + os_unfair_lock_unlock(&m->impl); + + return 0; +} + +int threading_mutex_destroy(threading_mutex m) +{ + (void)m; + return 0; +} diff --git a/source/threading/source/threading_mutex_pthread.c b/source/threading/source/threading_mutex_pthread.c new file mode 100644 index 000000000..b089c81f2 --- /dev/null +++ b/source/threading/source/threading_mutex_pthread.c @@ -0,0 +1,48 @@ +/* + * Abstract Data Type Library by Parra Studios + * A abstract data type library providing generic containers. + * + * Copyright (C) 2016 - 2022 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* -- Headers -- */ + +#include + +int threading_mutex_initialize(threading_mutex m) +{ + return pthread_mutex_init(&m->impl, NULL); +} + +int threading_mutex_lock(threading_mutex m) +{ + return pthread_mutex_lock(&m->impl); +} + +int threading_mutex_try_lock(threading_mutex m) +{ + return pthread_mutex_trylock(&m->impl); +} + +int threading_mutex_unlock(threading_mutex m) +{ + return pthread_mutex_unlock(&m->impl); +} + +int threading_mutex_destroy(threading_mutex m) +{ + return pthread_mutex_destroy(&m->impl); +} diff --git a/source/threading/source/threading_mutex_win32.c b/source/threading/source/threading_mutex_win32.c new file mode 100644 index 000000000..9c0338538 --- /dev/null +++ b/source/threading/source/threading_mutex_win32.c @@ -0,0 +1,63 @@ +/* + * Abstract Data Type Library by Parra Studios + * A abstract data type library providing generic containers. + * + * Copyright (C) 2016 - 2022 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* -- Headers -- */ + +#include + +#include + +int threading_mutex_initialize(threading_mutex m) +{ + InitializeCriticalSection(&m->impl); + + return 0; +} + +int threading_mutex_lock(threading_mutex m) +{ + EnterCriticalSection(&m->impl); + + return 0; +} + +int threading_mutex_try_lock(threading_mutex m) +{ + if (TryEnterCriticalSection(&m->impl) == 0) + { + return 1; + } + + return 0; +} + +int threading_mutex_unlock(threading_mutex m) +{ + LeaveCriticalSection(&m->impl); + + return 0; +} + +int threading_mutex_destroy(threading_mutex m) +{ + DeleteCriticalSection(&m->impl); + + return 0; +}