From 444e0bf9c43bae0261660e6318ba0e514c18d41e Mon Sep 17 00:00:00 2001
From: Chris Liddell <chris.liddell@artifex.com>
Date: Mon, 16 Mar 2015 12:52:49 +0000
Subject: [PATCH] Bug 695862: use PTHREAD_MUTEX_RECURSIVE(_NP) if available

or properly emulate recursive mutexes ourselves.

No cluster differences
---
 config.mak.in      |  2 +-
 configure.ac       | 37 +++++++++++++++++++----------
 gs/Makefile.in     |  2 +-
 gs/base/gp_psync.c | 70 ++++++++++++++++++++++++++++++++++++++++++++----------
 gs/configure.ac    | 37 +++++++++++++++++++----------
 5 files changed, 108 insertions(+), 40 deletions(-)

Index: ghostscript-9.15/configure.ac
===================================================================
--- ghostscript-9.15.orig/configure.ac
+++ ghostscript-9.15/configure.ac
@@ -494,8 +494,13 @@ AC_CHECK_LIB(m, cos)
 
 SYNC="nosync"
 PTHREAD_LIBS=""
+RECURSIVE_MUTEXATTR=""
 
-case `uname` in
+AC_ARG_ENABLE([threading], AC_HELP_STRING([--disable-threading],
+    [disable support for multithreaded rendering]))
+
+if test "$enable_threading" != "no"; then
+  case `uname` in
     MINGW*)
       AC_MSG_WARN([disabling support for pthreads......])
     ;;
@@ -504,11 +509,19 @@ case `uname` in
         SYNC=posync;
         PTHREAD_LIBS="-lpthread"
       ])
