From 88f4c1c0f96f82ac19ffe2b3a26214ace1b225d5 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Mon, 15 Aug 2011 13:20:33 -0400
Subject: [PATCH] Fix thread deadlock by using pthreads library instead of
 NSPR.

This borrows wrap.c and wrap.h from the slapi-nis plugin.

The 389-ds team is in the process of exposing slapi_rwlock which we
will switch to when it is available.

https://fedorahosted.org/freeipa/ticket/1630
---
 daemons/ipa-slapi-plugins/common/wrap.c           |  295 +++++++++++++++++++++
 daemons/ipa-slapi-plugins/common/wrap.h           |   46 ++++
 daemons/ipa-slapi-plugins/ipa-modrdn/Makefile.am  |    3 +
 daemons/ipa-slapi-plugins/ipa-modrdn/ipa_modrdn.c |   11 +-
 daemons/ipa-slapi-plugins/ipa-uuid/Makefile.am    |    3 +
 daemons/ipa-slapi-plugins/ipa-uuid/ipa_uuid.c     |   11 +-
 6 files changed, 359 insertions(+), 10 deletions(-)
 create mode 100644 daemons/ipa-slapi-plugins/common/wrap.c
 create mode 100644 daemons/ipa-slapi-plugins/common/wrap.h

diff --git a/daemons/ipa-slapi-plugins/common/wrap.c b/daemons/ipa-slapi-plugins/common/wrap.c
new file mode 100644
index 0000000..ce15c8a
--- /dev/null
+++ b/daemons/ipa-slapi-plugins/common/wrap.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright 2008,2010 Red Hat, Inc.
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This Program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; if not, write to the
+ *
+ *   Free Software Foundation, Inc.
+ *   59 Temple Place, Suite 330
+ *   Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <pthread.h>
+#include <search.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifdef HAVE_DIRSRV_SLAPI_PLUGIN_H
+#include <nspr.h>
+#include <nss.h>
+#include <dirsrv/slapi-plugin.h>
+#else
+#include <slapi-plugin.h>
+#endif
+
+#if defined(USE_NSPR_THREADS) || defined(USE_NSPR_LOCKS)
+#include <nspr.h>
+#endif
+
+#if defined(USE_PTHREADS) || defined(USE_PTHREAD_LOCKS)
+#include <pthread.h>
+#endif
+
+#include <rpc/xdr.h>
+
+#include "wrap.h"
+
+struct wrapped_thread {
+#if defined(USE_PTHREADS)
+	pthread_t tid;
+	void *arg;
+#elif defined(USE_NSPR_THREADS)
+	PRThread *tid;
+	struct wrapped_pthread_args {
+		void * (*fn)(struct wrapped_thread *);
+		void *arg, *result;
+	} args;
+#else
+#error "Unknown threading model!"
+#endif
+	int stopfd[2];
+};
+
+struct wrapped_rwlock {
+#if defined(USE_PTHREAD_LOCKS)
+	pthread_rwlock_t rwlock;
+#elif defined(USE_NSPR_LOCKS)
+	PRRWLock *rwlock;
+#else
+#error "Unknown thread-safe locking model!"
+#endif
+};
+
+#ifdef USE_NSPR_THREADS
+static void
+wrap_pthread_starter(void *p)
+{
+	struct wrapped_thread *t = p;
+	t->args.result = t->args.fn(t);
+}
+#endif
+
+void *
+wrap_thread_arg(struct wrapped_thread *t)
+{
+#ifdef USE_PTHREADS
+	return t->arg;
+#endif
+#ifdef USE_NSPR_THREADS
+	return t->args.arg;
+#endif
+}
+
+struct wrapped_thread *
+wrap_start_thread(void * (*fn)(struct wrapped_thread *), void *arg)
+{
+	struct wrapped_thread *t;
+	t = malloc(sizeof(*t));
+	if (t == NULL) {
+		return NULL;
+	}
+	memset(t, 0, sizeof(*t));
+	if (pipe(t->stopfd) == -1) {
+		free(t);
+		return NULL;
+	}
+#ifdef USE_PTHREADS
+	t->arg = arg;
+	if (pthread_create(&t->tid, NULL, fn, t) != 0) {
+		free(t);
+		return NULL;
+	}
+#endif
+#ifdef USE_NSPR_THREADS
+	t->args.fn = fn;
+	t->args.arg = arg;
+	t->args.result = NULL;
+	t->tid = PR_CreateThread(PR_SYSTEM_THREAD,
+				 wrap_pthread_starter, t,
+				 PR_PRIORITY_NORMAL,
+				 PR_GLOBAL_THREAD,
+				 PR_JOINABLE_THREAD,
+				 0);
+	if (t->tid == NULL) {
+		free(t);
+		return NULL;
+	}
+#endif
+	return t;
+}
+
+void *
+wrap_stop_thread(struct wrapped_thread *t)
+{
+	void *returned = NULL;
+#ifdef USE_PTHREADS
+	write(t->stopfd[1], "", 1);
+	close(t->stopfd[1]);
+	pthread_join(t->tid, &returned);
+#endif
+#ifdef USE_NSPR_THREADS
+	write(t->stopfd[1], "", 1);
+	close(t->stopfd[1]);
+	PR_JoinThread(t->tid);
+	returned = t->args.result;
+#endif
+	free(t);
+	return returned;
+}
+
+int
+wrap_thread_stopfd(struct wrapped_thread *t)
+{
+	int ret;
+#ifdef USE_PTHREADS
+	ret = t->stopfd[0];
+#endif
+#ifdef USE_NSPR_THREADS
+	ret = t->stopfd[0];
+#endif
+	return ret;
+}
+
+struct wrapped_rwlock *
+wrap_new_rwlock(void)
+{
+	struct wrapped_rwlock *rwlock;
+	rwlock = malloc(sizeof(*rwlock));
+	if (rwlock == NULL) {
+		return NULL;
+	}
+#ifdef USE_PTHREAD_LOCKS
+	if (pthread_rwlock_init(&rwlock->rwlock, NULL) != 0) {
+		free(rwlock);
+		return NULL;
+	}
+#endif
+#ifdef USE_NSPR_LOCKS
+	rwlock->rwlock = PR_NewRWLock(PR_RWLOCK_RANK_NONE,
+				      PACKAGE_NAME "-rw-lock");
+	if (rwlock->rwlock == NULL) {
+		free(rwlock);
+		return NULL;
+	}
+#endif
+	return rwlock;
+}
+
+void
+wrap_free_rwlock(struct wrapped_rwlock *rwlock)
+{
+#ifdef USE_PTHREAD_LOCKS
+	pthread_rwlock_destroy(&rwlock->rwlock);
+#endif
+#ifdef USE_NSPR_LOCKS
+	PR_DestroyRWLock(rwlock->rwlock);
+#endif
+	free(rwlock);
+}
+
+void
+wrap_rwlock_rdlock(struct wrapped_rwlock *rwlock)
+{
+#ifdef USE_PTHREAD_LOCKS
+	pthread_rwlock_rdlock(&rwlock->rwlock);
+#endif
+#ifdef USE_NSPR_LOCKS
+	PR_RWLock_Rlock(rwlock->rwlock);
+#endif
+}
+
+void
+wrap_rwlock_wrlock(struct wrapped_rwlock *rwlock)
+{
+#ifdef USE_PTHREAD_LOCKS
+	pthread_rwlock_wrlock(&rwlock->rwlock);
+#endif
+#ifdef USE_NSPR_LOCKS
+	PR_RWLock_Wlock(rwlock->rwlock);
+#endif
+}
+
+void
+wrap_rwlock_unlock(struct wrapped_rwlock *rwlock)
+{
+#ifdef USE_PTHREAD_LOCKS
+	pthread_rwlock_unlock(&rwlock->rwlock);
+#endif
+#ifdef USE_NSPR_LOCKS
+	PR_RWLock_Unlock(rwlock->rwlock);
+#endif
+}
+
+static int
+wrap_search_internal_get_entry_cb(Slapi_Entry *e, void *cb)
+{
+	Slapi_Entry **ret = cb;
+	if (*ret) {
+		slapi_entry_free(*ret);
+	}
+	*ret = slapi_entry_dup(e);
+	return 0;
+}
+
+int
+wrap_search_internal_get_entry(Slapi_DN *dn, char *filter, char **attrs,
+			       Slapi_Entry **ret_entry, void *caller_id)
+{
+	Slapi_PBlock *pb;
+	int ret;
+#ifdef HAVE_SLAPI_SEARCH_INTERNAL_GET_ENTRY
+	if (filter == NULL) {
+		return slapi_search_internal_get_entry(dn, attrs,
+						       ret_entry, caller_id);
+	}
+#endif
+	*ret_entry = NULL;
+	pb = slapi_pblock_new();
+	if (pb == NULL) {
+		return -1;
+	}
+	slapi_search_internal_set_pb(pb, slapi_sdn_get_dn(dn), LDAP_SCOPE_BASE,
+				     filter ? filter : "(objectClass=*)", attrs,
+				     FALSE, NULL, NULL, caller_id, 0);
+	ret = slapi_search_internal_callback_pb(pb, ret_entry,
+						NULL,
+						wrap_search_internal_get_entry_cb,
+						NULL);
+	slapi_pblock_destroy(pb);
+	return ret;
+}
+
+static __thread int call_level = 0;
+
+int
+wrap_get_call_level(void)
+{
+	return call_level;
+}
+int
+wrap_inc_call_level(void)
+{
+	return ++call_level;
+}
+int
+wrap_dec_call_level(void)
+{
+	return --call_level;
+}
diff --git a/daemons/ipa-slapi-plugins/common/wrap.h b/daemons/ipa-slapi-plugins/common/wrap.h
new file mode 100644
index 0000000..2d25a02
--- /dev/null
+++ b/daemons/ipa-slapi-plugins/common/wrap.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2008,2010 Red Hat, Inc.
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This Program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; if not, write to the
+ *
+ *   Free Software Foundation, Inc.
+ *   59 Temple Place, Suite 330
+ *   Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef wrap_h
+#define wrap_h
+
+struct wrapped_thread;
+struct wrapped_rwlock;
+
+struct wrapped_thread * wrap_start_thread(void * (*fn)(struct wrapped_thread *),
+					  void *arg);
+void *wrap_stop_thread(struct wrapped_thread *t);
+void *wrap_thread_arg(struct wrapped_thread *t);
+int wrap_thread_stopfd(struct wrapped_thread *t);
+
+struct wrapped_rwlock *wrap_new_rwlock(void);
+void wrap_free_rwlock(struct wrapped_rwlock *rwlock);
+void wrap_rwlock_rdlock(struct wrapped_rwlock *rwlock);
+void wrap_rwlock_wrlock(struct wrapped_rwlock *rwlock);
+void wrap_rwlock_unlock(struct wrapped_rwlock *rwlock);
+
+int wrap_search_internal_get_entry(Slapi_DN *dn, char *filter, char **attrs,
+				   Slapi_Entry **ret_entry, void *caller_id);
+int wrap_get_call_level(void);
+int wrap_inc_call_level(void);
+int wrap_dec_call_level(void);
+
+#endif
diff --git a/daemons/ipa-slapi-plugins/ipa-modrdn/Makefile.am b/daemons/ipa-slapi-plugins/ipa-modrdn/Makefile.am
index 5d9db51..6549804 100644
--- a/daemons/ipa-slapi-plugins/ipa-modrdn/Makefile.am
+++ b/daemons/ipa-slapi-plugins/ipa-modrdn/Makefile.am
@@ -7,6 +7,8 @@ INCLUDES =							\
 	-I$(srcdir)						\
 	-I$(PLUGIN_COMMON_DIR)					\
 	-I/usr/include/dirsrv					\
