From 2d1da5ab13463dc52c9e8d262cb51b80c7376ebb Mon Sep 17 00:00:00 2001
From: Eric Haszlakiewicz <erh+git@nimenees.com>
Date: Sun, 3 Sep 2017 23:37:12 -0400
Subject: [PATCH] Add a --enable-threading configure option, and only use the
 (slower) __sync_add_and_fetch()/__sync_sub_and_fetch() function when it is
 specified.

---
 README.md     | 21 ++++++++++++++++++++-
 configure.ac  | 16 +++++++++++++++-
 json_object.c | 12 ++++++------
 3 files changed, 41 insertions(+), 8 deletions(-)

diff --git a/README.md b/README.md
index 0e87cd5..bddcff7 100644
--- a/README.md
+++ b/README.md
@@ -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`
 ----------------------
 
diff --git a/configure.ac b/configure.ac
index e909cf2..0ebf823 100644
--- a/configure.ac
+++ b/configure.ac
@@ -11,12 +11,26 @@ 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
diff --git a/json_object.c b/json_object.c
index c09d61d..fcda69f 100644
--- a/json_object.c
+++ b/json_object.c
@@ -165,7 +165,7 @@ extern struct json_object* json_object_get(struct json_object *jso)
 {
 	if (!jso) return jso;
 
-#ifdef HAVE_ATOMIC_BUILTINS
+#if defined(HAVE_ATOMIC_BUILTINS) && defined(ENABLE_THREADING)
 	__sync_add_and_fetch(&jso->_ref_count, 1);
 #else
 	++jso->_ref_count;
@@ -178,7 +178,7 @@ int json_object_put(struct json_object *jso)
 {
 	if(!jso) return 0;
 
-#ifdef HAVE_ATOMIC_BUILTINS
+#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,
@@ -703,7 +703,7 @@ int json_object_set_int64(struct json_object *jso,int64_t new_value){
 
 /* json_object_double */
 
-#ifdef HAVE___THREAD
+#if defined(HAVE___THREAD) && defined(ENABLE_THREADING)
 // i.e. __thread or __declspec(thread)
 static SPEC___THREAD char *tls_serialization_float_format = NULL;
 #endif
@@ -713,7 +713,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) && defined(ENABLE_THREADING)
 		if (tls_serialization_float_format)
 		{
 			free(tls_serialization_float_format);
@@ -726,7 +726,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) && defined(ENABLE_THREADING)
 		if (tls_serialization_float_format)
 		{
 			free(tls_serialization_float_format);
@@ -775,7 +775,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) && defined(ENABLE_THREADING)
 		if (tls_serialization_float_format)
 			std_format = tls_serialization_float_format;
 		else
-- 
2.40.0