From dc3b313e91b573f393d8717c1c9e8c11a51f7ab1 Mon Sep 17 00:00:00 2001 From: Corey Farrell Date: Mon, 22 Jan 2018 14:50:37 -0500 Subject: Use thread-safe reference counting if supported by the compiler. This makes use of __atomic or __sync builtin compiler functions to make json_decref and json_incref thread-safe. Issue #387 --- CMakeLists.txt | 4 ++-- configure.ac | 12 ++++++++++-- src/jansson.h | 23 ++++++++++++++++++++--- src/jansson_config.h.in | 8 ++++++++ 4 files changed, 40 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 68cb35b..7c1082b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -303,8 +303,8 @@ else() set (JSON_INLINE) endif() -check_c_source_compiles ("int main() { unsigned long val; __sync_bool_compare_and_swap(&val, 0, 1); return 0; } " HAVE_SYNC_BUILTINS) -check_c_source_compiles ("int main() { char l; unsigned long v; __atomic_test_and_set(&l, __ATOMIC_RELAXED); __atomic_store_n(&v, 1, __ATOMIC_RELEASE); __atomic_load_n(&v, __ATOMIC_ACQUIRE); return 0; }" HAVE_ATOMIC_BUILTINS) +check_c_source_compiles ("int main() { unsigned long val; __sync_bool_compare_and_swap(&val, 0, 1); __sync_add_and_fetch(&val, 1); __sync_sub_and_fetch(&val, 1); return 0; } " HAVE_SYNC_BUILTINS) +check_c_source_compiles ("int main() { char l; unsigned long v; __atomic_test_and_set(&l, __ATOMIC_RELAXED); __atomic_store_n(&v, 1, __ATOMIC_RELEASE); __atomic_load_n(&v, __ATOMIC_ACQUIRE); __atomic_add_fetch(&v, 1, __ATOMIC_ACQUIRE); __atomic_sub_fetch(&v, 1, __ATOMIC_RELEASE); return 0; }" HAVE_ATOMIC_BUILTINS) set (JANSSON_INITIAL_HASHTABLE_ORDER 3 CACHE STRING "Number of buckets new object hashtables contain is 2 raised to this power. The default is 3, so empty hashtables contain 2^3 = 8 buckets.") diff --git a/configure.ac b/configure.ac index d1c4faf..fa0f005 100644 --- a/configure.ac +++ b/configure.ac @@ -38,25 +38,33 @@ AC_CHECK_FUNCS([close getpid gettimeofday localeconv open read sched_yield strto AC_MSG_CHECKING([for gcc __sync builtins]) have_sync_builtins=no AC_TRY_LINK( - [], [unsigned long val; __sync_bool_compare_and_swap(&val, 0, 1);], + [], [unsigned long val; __sync_bool_compare_and_swap(&val, 0, 1); __sync_add_and_fetch(&val, 1); __sync_sub_and_fetch(&val, 1);], [have_sync_builtins=yes], ) if test "x$have_sync_builtins" = "xyes"; then AC_DEFINE([HAVE_SYNC_BUILTINS], [1], [Define to 1 if gcc's __sync builtins are available]) + json_have_sync_builtins=1 +else + json_have_sync_builtins=0 fi +AC_SUBST([json_have_sync_builtins]) AC_MSG_RESULT([$have_sync_builtins]) AC_MSG_CHECKING([for gcc __atomic builtins]) have_atomic_builtins=no AC_TRY_LINK( - [], [char l; unsigned long v; __atomic_test_and_set(&l, __ATOMIC_RELAXED); __atomic_store_n(&v, 1, __ATOMIC_RELEASE); __atomic_load_n(&v, __ATOMIC_ACQUIRE);], + [], [char l; unsigned long v; __atomic_test_and_set(&l, __ATOMIC_RELAXED); __atomic_store_n(&v, 1, __ATOMIC_RELEASE); __atomic_load_n(&v, __ATOMIC_ACQUIRE); __atomic_add_fetch(&v, 1, __ATOMIC_ACQUIRE); __atomic_sub_fetch(&v, 1, __ATOMIC_RELEASE);], [have_atomic_builtins=yes], ) if test "x$have_atomic_builtins" = "xyes"; then AC_DEFINE([HAVE_ATOMIC_BUILTINS], [1], [Define to 1 if gcc's __atomic builtins are available]) + json_have_atomic_builtins=1 +else + json_have_atomic_builtins=0 fi +AC_SUBST([json_have_atomic_builtins]) AC_MSG_RESULT([$have_atomic_builtins]) case "$ac_cv_type_long_long_int$ac_cv_func_strtoll" in diff --git a/src/jansson.h b/src/jansson.h index ecf0e09..042390c 100644 --- a/src/jansson.h +++ b/src/jansson.h @@ -33,6 +33,11 @@ extern "C" { (JANSSON_MINOR_VERSION << 8) | \ (JANSSON_MICRO_VERSION << 0)) +/* If __atomic or __sync builtins are available the library is thread + * safe for all read-only functions plus reference counting. */ +#if JSON_HAVE_ATOMIC_BUILTINS || JSON_HAVE_SYNC_BUILTINS +#define JANSSON_THREAD_SAFE +#endif /* types */ @@ -49,7 +54,7 @@ typedef enum { typedef struct json_t { json_type type; - size_t refcount; + volatile size_t refcount; } json_t; #ifndef JANSSON_USING_CMAKE /* disabled if using cmake */ @@ -94,11 +99,23 @@ json_t *json_false(void); #define json_boolean(val) ((val) ? json_true() : json_false()) json_t *json_null(void); +/* do not call JSON_INTERNAL_INCREF or JSON_INTERNAL_DECREF directly */ +#if JSON_HAVE_ATOMIC_BUILTINS +#define JSON_INTERNAL_INCREF(json) __atomic_add_fetch(&json->refcount, 1, __ATOMIC_ACQUIRE) +#define JSON_INTERNAL_DECREF(json) __atomic_sub_fetch(&json->refcount, 1, __ATOMIC_RELEASE) +#elif JSON_HAVE_SYNC_BUILTINS +#define JSON_INTERNAL_INCREF(json) __sync_add_and_fetch(&json->refcount, 1) +#define JSON_INTERNAL_DECREF(json) __sync_sub_and_fetch(&json->refcount, 1) +#else +#define JSON_INTERNAL_INCREF(json) (++json->refcount) +#define JSON_INTERNAL_DECREF(json) (--json->refcount) +#endif + static JSON_INLINE json_t *json_incref(json_t *json) { if(json && json->refcount != (size_t)-1) - ++json->refcount; + JSON_INTERNAL_INCREF(json); return json; } @@ -108,7 +125,7 @@ void json_delete(json_t *json); static JSON_INLINE void json_decref(json_t *json) { - if(json && json->refcount != (size_t)-1 && --json->refcount == 0) + if(json && json->refcount != (size_t)-1 && JSON_INTERNAL_DECREF(json) == 0) json_delete(json); } diff --git a/src/jansson_config.h.in b/src/jansson_config.h.in index 5e94762..fe692ab 100644 --- a/src/jansson_config.h.in +++ b/src/jansson_config.h.in @@ -36,6 +36,14 @@ otherwise to 0. */ #define JSON_HAVE_LOCALECONV @json_have_localeconv@ +/* If __atomic builtins are available they will be used to manage + reference counts of json_t. */ +#define JSON_HAVE_ATOMIC_BUILTINS @json_have_atomic_builtins@ + +/* If __atomic builtins are not available we try using __sync builtins + to manage reference counts of json_t. */ +#define JSON_HAVE_SYNC_BUILTINS @json_have_sync_builtins@ + /* Maximum recursion depth for parsing JSON input. This limits the depth of e.g. array-within-array constructions. */ #define JSON_PARSER_MAX_DEPTH 2048 -- cgit v1.1