summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2016-09-27 14:40:25 +0100
committerRobin Watts <robin.watts@artifex.com>2016-09-27 16:50:34 +0100
commit85b05e85f9952036abb61d7c99b9bb724bb22563 (patch)
treed1350b9c7a02da811ec438eae175ca9689bf9d4c
parent99c9cbc8f52a7f7da6c82a0a43eda9144e0b6452 (diff)
downloadghostpdl-openjpeg-update-20160927.tar.gz
OpenJPEG allocation fixes.openjpeg-update-20160927
The old version of openjpeg allocates using malloc/free. The new version of openjpeg allocates via opj_malloc (and co). We can capture these and pass them down to our own allocators, but we cannot get openjpeg to pass us a gs_memory_t * to use. We therefore resort to using a static to hold 'the current gs_memory_t *' before we call openjpeg. To keep this threadsafe we add some private data to the gslibctx, which the openjpeg implementation can use to hold a lock. We add calls to both jpx decode implementations to init this private data (where the luratech implementation is null).
-rw-r--r--base/gslibctx.c9
-rw-r--r--base/gslibctx.h6
-rw-r--r--base/sjpx_luratech.c9
-rw-r--r--base/sjpx_openjpeg.c178
4 files changed, 198 insertions, 4 deletions
diff --git a/base/gslibctx.c b/base/gslibctx.c
index fa4432a85..eaa04580f 100644
--- a/base/gslibctx.c
+++ b/base/gslibctx.c
@@ -175,7 +175,7 @@ int gs_lib_ctx_init( gs_memory_t *mem )
/* Initialise the underlying CMS. */
if (gscms_create(mem)) {
-
+Failure:
gs_free_object(mem, mem->gs_lib_ctx->default_device_list,
"gs_lib_ctx_fin");
@@ -183,6 +183,12 @@ int gs_lib_ctx_init( gs_memory_t *mem )
mem->gs_lib_ctx = NULL;
return -1;
}
+
+ /* Initialise any lock required for the jpx codec */
+ if (sjpxd_create(mem)) {
+ gscms_destroy(mem);
+ goto Failure;
+ }
gp_get_realtime(pio->real_time_0);
@@ -214,6 +220,7 @@ void gs_lib_ctx_fin(gs_memory_t *mem)
ctx = mem->gs_lib_ctx;
ctx_mem = ctx->memory;
+ sjpxd_destroy(mem);
gscms_destroy(ctx_mem);
gs_free_object(ctx_mem, ctx->profiledir,
"gs_lib_ctx_fin");
diff --git a/base/gslibctx.h b/base/gslibctx.h
index 84ec205f2..7a4e110ba 100644
--- a/base/gslibctx.h
+++ b/base/gslibctx.h
@@ -85,6 +85,7 @@ typedef struct gs_lib_ctx_s
char *default_device_list;
int gcsignal;
int scanconverter;
+ void *sjpxd_private; /* optional for use of jpx codec */
} gs_lib_ctx_t;
enum {
@@ -135,4 +136,9 @@ gs_lib_ctx_get_default_device_list(const gs_memory_t *mem, char** dev_list_str,
#define IS_LIBCTX_STDOUT(mem, f) (f == mem->gs_lib_ctx->fstdout)
#define IS_LIBCTX_STDERR(mem, f) (f == mem->gs_lib_ctx->fstderr)
+/* Functions to init/fin JPX decoder libctx entry */
+int sjpxd_create(gs_memory_t *mem);
+
+void sjpxd_destroy(gs_memory_t *mem);
+
#endif /* GSLIBCTX_H */
diff --git a/base/sjpx_luratech.c b/base/sjpx_luratech.c
index a2f1fad3b..8488cc6d4 100644
--- a/base/sjpx_luratech.c
+++ b/base/sjpx_luratech.c
@@ -1076,6 +1076,15 @@ s_jpxe_release(stream_state *ss)
gs_free_object(state->memory->non_gc_memory, state->inbuf, "s_jpxe_release(inbuf)");
}
+int sjpxd_create(gs_memory_t *mem)
+{
+ return 0;
+}
+
+void sjpxd_destroy(gs_memory_t *mem)
+{
+}
+
/* encoder stream template */
const stream_template s_jpxe_template = {
&st_jpxe_state,
diff --git a/base/sjpx_openjpeg.c b/base/sjpx_openjpeg.c
index cd52ccaad..e8d919a5d 100644
--- a/base/sjpx_openjpeg.c
+++ b/base/sjpx_openjpeg.c
@@ -22,6 +22,144 @@
#include "gdebug.h"
#include "strimpl.h"
#include "sjpx_openjpeg.h"
+#include "gxsync.h"
+#include "assert_.h"
+#include "opj_malloc.h"
+
+/* Some locking to get around the criminal lack of context
+ * in the openjpeg library. */
+static gs_memory_t *opj_memory;
+
+int sjpxd_create(gs_memory_t *mem)
+{
+#if !defined(SHARE_JPX) || (SHARE_JPX == 0)
+ gs_lib_ctx_t *ctx = mem->gs_lib_ctx;
+
+ ctx->sjpxd_private = gx_monitor_label(gx_monitor_alloc(mem), "sjpxd_monitor");
+ if (ctx->sjpxd_private == NULL)
+ return gs_error_VMerror;
+#endif
+ return 0;
+}
+
+void sjpxd_destroy(gs_memory_t *mem)
+{
+#if !defined(SHARE_JPX) || (SHARE_JPX == 0)
+ gs_lib_ctx_t *ctx = mem->gs_lib_ctx;
+
+ gx_monitor_free((gx_monitor_t *)ctx->sjpxd_private);
+ ctx->sjpxd_private = NULL;
+#endif
+}
+
+static int opj_lock(gs_memory_t *mem)
+{
+#if !defined(SHARE_JPX) || (SHARE_JPX == 0)
+ gs_lib_ctx_t *ctx = mem->gs_lib_ctx;
+ int ret;
+
+ ret = gx_monitor_enter((gx_monitor_t *)ctx->sjpxd_private);
+ assert(opj_memory == NULL);
+ opj_memory = mem->non_gc_memory;
+ return ret;
+#else
+ return 0;
+#endif
+}
+
+static int opj_unlock(gs_memory_t *mem)
+{
+#if !defined(SHARE_JPX) || (SHARE_JPX == 0)
+ gs_lib_ctx_t *ctx = mem->gs_lib_ctx;
+
+ assert(opj_memory != NULL);
+ opj_memory = NULL;
+ return gx_monitor_leave((gx_monitor_t *)ctx->sjpxd_private);
+#else
+ return 0;
+#endif
+}
+
+#if !defined(SHARE_JPX) || (SHARE_JPX == 0)
+/* Allocation routines that use the memory pointer given above */
+void *opj_malloc(size_t size)
+{
+ if (size == 0)
+ return NULL;
+
+ assert(opj_memory != NULL);
+
+ return (void *)gs_alloc_bytes(opj_memory, size, "opj_malloc");
+}
+
+void *opj_calloc(size_t n, size_t size)
+{
+ void *ptr;
+
+ /* FIXME: Check for overflow? */
+ size *= n;
+
+ ptr = opj_malloc(size);
+ if (ptr)
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+void *opj_realloc(void *ptr, size_t size)
+{
+ if (ptr == NULL)
+ return opj_malloc(size);
+
+ if (size == 0)
+ {
+ opj_free(ptr);
+ return NULL;
+ }
+
+ return gs_resize_object(opj_memory, ptr, size, "opj_malloc");
+}
+
+void opj_free(void *ptr)
+{
+ gs_free_object(opj_memory, ptr, "opj_malloc");
+}
+
+void * opj_aligned_malloc(size_t size)
+{
+ uint8_t *ptr;
+ int off;
+
+ if (size == 0)
+ return NULL;
+
+ size += 16 + sizeof(uint8_t);
+ ptr = opj_malloc(size);
+ if (ptr == NULL)
+ return NULL;
+ off = 16-(((int)(intptr_t)ptr) & 15);
+ ptr[off-1] = off;
+ return ptr + off;
+}
+
+void opj_aligned_free(void* ptr_)
+{
+ uint8_t *ptr = (uint8_t *)ptr_;
+ uint8_t off;
+ if (ptr == NULL)
+ return;
+
+ off = ptr[-1];
+ opj_free((void *)(((unsigned char *)ptr) - off));
+}
+
+#if 0
+/* UNUSED currently, and moderately tricky, so deferred until required */
+void * opj_aligned_realloc(void *ptr, size_t size)
+{
+ return opj_realloc(ptr, size);
+}
+#endif
+#endif
gs_private_st_simple(st_jpxd_state, stream_jpxd_state,
"JPXDecode filter state"); /* creates a gc object for our state,
@@ -536,11 +674,17 @@ s_opjd_process(stream_state * ss, stream_cursor_read * pr,
{
stream_jpxd_state *const state = (stream_jpxd_state *) ss;
long in_size = pr->limit - pr->ptr;
+ int locked = 0;
+ int code;
if (in_size > 0)
{
/* buffer available data */
- int code = s_opjd_accumulate_input(state, pr);
+ code = opj_lock(ss->memory);
+ if (code < 0) return code;
+ locked = 1;
+
+ code = s_opjd_accumulate_input(state, pr);
if (code < 0) return code;
if (state->codec == NULL) {
@@ -550,7 +694,11 @@ s_opjd_process(stream_state * ss, stream_cursor_read * pr,
code = s_opjd_set_codec_format(ss, OPJ_CODEC_J2K);
else
code = s_opjd_set_codec_format(ss, OPJ_CODEC_JP2);
- if (code < 0) return code;
+ if (code < 0)
+ {
+ (void)opj_unlock(ss->memory);
+ return code;
+ }
}
}
@@ -558,7 +706,15 @@ s_opjd_process(stream_state * ss, stream_cursor_read * pr,
{
if (state->image == NULL)
{
- int ret = ERRC;
+ int ret;
+
+ if (locked == 0)
+ {
+ ret = opj_lock(ss->memory);
+ if (ret < 0) return ret;
+ locked = 1;
+ }
+
#if OPJ_VERSION_MAJOR >= 2 && OPJ_VERSION_MINOR >= 1
opj_stream_set_user_data(state->stream, &(state->sb), NULL);
#else
@@ -567,7 +723,16 @@ s_opjd_process(stream_state * ss, stream_cursor_read * pr,
opj_stream_set_user_data_length(state->stream, state->sb.size);
ret = decode_image(state);
if (ret != 0)
+ {
+ (void)opj_unlock(ss->memory);
return ret;
+ }
+ }
+
+ if (locked)
+ {
+ code = opj_unlock(ss->memory);
+ if (code < 0) return code;
}
/* copy out available data */
@@ -575,6 +740,9 @@ s_opjd_process(stream_state * ss, stream_cursor_read * pr,
}
+ if (locked)
+ return opj_unlock(ss->memory);
+
/* ask for more data */
return 0;
}
@@ -600,6 +768,8 @@ s_opjd_release(stream_state *ss)
if (state->codec == NULL)
return;
+ (void)opj_lock(ss->memory);
+
/* free image data structure */
if (state->image)
opj_image_destroy(state->image);
@@ -612,6 +782,8 @@ s_opjd_release(stream_state *ss)
if (state->codec)
opj_destroy_codec(state->codec);
+ (void)opj_unlock(ss->memory);
+
/* free input buffer */
if (state->sb.data)
gs_free_object(state->memory->non_gc_memory, state->sb.data, "s_opjd_release(sb.data)");