@@ -56,13 +56,43 @@ libjson_c_la_SOURCES = \ | |||||
strerror_override.c \ | strerror_override.c \ | ||||
strerror_override_private.h | strerror_override_private.h | ||||
DISTCLEANFILES= | |||||
DISTCLEANFILES+= \ | |||||
config.h \ | |||||
json-c-uninstalled.pc \ | |||||
json-c.pc \ | |||||
json_config.h | |||||
distclean-local: | distclean-local: | ||||
-rm -rf $(testsubdir) | -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: | uninstall-local: | ||||
rm -rf "$(DESTDIR)@includedir@/json-c" | rm -rf "$(DESTDIR)@includedir@/json-c" | ||||
@@ -45,7 +45,7 @@ $ sh autogen.sh | |||||
followed by | followed by | ||||
```sh | ```sh | ||||
$ ./configure | |||||
$ ./configure # --enable-threading | |||||
$ make | $ make | ||||
$ make install | $ make install | ||||
``` | ``` | ||||
@@ -56,6 +56,25 @@ To build and run the test programs: | |||||
$ make check | $ 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` | Linking to `libjson-c` | ||||
---------------------- | ---------------------- | ||||
@@ -22,6 +22,7 @@ | |||||
# include <strings.h> | # include <strings.h> | ||||
#endif /* HAVE_STRINGS_H */ | #endif /* HAVE_STRINGS_H */ | ||||
#ifndef SIZE_T_MAX | |||||
#if SIZEOF_SIZE_T == SIZEOF_INT | #if SIZEOF_SIZE_T == SIZEOF_INT | ||||
#define SIZE_T_MAX UINT_MAX | #define SIZE_T_MAX UINT_MAX | ||||
#elif SIZEOF_SIZE_T == SIZEOF_LONG | #elif SIZEOF_SIZE_T == SIZEOF_LONG | ||||
@@ -31,6 +32,7 @@ | |||||
#else | #else | ||||
#error Unable to determine size of size_t | #error Unable to determine size of size_t | ||||
#endif | #endif | ||||
#endif | |||||
#include "arraylist.h" | #include "arraylist.h" | ||||
@@ -1,5 +1,5 @@ | |||||
#!/bin/sh | #!/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. | # If there are any options, assume the user wants to run configure. | ||||
# To run configure w/o any options, use ./autogen.sh --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 | AM_INIT_AUTOMAKE | ||||
AC_CONFIG_MACRO_DIRS([autoconf-archive/m4]) | |||||
AC_PROG_MAKE_SET | AC_PROG_MAKE_SET | ||||
AC_CANONICAL_HOST | 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, | AC_ARG_ENABLE(rdrand, | ||||
AS_HELP_STRING([--enable-rdrand], | AS_HELP_STRING([--enable-rdrand], | ||||
[Enable RDRAND Hardware RNG Hash Seed generation on supported x86/x64 platforms.]), | [Enable RDRAND Hardware RNG Hash Seed generation on supported x86/x64 platforms.]), | ||||
[if test x$enableval = xyes; then | [if test x$enableval = xyes; then | ||||
enable_rdrand=yes | 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]) | fi]) | ||||
if test "x$enable_rdrand" = "xyes"; then | 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([isinf], [], [], [[#include <math.h>]]) | ||||
AC_CHECK_DECLS([_isnan], [], [], [[#include <float.h>]]) | AC_CHECK_DECLS([_isnan], [], [], [[#include <float.h>]]) | ||||
AC_CHECK_DECLS([_finite], [], [], [[#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 | case "${host_os}" in | ||||
linux*) | 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) | 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; | return jso; | ||||
} | } | ||||
int json_object_release(struct json_object *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 */ | /* json_object_double */ | ||||
#ifdef HAVE___THREAD | |||||
#if defined(HAVE___THREAD) | |||||
// i.e. __thread or __declspec(thread) | // i.e. __thread or __declspec(thread) | ||||
static SPEC___THREAD char *tls_serialization_float_format = NULL; | static SPEC___THREAD char *tls_serialization_float_format = NULL; | ||||
#endif | #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) | if (global_or_thread == JSON_C_OPTION_GLOBAL) | ||||
{ | { | ||||
#ifdef HAVE___THREAD | |||||
#if defined(HAVE___THREAD) | |||||
if (tls_serialization_float_format) | if (tls_serialization_float_format) | ||||
{ | { | ||||
free(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) | else if (global_or_thread == JSON_C_OPTION_THREAD) | ||||
{ | { | ||||
#ifdef HAVE___THREAD | |||||
#if defined(HAVE___THREAD) | |||||
if (tls_serialization_float_format) | if (tls_serialization_float_format) | ||||
{ | { | ||||
free(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"; | const char *std_format = "%.17g"; | ||||
#ifdef HAVE___THREAD | |||||
#if defined(HAVE___THREAD) | |||||
if (tls_serialization_float_format) | if (tls_serialization_float_format) | ||||
std_format = tls_serialization_float_format; | std_format = tls_serialization_float_format; | ||||
else | else | ||||
@@ -265,7 +265,9 @@ int json_parse_int64(const char *buf, int64_t *retval) | |||||
// Skip leading zeros, but keep at least one digit | // Skip leading zeros, but keep at least one digit | ||||
while (buf_sig_digits[0] == '0' && buf_sig_digits[1] != '\0') | while (buf_sig_digits[0] == '0' && buf_sig_digits[1] != '\0') | ||||
buf_sig_digits++; | 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" | orig_has_neg = 0; // "-0" is the same as just plain "0" | ||||
snprintf(buf_cmp_start, sizeof(buf_cmp), "%" PRId64, num64); | snprintf(buf_cmp_start, sizeof(buf_cmp), "%" PRId64, num64); | ||||