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
This commit is contained in:
Corey Farrell 2018-01-22 14:50:37 -05:00
parent 9e5af7c3b7
commit dc3b313e91
4 changed files with 40 additions and 7 deletions

View File

@ -303,8 +303,8 @@ else()
set (JSON_INLINE) set (JSON_INLINE)
endif() 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() { 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); return 0; }" HAVE_ATOMIC_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.") 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.")

View File

@ -38,25 +38,33 @@ AC_CHECK_FUNCS([close getpid gettimeofday localeconv open read sched_yield strto
AC_MSG_CHECKING([for gcc __sync builtins]) AC_MSG_CHECKING([for gcc __sync builtins])
have_sync_builtins=no have_sync_builtins=no
AC_TRY_LINK( 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], [have_sync_builtins=yes],
) )
if test "x$have_sync_builtins" = "xyes"; then if test "x$have_sync_builtins" = "xyes"; then
AC_DEFINE([HAVE_SYNC_BUILTINS], [1], AC_DEFINE([HAVE_SYNC_BUILTINS], [1],
[Define to 1 if gcc's __sync builtins are available]) [Define to 1 if gcc's __sync builtins are available])
json_have_sync_builtins=1
else
json_have_sync_builtins=0
fi fi
AC_SUBST([json_have_sync_builtins])
AC_MSG_RESULT([$have_sync_builtins]) AC_MSG_RESULT([$have_sync_builtins])
AC_MSG_CHECKING([for gcc __atomic builtins]) AC_MSG_CHECKING([for gcc __atomic builtins])
have_atomic_builtins=no have_atomic_builtins=no
AC_TRY_LINK( 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], [have_atomic_builtins=yes],
) )
if test "x$have_atomic_builtins" = "xyes"; then if test "x$have_atomic_builtins" = "xyes"; then
AC_DEFINE([HAVE_ATOMIC_BUILTINS], [1], AC_DEFINE([HAVE_ATOMIC_BUILTINS], [1],
[Define to 1 if gcc's __atomic builtins are available]) [Define to 1 if gcc's __atomic builtins are available])
json_have_atomic_builtins=1
else
json_have_atomic_builtins=0
fi fi
AC_SUBST([json_have_atomic_builtins])
AC_MSG_RESULT([$have_atomic_builtins]) AC_MSG_RESULT([$have_atomic_builtins])
case "$ac_cv_type_long_long_int$ac_cv_func_strtoll" in case "$ac_cv_type_long_long_int$ac_cv_func_strtoll" in

View File

@ -33,6 +33,11 @@ extern "C" {
(JANSSON_MINOR_VERSION << 8) | \ (JANSSON_MINOR_VERSION << 8) | \
(JANSSON_MICRO_VERSION << 0)) (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 */ /* types */
@ -49,7 +54,7 @@ typedef enum {
typedef struct json_t { typedef struct json_t {
json_type type; json_type type;
size_t refcount; volatile size_t refcount;
} json_t; } json_t;
#ifndef JANSSON_USING_CMAKE /* disabled if using cmake */ #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()) #define json_boolean(val) ((val) ? json_true() : json_false())
json_t *json_null(void); 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 static JSON_INLINE
json_t *json_incref(json_t *json) json_t *json_incref(json_t *json)
{ {
if(json && json->refcount != (size_t)-1) if(json && json->refcount != (size_t)-1)
++json->refcount; JSON_INTERNAL_INCREF(json);
return json; return json;
} }
@ -108,7 +125,7 @@ void json_delete(json_t *json);
static JSON_INLINE static JSON_INLINE
void json_decref(json_t *json) 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); json_delete(json);
} }

View File

@ -36,6 +36,14 @@
otherwise to 0. */ otherwise to 0. */
#define JSON_HAVE_LOCALECONV @json_have_localeconv@ #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. /* Maximum recursion depth for parsing JSON input.
This limits the depth of e.g. array-within-array constructions. */ This limits the depth of e.g. array-within-array constructions. */
#define JSON_PARSER_MAX_DEPTH 2048 #define JSON_PARSER_MAX_DEPTH 2048