| @@ -56,13 +56,43 @@ libjson_c_la_SOURCES = \ | |||
| strerror_override.c \ | |||
| strerror_override_private.h | |||
| DISTCLEANFILES= | |||
| DISTCLEANFILES+= \ | |||
| config.h \ | |||
| json-c-uninstalled.pc \ | |||
| json-c.pc \ | |||
| json_config.h | |||
| distclean-local: | |||
| -rm -rf $(testsubdir) | |||
| -rm -rf config.h.in~ Makefile.in aclocal.m4 autom4te.cache/ config.guess config.sub depcomp install-sh ltmain.sh missing | |||
| -rm -f INSTALL test-driver tests/Makefile.in compile | |||
| maintainer-clean-local: | |||
| -rm -rf configure | |||
| JSON_CLEANFILES= | |||
| JSON_CLEANFILES+= \ | |||
| Makefile.in \ | |||
| aclocal.m4 \ | |||
| autom4te.cache/ \ | |||
| compile \ | |||
| config.guess \ | |||
| config.h.in \ | |||
| config.sub \ | |||
| configure \ | |||
| depcomp \ | |||
| install-sh \ | |||
| ltmain.sh \ | |||
| missing \ | |||
| test-driver \ | |||
| tests/Makefile.in | |||
| JSON_CLEANFILES+= \ | |||
| libtool \ | |||
| stamp-h1 \ | |||
| stamp-h2 | |||
| # There's no built-in way to remove these after all the other | |||
| # maintainer-clean steps happen, so do it explicitly here. | |||
| really-clean: | |||
| $(MAKE) maintainer-clean | |||
| rm -rf ${JSON_CLEANFILES} | |||
| uninstall-local: | |||
| rm -rf "$(DESTDIR)@includedir@/json-c" | |||
| @@ -45,7 +45,7 @@ $ sh autogen.sh | |||
| followed by | |||
| ```sh | |||
| $ ./configure | |||
| $ ./configure # --enable-threading | |||
| $ make | |||
| $ make install | |||
| ``` | |||
| @@ -56,6 +56,25 @@ To build and run the test programs: | |||
| $ make check | |||
| ``` | |||
| Building with partial threading support | |||
| ---------------------------------------- | |||
| Although json-c does not support fully multi-threaded access to | |||
| object trees, it has some code to help make use in threaded programs | |||
| a bit safer. Currently, this is limited to using atomic operations for | |||
| json_object_get() and json_object_put(). | |||
| Since this may have a performance impact, of at least 3x slower | |||
| according to https://stackoverflow.com/a/11609063, it is disabled by | |||
| default. You may turn it on by adjusting your configure command with: | |||
| --enable-threading | |||
| Separately, the default hash function used for object field keys, | |||
| lh_char_hash, uses a compare-and-swap operation to ensure the randomly | |||
| seed is only generated once. Because this is a one-time operation, it | |||
| is always compiled in when the compare-and-swap operation is available. | |||
| Linking to `libjson-c` | |||
| ---------------------- | |||
| @@ -22,6 +22,7 @@ | |||
| # include <strings.h> | |||
| #endif /* HAVE_STRINGS_H */ | |||
| #ifndef SIZE_T_MAX | |||
| #if SIZEOF_SIZE_T == SIZEOF_INT | |||
| #define SIZE_T_MAX UINT_MAX | |||
| #elif SIZEOF_SIZE_T == SIZEOF_LONG | |||
| @@ -31,6 +32,7 @@ | |||
| #else | |||
| #error Unable to determine size of size_t | |||
| #endif | |||
| #endif | |||
| #include "arraylist.h" | |||
| @@ -1,5 +1,5 @@ | |||
| #!/bin/sh | |||
| autoreconf -Iautoconf-archive/m4 -v --install || exit 1 | |||
| autoreconf -v --install || exit 1 | |||
| # If there are any options, assume the user wants to run configure. | |||
| # To run configure w/o any options, use ./autogen.sh --configure | |||
| @@ -5,16 +5,32 @@ AC_INIT([json-c], 0.12.99, [json-c@googlegroups.com]) | |||
| AM_INIT_AUTOMAKE | |||
| AC_CONFIG_MACRO_DIRS([autoconf-archive/m4]) | |||
| AC_PROG_MAKE_SET | |||
| AC_CANONICAL_HOST | |||
| AC_ARG_ENABLE(threading, | |||
| AS_HELP_STRING([--enable-threading], | |||
| [Enable code to support partly multi-threaded use]), | |||
| [if test x$enableval = xyes; then | |||
| enable_threading=yes | |||
| AC_DEFINE(ENABLE_THREADING, 1, [Enable partial threading support]) | |||
| fi]) | |||
| if test "x$enable_threading" = "xyes"; then | |||
| AC_MSG_RESULT([Partial multi-threaded support enabled.]) | |||
| else | |||
| AC_MSG_RESULT([Multi-threaded support disabled. Use --enable-threading to enable.]) | |||
| fi | |||
| AC_ARG_ENABLE(rdrand, | |||
| AS_HELP_STRING([--enable-rdrand], | |||
| [Enable RDRAND Hardware RNG Hash Seed generation on supported x86/x64 platforms.]), | |||
| [if test x$enableval = xyes; then | |||
| enable_rdrand=yes | |||
| AC_DEFINE(ENABLE_RDRAND, 1, [Enable RDRANR Hardware RNG Hash Seed]) | |||
| AC_DEFINE(ENABLE_RDRAND, 1, [Enable RDRAND Hardware RNG Hash Seed]) | |||
| fi]) | |||
| if test "x$enable_rdrand" = "xyes"; then | |||
| @@ -66,6 +82,30 @@ AC_CHECK_DECLS([isnan], [], [], [[#include <math.h>]]) | |||
| AC_CHECK_DECLS([isinf], [], [], [[#include <math.h>]]) | |||
| AC_CHECK_DECLS([_isnan], [], [], [[#include <float.h>]]) | |||
| AC_CHECK_DECLS([_finite], [], [], [[#include <float.h>]]) | |||
| AC_MSG_CHECKING(for GCC atomic builtins) | |||
| AC_LINK_IFELSE( | |||
| [ | |||
| AC_LANG_SOURCE([[ | |||
| int main() { | |||
| volatile unsigned int val = 1; | |||
| /* Note: __sync_val_compare_and_swap isn't checked here | |||
| * because it's protected by __GCC_HAVE_SYNC_COMPARE_AND_SWAP_<n>, | |||
| * which is automatically defined by gcc. | |||
| */ | |||
| __sync_add_and_fetch(&val, 1); | |||
| __sync_sub_and_fetch(&val, 1); | |||
| return 0; | |||
| } | |||
| ]]) | |||
| ], | |||
| [ | |||
| AC_MSG_RESULT([yes]) | |||
| AC_DEFINE([HAVE_ATOMIC_BUILTINS],[1],[Has atomic builtins]) | |||
| ], | |||
| [ | |||
| AC_MSG_RESULT([no]) | |||
| AC_MSG_WARN([json-c will be built without atomic refcounts because atomic builtins are missing]) | |||
| ]) | |||
| case "${host_os}" in | |||
| linux*) | |||
| @@ -163,25 +163,37 @@ static int json_escape_str(struct printbuf *pb, const char *str, int len, int fl | |||
| extern struct json_object* json_object_retain(struct json_object *jso) | |||
| { | |||
| if (jso) | |||
| jso->_ref_count++; | |||
| if (!jso) return jso; | |||
| #if defined(HAVE_ATOMIC_BUILTINS) && defined(ENABLE_THREADING) | |||
| __sync_add_and_fetch(&jso->_ref_count, 1); | |||
| #else | |||
| ++jso->_ref_count; | |||
| #endif | |||
| return jso; | |||
| } | |||
| int json_object_release(struct json_object *jso) | |||
| { | |||
| if(jso) | |||
| { | |||
| jso->_ref_count--; | |||
| if(!jso->_ref_count) | |||
| { | |||
| if (jso->_user_delete) | |||
| jso->_user_delete(jso, jso->_userdata); | |||
| jso->_delete(jso); | |||
| return 1; | |||
| } | |||
| } | |||
| return 0; | |||
| if(!jso) return 0; | |||
| #if defined(HAVE_ATOMIC_BUILTINS) && defined(ENABLE_THREADING) | |||
| /* Note: this only allow the refcount to remain correct | |||
| * when multiple threads are adjusting it. It is still an error | |||
| * for a thread to decrement the refcount if it doesn't "own" it, | |||
| * as that can result in the thread that loses the race to 0 | |||
| * operating on an already-freed object. | |||
| */ | |||
| if (__sync_sub_and_fetch(&jso->_ref_count, 1) > 0) return 0; | |||
| #else | |||
| if (--jso->_ref_count > 0) return 0; | |||
| #endif | |||
| if (jso->_user_delete) | |||
| jso->_user_delete(jso, jso->_userdata); | |||
| jso->_delete(jso); | |||
| return 1; | |||
| } | |||
| @@ -693,7 +705,7 @@ int json_object_set_int64(struct json_object *jso,int64_t new_value){ | |||
| /* json_object_double */ | |||
| #ifdef HAVE___THREAD | |||
| #if defined(HAVE___THREAD) | |||
| // i.e. __thread or __declspec(thread) | |||
| static SPEC___THREAD char *tls_serialization_float_format = NULL; | |||
| #endif | |||
| @@ -703,7 +715,7 @@ int json_c_set_serialization_double_format(const char *double_format, int global | |||
| { | |||
| if (global_or_thread == JSON_C_OPTION_GLOBAL) | |||
| { | |||
| #ifdef HAVE___THREAD | |||
| #if defined(HAVE___THREAD) | |||
| if (tls_serialization_float_format) | |||
| { | |||
| free(tls_serialization_float_format); | |||
| @@ -716,7 +728,7 @@ int json_c_set_serialization_double_format(const char *double_format, int global | |||
| } | |||
| else if (global_or_thread == JSON_C_OPTION_THREAD) | |||
| { | |||
| #ifdef HAVE___THREAD | |||
| #if defined(HAVE___THREAD) | |||
| if (tls_serialization_float_format) | |||
| { | |||
| free(tls_serialization_float_format); | |||
| @@ -765,7 +777,7 @@ static int json_object_double_to_json_string_format(struct json_object* jso, | |||
| { | |||
| const char *std_format = "%.17g"; | |||
| #ifdef HAVE___THREAD | |||
| #if defined(HAVE___THREAD) | |||
| if (tls_serialization_float_format) | |||
| std_format = tls_serialization_float_format; | |||
| else | |||
| @@ -265,7 +265,9 @@ int json_parse_int64(const char *buf, int64_t *retval) | |||
| // Skip leading zeros, but keep at least one digit | |||
| while (buf_sig_digits[0] == '0' && buf_sig_digits[1] != '\0') | |||
| buf_sig_digits++; | |||
| if (num64 == 0) // assume all sscanf impl's will parse -0 to 0 | |||
| // Can't check num64==0 because some sscanf impl's parse | |||
| // non-zero values to 0. (e.g. Illumos with UINT64_MAX) | |||
| if (buf_sig_digits[0] == '0' && buf_sig_digits[1] == '\0') | |||
| orig_has_neg = 0; // "-0" is the same as just plain "0" | |||
| snprintf(buf_cmp_start, sizeof(buf_cmp), "%" PRId64, num64); | |||