+	-DUSE_NSPR_THREADS					\
+	-DUSE_PTHREAD_LOCKS					\
 	-DPREFIX=\""$(prefix)"\" 				\
 	-DBINDIR=\""$(bindir)"\"				\
 	-DLIBDIR=\""$(libdir)"\" 				\
@@ -23,6 +25,7 @@ plugin_LTLIBRARIES = 		\
 	$(NULL)
 
 libipa_modrdn_la_SOURCES = 	\
+	../common/wrap.c	\
 	ipa_modrdn.c		\
 	$(NULL)
 
diff --git a/daemons/ipa-slapi-plugins/ipa-modrdn/ipa_modrdn.c b/daemons/ipa-slapi-plugins/ipa-modrdn/ipa_modrdn.c
index 45a29a5..422d314 100644
--- a/daemons/ipa-slapi-plugins/ipa-modrdn/ipa_modrdn.c
+++ b/daemons/ipa-slapi-plugins/ipa-modrdn/ipa_modrdn.c
@@ -44,6 +44,7 @@
 #include "prclist.h"
 
 #include "util.h"
+#include "wrap.h"
 
 #define IPA_PLUGIN_NAME "ipa-modrdn-plugin"
 #define IPAMODRDN_PLUGIN_VERSION 0x00010000
