aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSeebs <seebs@seebs.net>2016-12-12 14:23:27 -0600
committerSeebs <seebs@seebs.net>2016-12-12 14:23:27 -0600
commitbd45c2d860de783433bedd3832c0c2c574591a3b (patch)
tree9f3ed55aaf3f34f3ef3a5ba75886ee796997a52a
parentfc66205e2603a7e715c921d66314457e286786e0 (diff)
downloadpseudo-bd45c2d860de783433bedd3832c0c2c574591a3b.tar.gz
pseudo-bd45c2d860de783433bedd3832c0c2c574591a3b.tar.bz2
pseudo-bd45c2d860de783433bedd3832c0c2c574591a3b.zip
From: Rabin Vincent <rabinv@axis.com>
When tclsh forks it can create new threads in the child process, in a pthread_atfork() handler. Running this under pseudo results in a deadlock since the pseudo_lock() call in the new thread in the child process premanently believes that the mutex is already locked by another thread (which actually only existed in the parent process). The provided test cases reproduces this. Similar hangs can also been seen in other cases, such as when attempting to use vim's cscope support under pseudo. Fix it by reseting the mutex in a pthread_atfork() child function. Signed-off-by: Rabin Vincent <rabinv@axis.com> Signed-off-by: Seebs <seebs@seebs.net>
-rw-r--r--ChangeLog.txt1
-rw-r--r--pseudo_wrappers.c10
-rw-r--r--test/test-tclsh-fork.sh6
3 files changed, 17 insertions, 0 deletions
diff --git a/ChangeLog.txt b/ChangeLog.txt
index 77b1ff4..d2f93b0 100644
--- a/ChangeLog.txt
+++ b/ChangeLog.txt
@@ -4,6 +4,7 @@
* (seebs) contributed fixes for Python 3 support
* All of these from: Gaƫl PORTAY <gael.portay@savoirfairelinux.com>
* (seebs) import fix from Rabin Vincent for test case
+ * (seebs) import fix from Rabin Vincent for pthread mutexes
2016-11-23:
* (seebs) actually wait on server shutdown for pseudo -S [cmd]
diff --git a/pseudo_wrappers.c b/pseudo_wrappers.c
index 615ac9a..12bc541 100644
--- a/pseudo_wrappers.c
+++ b/pseudo_wrappers.c
@@ -98,8 +98,18 @@ extern int (*pseudo_real_lsetxattr)(const char *, const char *, const void *, si
extern int (*pseudo_real_fsetxattr)(int, const char *, const void *, size_t, int);
#endif
+static void libpseudo_atfork_child(void)
+{
+ pthread_mutex_init(&pseudo_mutex, NULL);
+ pseudo_mutex_recursion = 0;
+ pseudo_mutex_holder = 0;
+}
+
static void
_libpseudo_init(void) {
+ if (!_libpseudo_initted)
+ pthread_atfork(NULL, NULL, libpseudo_atfork_child);
+
pseudo_getlock();
pseudo_antimagic();
_libpseudo_initted = 1;
diff --git a/test/test-tclsh-fork.sh b/test/test-tclsh-fork.sh
new file mode 100644
index 0000000..e9dec66
--- /dev/null
+++ b/test/test-tclsh-fork.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+# Check that tclsh doesn't hang. Note that the timeout is not needed to
+# reproduce the hang in tclsh, it's only there to ensure that this test script
+# doesn't hang in case of a failing test.
+timeout 2s bash -c "echo 'open {|true} r+' | tclsh"