TurboJPEG: Thread-safe error message retrieval
Introduce a new C API function (tjGetErrorStr2()) that can be used to
retrieve compression/decompression/transform error messages in a
thread-safe (i.e. instance-specific) manner. Retrieving error messages
from global functions is still thread-unsafe.
Addresses a concern expressed in #151.
diff --git a/turbojpeg.c b/turbojpeg.c
index f3c9922..8b77100 100644
--- a/turbojpeg.c
+++ b/turbojpeg.c
@@ -96,6 +96,8 @@
struct jpeg_decompress_struct dinfo;
struct my_error_mgr jerr;
int init, headerRead;
+ char errStr[JMSG_LENGTH_MAX];
+ boolean isInstanceError;
} tjinstance;
static const int pixelsize[TJ_NUMSAMP]={3, 3, 3, 1, 3, 3};
@@ -126,26 +128,31 @@
{1, 8}
};
-#define _throw(m) {snprintf(errStr, JMSG_LENGTH_MAX, "%s", m); \
+#define _throwg(m) {snprintf(errStr, JMSG_LENGTH_MAX, "%s", m); \
retval=-1; goto bailout;}
+#define _throw(m) {snprintf(this->errStr, JMSG_LENGTH_MAX, "%s", m); \
+ this->isInstanceError=TRUE; _throwg(m);}
#define getinstance(handle) tjinstance *this=(tjinstance *)handle; \
j_compress_ptr cinfo=NULL; j_decompress_ptr dinfo=NULL; \
if(!this) {snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
return -1;} \
cinfo=&this->cinfo; dinfo=&this->dinfo; \
- this->jerr.warning=FALSE;
+ this->jerr.warning=FALSE; \
+ this->isInstanceError=FALSE;
#define getcinstance(handle) tjinstance *this=(tjinstance *)handle; \
j_compress_ptr cinfo=NULL; \
if(!this) {snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
return -1;} \
cinfo=&this->cinfo; \
- this->jerr.warning=FALSE;
+ this->jerr.warning=FALSE; \
+ this->isInstanceError=FALSE;
#define getdinstance(handle) tjinstance *this=(tjinstance *)handle; \
j_decompress_ptr dinfo=NULL; \
if(!this) {snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \
return -1;} \
dinfo=&this->dinfo; \
- this->jerr.warning=FALSE;
+ this->jerr.warning=FALSE; \
+ this->isInstanceError=FALSE;
static int getPixelFormat(int pixelSize, int flags)
{
@@ -272,37 +279,36 @@
return retval;
}
-static int setDecompDefaults(struct jpeg_decompress_struct *dinfo,
- int pixelFormat, int flags)
+static int setDecompDefaults(tjinstance *this, int pixelFormat, int flags)
{
int retval=0;
switch(pixelFormat)
{
case TJPF_GRAY:
- dinfo->out_color_space=JCS_GRAYSCALE; break;
+ this->dinfo.out_color_space=JCS_GRAYSCALE; break;
#if JCS_EXTENSIONS==1
case TJPF_RGB:
- dinfo->out_color_space=JCS_EXT_RGB; break;
+ this->dinfo.out_color_space=JCS_EXT_RGB; break;
case TJPF_BGR:
- dinfo->out_color_space=JCS_EXT_BGR; break;
+ this->dinfo.out_color_space=JCS_EXT_BGR; break;
case TJPF_RGBX:
- dinfo->out_color_space=JCS_EXT_RGBX; break;
+ this->dinfo.out_color_space=JCS_EXT_RGBX; break;
case TJPF_BGRX:
- dinfo->out_color_space=JCS_EXT_BGRX; break;
+ this->dinfo.out_color_space=JCS_EXT_BGRX; break;
case TJPF_XRGB:
- dinfo->out_color_space=JCS_EXT_XRGB; break;
+ this->dinfo.out_color_space=JCS_EXT_XRGB; break;
case TJPF_XBGR:
- dinfo->out_color_space=JCS_EXT_XBGR; break;
+ this->dinfo.out_color_space=JCS_EXT_XBGR; break;
#if JCS_ALPHA_EXTENSIONS==1
case TJPF_RGBA:
- dinfo->out_color_space=JCS_EXT_RGBA; break;
+ this->dinfo.out_color_space=JCS_EXT_RGBA; break;
case TJPF_BGRA:
- dinfo->out_color_space=JCS_EXT_BGRA; break;
+ this->dinfo.out_color_space=JCS_EXT_BGRA; break;
case TJPF_ARGB:
- dinfo->out_color_space=JCS_EXT_ARGB; break;
+ this->dinfo.out_color_space=JCS_EXT_ARGB; break;
case TJPF_ABGR:
- dinfo->out_color_space=JCS_EXT_ABGR; break;
+ this->dinfo.out_color_space=JCS_EXT_ABGR; break;
#endif
#else
case TJPF_RGB:
@@ -315,15 +321,15 @@
case TJPF_BGRA:
case TJPF_ARGB:
case TJPF_ABGR:
- dinfo->out_color_space=JCS_RGB; break;
+ this->dinfo.out_color_space=JCS_RGB; break;
#endif
case TJPF_CMYK:
- dinfo->out_color_space=JCS_CMYK; break;
+ this->dinfo.out_color_space=JCS_CMYK; break;
default:
_throw("Unsupported pixel format");
}
- if(flags&TJFLAG_FASTDCT) dinfo->dct_method=JDCT_FASTEST;
+ if(flags&TJFLAG_FASTDCT) this->dinfo.dct_method=JDCT_FASTEST;
bailout:
return retval;
@@ -542,6 +548,18 @@
/* General API functions */
+DLLEXPORT char* DLLCALL tjGetErrorStr2(tjhandle handle)
+{
+ tjinstance *this=(tjinstance *)handle;
+ if(this && this->isInstanceError)
+ {
+ this->isInstanceError=FALSE;
+ return this->errStr;
+ }
+ else return errStr;
+}
+
+
DLLEXPORT char* DLLCALL tjGetErrorStr(void)
{
return errStr;
@@ -615,6 +633,7 @@
return NULL;
}
MEMZERO(this, sizeof(tjinstance));
+ snprintf(this->errStr, JMSG_LENGTH_MAX, "No error");
return _tjInitCompress(this);
}
@@ -624,7 +643,7 @@
{
unsigned long retval=0; int mcuw, mcuh, chromasf;
if(width<1 || height<1 || jpegSubsamp<0 || jpegSubsamp>=NUMSUBOPT)
- _throw("tjBufSize(): Invalid argument");
+ _throwg("tjBufSize(): Invalid argument");
/* This allows for rare corner cases in which a JPEG image can actually be
larger than the uncompressed input (we wouldn't mention it if it hadn't
@@ -642,7 +661,7 @@
{
unsigned long retval=0;
if(width<1 || height<1)
- _throw("TJBUFSIZE(): Invalid argument");
+ _throwg("TJBUFSIZE(): Invalid argument");
/* This allows for rare corner cases in which a JPEG image can actually be
larger than the uncompressed input (we wouldn't mention it if it hadn't
@@ -660,7 +679,7 @@
int retval=0, nc, i;
if(subsamp<0 || subsamp>=NUMSUBOPT)
- _throw("tjBufSizeYUV2(): Invalid argument");
+ _throwg("tjBufSizeYUV2(): Invalid argument");
nc=(subsamp==TJSAMP_GRAY? 1:3);
for(i=0; i<nc; i++)
@@ -694,10 +713,10 @@
int pw, nc, retval=0;
if(width<1 || subsamp<0 || subsamp>=TJ_NUMSAMP)
- _throw("tjPlaneWidth(): Invalid argument");
+ _throwg("tjPlaneWidth(): Invalid argument");
nc=(subsamp==TJSAMP_GRAY? 1:3);
if(componentID<0 || componentID>=nc)
- _throw("tjPlaneWidth(): Invalid argument");
+ _throwg("tjPlaneWidth(): Invalid argument");
pw=PAD(width, tjMCUWidth[subsamp]/8);
if(componentID==0)
@@ -715,10 +734,10 @@
int ph, nc, retval=0;
if(height<1 || subsamp<0 || subsamp>=TJ_NUMSAMP)
- _throw("tjPlaneHeight(): Invalid argument");
+ _throwg("tjPlaneHeight(): Invalid argument");
nc=(subsamp==TJSAMP_GRAY? 1:3);
if(componentID<0 || componentID>=nc)
- _throw("tjPlaneHeight(): Invalid argument");
+ _throwg("tjPlaneHeight(): Invalid argument");
ph=PAD(height, tjMCUHeight[subsamp]/8);
if(componentID==0)
@@ -738,7 +757,7 @@
int pw, ph;
if(width<1 || height<1 || subsamp<0 || subsamp>=NUMSUBOPT)
- _throw("tjPlaneSizeYUV(): Invalid argument");
+ _throwg("tjPlaneSizeYUV(): Invalid argument");
pw=tjPlaneWidth(componentID, width, subsamp);
ph=tjPlaneHeight(componentID, height, subsamp);
@@ -1032,6 +1051,8 @@
unsigned char *dstPlanes[3];
int pw0, ph0, strides[3], retval=-1;
+ getcinstance(handle);
+
if(width<=0 || height<=0 || dstBuf==NULL || pad<0 || !isPow2(pad)
|| subsamp<0 || subsamp>=NUMSUBOPT)
_throw("tjEncodeYUV3(): Invalid argument");
@@ -1224,6 +1245,8 @@
const unsigned char *srcPlanes[3];
int pw0, ph0, strides[3], retval=-1;
+ getcinstance(handle);
+
if(srcBuf==NULL || width<=0 || pad<1 || height<=0 || subsamp<0
|| subsamp>=NUMSUBOPT)
_throw("tjCompressFromYUV(): Invalid argument");
@@ -1292,6 +1315,7 @@
return NULL;
}
MEMZERO(this, sizeof(tjinstance));
+ snprintf(this->errStr, JMSG_LENGTH_MAX, "No error");
return _tjInitDecompress(this);
}
@@ -1410,7 +1434,7 @@
jpeg_mem_src_tj(dinfo, jpegBuf, jpegSize);
jpeg_read_header(dinfo, TRUE);
- if(setDecompDefaults(dinfo, pixelFormat, flags)==-1)
+ if(setDecompDefaults(this, pixelFormat, flags)==-1)
{
retval=-1; goto bailout;
}
@@ -1616,7 +1640,7 @@
dinfo->marker->read_markers=old_read_markers;
dinfo->marker->reset_marker_reader=old_reset_marker_reader;
- if(setDecompDefaults(dinfo, pixelFormat, flags)==-1)
+ if(setDecompDefaults(this, pixelFormat, flags)==-1)
{
retval=-1; goto bailout;
}
@@ -1728,6 +1752,8 @@
const unsigned char *srcPlanes[3];
int pw0, ph0, strides[3], retval=-1;
+ getdinstance(handle);
+
if(srcBuf==NULL || pad<0 || !isPow2(pad) || subsamp<0 || subsamp>=NUMSUBOPT
|| width<=0 || height<=0)
_throw("tjDecodeYUV(): Invalid argument");
@@ -2025,6 +2051,7 @@
return NULL;
}
MEMZERO(this, sizeof(tjinstance));
+ snprintf(this->errStr, JMSG_LENGTH_MAX, "No error");
handle=_tjInitCompress(this);
if(!handle) return NULL;
handle=_tjInitDecompress(this);