+      AC_TRY_COMPILE([#include <pthread.h>],
+           [static int k = PTHREAD_MUTEX_RECURSIVE;],
+           [RECURSIVE_MUTEXATTR="-DGS_RECURSIVE_MUTEXATTR=PTHREAD_MUTEX_RECURSIVE"],
+           [AC_TRY_COMPILE([#include <pthread.h>],
+              [static int k = PTHREAD_MUTEX_RECURSIVE_NP;],
+              [RECURSIVE_MUTEXATTR="-DGS_RECURSIVE_MUTEXATTR=PTHREAD_MUTEX_RECURSIVE_NP"])])
     ;;
-esac
+  esac
+fi
 
 AC_SUBST(SYNC)
 AC_SUBST(PTHREAD_LIBS)
+AC_SUBST(RECURSIVE_MUTEXATTR)
 
 AC_MSG_CHECKING([for local trio library source])
 if test -f $srcdir/trio/trio.h; then
Index: ghostscript-9.15/Makefile.in
===================================================================
--- ghostscript-9.15.orig/Makefile.in
+++ ghostscript-9.15/Makefile.in
@@ -142,7 +142,7 @@ GENOPT=
 # -DHAVE_SSE2
 #       use sse2 intrinsics
 
-CAPOPT= @HAVE_MKSTEMP@ @HAVE_FILE64@ @HAVE_FSEEKO@ @HAVE_MKSTEMP64@ @HAVE_FONTCONFIG@ @HAVE_LIBIDN@ @HAVE_SETLOCALE@ @HAVE_SSE2@ @HAVE_DBUS@ @HAVE_BSWAP32@ @HAVE_BYTESWAP_H@ @HAVE_STRERROR@
+CAPOPT= @HAVE_MKSTEMP@ @HAVE_FILE64@ @HAVE_FSEEKO@ @HAVE_MKSTEMP64@ @HAVE_FONTCONFIG@ @HAVE_LIBIDN@ @HAVE_SETLOCALE@ @HAVE_SSE2@ @HAVE_DBUS@ @HAVE_BSWAP32@ @HAVE_BYTESWAP_H@ @HAVE_STRERROR@ @RECURSIVE_MUTEXATTR@
 
 # Define the name of the executable file.
 
Index: ghostscript-9.15/base/gp_psync.c
===================================================================
--- ghostscript-9.15.orig/base/gp_psync.c
+++ ghostscript-9.15/base/gp_psync.c
@@ -128,13 +128,20 @@ gp_semaphore_signal(gp_semaphore * sema)
 /* Monitor supports enter/leave semantics */
 
 /*
- * We need PTHREAD_MUTEX_RECURSIVE behavior, but this isn't totally portable
- * so we implement it in a more portable fashion, keeping track of the
- * owner thread using 'pthread_self()'
+ * We need PTHREAD_MUTEX_RECURSIVE behavior, but this isn't
+ * supported on all pthread platforms, so if it's available
+ * we'll use it, otherwise we'll emulate it.
+ * GS_RECURSIVE_MUTEXATTR is set by the configure script
+ * on Unix-like machines to the attribute setting for
+ * PTHREAD_MUTEX_RECURSIVE - on linux this is usually
+ * PTHREAD_MUTEX_RECURSIVE_NP
  */
 typedef struct gp_pthread_recursive_s {
     pthread_mutex_t mutex;	/* actual mutex */
+#ifndef GS_RECURSIVE_MUTEXATTR
     pthread_t	self_id;	/* owner */
+    int lcount;
+#endif
 } gp_pthread_recursive_t;
 
 uint
@@ -148,12 +155,32 @@ gp_monitor_open(gp_monitor * mona)
 {
     pthread_mutex_t *mon;
     int scode;
+    pthread_mutexattr_t attr;
+    pthread_mutexattr_t *attrp = NULL;
 
     if (!mona)
         return -1;		/* monitors are not movable */
-    mon = &((gp_pthread_recursive_t *)mona)->mutex;
+
+
+#ifdef GS_RECURSIVE_MUTEXATTR
+    attrp = &attr;
+    scode = pthread_mutexattr_init(attrp);
+    if (scode < 0) goto done;
+
+    scode = pthread_mutexattr_settype(attrp, GS_RECURSIVE_MUTEXATTR);
+    if (scode < 0) {
+        goto done;
+    }
+#else     
     ((gp_pthread_recursive_t *)mona)->self_id = 0;	/* Not valid unless mutex is locked */
-    scode = pthread_mutex_init(mon, NULL);
+    ((gp_pthread_recursive_t *)mona)->lcount = 0;
+#endif
+
+    mon = &((gp_pthread_recursive_t *)mona)->mutex;
+    scode = pthread_mutex_init(mon, attrp);
+    if (attrp)
+        (void)pthread_mutexattr_destroy(attrp);
+done:
     return SEM_ERROR_CODE(scode);
 }
 
@@ -173,29 +200,48 @@ gp_monitor_enter(gp_monitor * mona)
     pthread_mutex_t * const mon = (pthread_mutex_t *)mona;
     int scode;
 
+#ifdef GS_RECURSIVE_MUTEXATTR
+    scode = pthread_mutex_lock(mon);
+#else
     if ((scode = pthread_mutex_trylock(mon)) == 0) {
         ((gp_pthread_recursive_t *)mona)->self_id = pthread_self();
-        return SEM_ERROR_CODE(scode);
+        ((gp_pthread_recursive_t *)mona)->lcount++;
     } else {
-        if (pthread_equal(pthread_self(),((gp_pthread_recursive_t *)mona)->self_id))
-            return 0;
+        if (pthread_equal(pthread_self(),((gp_pthread_recursive_t *)mona)->self_id)) {
+            ((gp_pthread_recursive_t *)mona)->lcount++;
+            scode = 0;
+        }
         else {
             /* we were not the owner, wait */
             scode = pthread_mutex_lock(mon);
             ((gp_pthread_recursive_t *)mona)->self_id = pthread_self();
-            return SEM_ERROR_CODE(scode);
+            ((gp_pthread_recursive_t *)mona)->lcount++;
         }
     }
+#endif
+    return SEM_ERROR_CODE(scode);
 }
 
 int
 gp_monitor_leave(gp_monitor * mona)
 {
     pthread_mutex_t * const mon = (pthread_mutex_t *)mona;
-    int scode;
+    int scode = 0;
 
+#ifdef GS_RECURSIVE_MUTEXATTR
     scode = pthread_mutex_unlock(mon);
-    ((gp_pthread_recursive_t *)mona)->self_id = 0;	/* Not valid unless mutex is locked */
+#else
+    if (pthread_equal(pthread_self(),((gp_pthread_recursive_t *)mona)->self_id)) {
+      if ((--((gp_pthread_recursive_t *)mona)->lcount) == 0) {
+          scode = pthread_mutex_unlock(mon);
+          ((gp_pthread_recursive_t *)mona)->self_id = 0;	/* Not valid unless mutex is locked */
+
+      }
+    }
+    else {
+        scode = -1 /* should be EPERM */;
+    }
+#endif
     return SEM_ERROR_CODE(scode);
 }
 
