TurboJPEG: Make global error handling thread-safe
... on platforms that support thread-local storage. This currently
includes all supported platforms except 32-bit macOS.
Fixes #396
diff --git a/CMakeLists.txt b/CMakeLists.txt
index fe16d8e..8656d7a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -451,6 +451,21 @@
endif()
message(STATUS "INLINE = ${INLINE} (FORCE_INLINE = ${FORCE_INLINE})")
+if(WITH_TURBOJPEG)
+ if(MSVC)
+ set(THREAD_LOCAL "__declspec(thread)")
+ else()
+ set(THREAD_LOCAL "__thread")
+ endif()
+ check_c_source_compiles("${THREAD_LOCAL} int i; int main(void) { i = 0; return i; }" HAVE_THREAD_LOCAL)
+ if(HAVE_THREAD_LOCAL)
+ message(STATUS "THREAD_LOCAL = ${THREAD_LOCAL}")
+ else()
+ message(WARNING "Thread-local storage is not available. The TurboJPEG API library's global error handler will not be thread-safe.")
+ unset(THREAD_LOCAL)
+ endif()
+endif()
+
if(UNIX AND NOT APPLE)
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/conftest.map "VERS_1 { global: *; };")
set(CMAKE_REQUIRED_FLAGS
diff --git a/ChangeLog.md b/ChangeLog.md
index 685da69..59fb2de 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -22,6 +22,11 @@
included a similar fix for binary PPM/PGM files with maximum values greater
than 255.
+4. The TurboJPEG API library's global error handler, which is used in functions
+such as `tjBufSize()` and `tjLoadImage()` that do not require a TurboJPEG
+instance handle, is now thread-safe on platforms that support thread-local
+storage.
+
2.0.4
=====
diff --git a/doc/html/group___turbo_j_p_e_g.html b/doc/html/group___turbo_j_p_e_g.html
index 5d67d78..2ee145f 100644
--- a/doc/html/group___turbo_j_p_e_g.html
+++ b/doc/html/group___turbo_j_p_e_g.html
@@ -2129,7 +2129,7 @@
<p>Returns a descriptive error message explaining why the last command failed. </p>
<dl class="params"><dt>Parameters</dt><dd>
<table class="params">
- <tr><td class="paramname">handle</td><td>a handle to a TurboJPEG compressor, decompressor, or transformer instance, or NULL if the error was generated by a global function (but note that retrieving the error message for a global function is not thread-safe.)</td></tr>
+ <tr><td class="paramname">handle</td><td>a handle to a TurboJPEG compressor, decompressor, or transformer instance, or NULL if the error was generated by a global function (but note that retrieving the error message for a global function is thread-safe only on platforms that support thread-local storage.)</td></tr>
</table>
</dd>
</dl>
diff --git a/jconfigint.h.in b/jconfigint.h.in
index 55df053..68cbc2a 100644
--- a/jconfigint.h.in
+++ b/jconfigint.h.in
@@ -7,6 +7,9 @@
/* How to obtain function inlining. */
#define INLINE @INLINE@
+/* How to obtain thread-local storage */
+#define THREAD_LOCAL @THREAD_LOCAL@
+
/* Define to the full name of this package. */
#define PACKAGE_NAME "@CMAKE_PROJECT_NAME@"
diff --git a/turbojpeg.c b/turbojpeg.c
index 6226bc9..e63d113 100644
--- a/turbojpeg.c
+++ b/turbojpeg.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C)2009-2019 D. R. Commander. All Rights Reserved.
+ * Copyright (C)2009-2020 D. R. Commander. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -43,6 +43,7 @@
#include "transupp.h"
#include "./jpegcomp.h"
#include "./cdjpeg.h"
+#include "jconfigint.h"
extern void jpeg_mem_dest_tj(j_compress_ptr, unsigned char **, unsigned long *,
boolean);
@@ -55,7 +56,7 @@
/* Error handling (based on example in example.txt) */
-static char errStr[JMSG_LENGTH_MAX] = "No error";
+static THREAD_LOCAL char errStr[JMSG_LENGTH_MAX] = "No error";
struct my_error_mgr {
struct jpeg_error_mgr pub;
diff --git a/turbojpeg.h b/turbojpeg.h
index 074f015..f3209dd 100644
--- a/turbojpeg.h
+++ b/turbojpeg.h
@@ -1650,7 +1650,7 @@
* @param handle a handle to a TurboJPEG compressor, decompressor, or
* transformer instance, or NULL if the error was generated by a global
* function (but note that retrieving the error message for a global function
- * is not thread-safe.)
+ * is thread-safe only on platforms that support thread-local storage.)
*
* @return a descriptive error message explaining why the last command failed.
*/