@@ -88,7 +89,7 @@ struct configEntry {
 };
 
 static PRCList *ipamodrdn_global_config = NULL;
-static PRRWLock *g_ipamodrdn_cache_lock;
+static struct wrapped_rwlock *g_ipamodrdn_cache_lock;
 
 static void *_PluginID = NULL;
 static char *_PluginDN = NULL;
@@ -144,17 +145,17 @@ void ipamodrdn_dump_config_entry(struct configEntry *);
  */
 void ipamodrdn_read_lock(void)
 {
-    PR_RWLock_Rlock(g_ipamodrdn_cache_lock);
+    wrap_rwlock_rdlock(g_ipamodrdn_cache_lock);
 }
 
 void ipamodrdn_write_lock(void)
 {
-    PR_RWLock_Wlock(g_ipamodrdn_cache_lock);
+    wrap_rwlock_wrlock(g_ipamodrdn_cache_lock);
 }
 
 void ipamodrdn_unlock(void)
 {
-    PR_RWLock_Unlock(g_ipamodrdn_cache_lock);
+    wrap_rwlock_unlock(g_ipamodrdn_cache_lock);
 }
 
 /**
@@ -255,7 +256,7 @@ ipamodrdn_start(Slapi_PBlock * pb)
         goto done;
     }
 
-    g_ipamodrdn_cache_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "ipaModRDN");
+    g_ipamodrdn_cache_lock = wrap_new_rwlock();
 
     if (!g_ipamodrdn_cache_lock) {
         LOG_FATAL("lock creation failed\n");
diff --git a/daemons/ipa-slapi-plugins/ipa-uuid/Makefile.am b/daemons/ipa-slapi-plugins/ipa-uuid/Makefile.am
index 15e6ded..5daefe7 100644
--- a/daemons/ipa-slapi-plugins/ipa-uuid/Makefile.am
+++ b/daemons/ipa-slapi-plugins/ipa-uuid/Makefile.am
@@ -7,6 +7,8 @@ INCLUDES =							\
 	-I$(srcdir)						\
 	-I$(PLUGIN_COMMON_DIR)					\
 	-I/usr/include/dirsrv					\
+	-DUSE_NSPR_THREADS					\
+	-DUSE_PTHREAD_LOCKS					\
 	-DPREFIX=\""$(prefix)"\" 				\
 	-DBINDIR=\""$(bindir)"\"				\
 	-DLIBDIR=\""$(libdir)"\" 				\
@@ -23,6 +25,7 @@ plugin_LTLIBRARIES = 		\
 	$(NULL)
 
 libipa_uuid_la_SOURCES = 	\
+	../common/wrap.c	\
 	ipa_uuid.c		\
 	$(NULL)
 
diff --git a/daemons/ipa-slapi-plugins/ipa-uuid/ipa_uuid.c b/daemons/ipa-slapi-plugins/ipa-uuid/ipa_uuid.c
index 3249ce4..deb4be2 100644
--- a/daemons/ipa-slapi-plugins/ipa-uuid/ipa_uuid.c
+++ b/daemons/ipa-slapi-plugins/ipa-uuid/ipa_uuid.c
@@ -45,6 +45,7 @@
 #include "uuid/uuid.h"
 
 #include "util.h"
+#include "wrap.h"
 
 #define IPAUUID_STR_SIZE 36
 
@@ -94,7 +95,7 @@ struct configEntry {
 };
 
 static PRCList *ipauuid_global_config = NULL;
-static PRRWLock *g_ipauuid_cache_lock;
+static struct wrapped_rwlock *g_ipauuid_cache_lock;
 
 static void *_PluginID = NULL;
 static char *_PluginDN = NULL;
@@ -155,17 +156,17 @@ void ipauuid_dump_config_entry(struct configEntry *);
  */
 void ipauuid_read_lock(void)
 {
-    PR_RWLock_Rlock(g_ipauuid_cache_lock);
+    wrap_rwlock_rdlock(g_ipauuid_cache_lock);
 }
 
 void ipauuid_write_lock(void)
 {
-    PR_RWLock_Wlock(g_ipauuid_cache_lock);
+    wrap_rwlock_wrlock(g_ipauuid_cache_lock);
 }
 
 void ipauuid_unlock(void)
 {
-    PR_RWLock_Unlock(g_ipauuid_cache_lock);
+    wrap_rwlock_unlock(g_ipauuid_cache_lock);
 }
 
 /**
@@ -324,7 +325,7 @@ ipauuid_start(Slapi_PBlock * pb)
         goto done;
     }
 
-    g_ipauuid_cache_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "ipaUuid");
+    g_ipauuid_cache_lock = wrap_new_rwlock();
 
     if (!g_ipauuid_cache_lock) {
         LOG_FATAL("lock creation failed\n");
-- 
1.7.6

