@@ -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); | |||