summaryrefslogtreecommitdiffstats
path: root/macosx/tclMacOSXNotify.c
diff options
context:
space:
mode:
authordas <das>2006-07-20 06:21:41 (GMT)
committerdas <das>2006-07-20 06:21:41 (GMT)
commitbed796e06772e11d807d9394771ef16cf766b2cd (patch)
tree87aa93a682398fe04c4b734c7ea00049bf6fba02 /macosx/tclMacOSXNotify.c
parent8cb60c333409bcc5ab4d0388905e7840b41422b2 (diff)
downloadtcl-bed796e06772e11d807d9394771ef16cf766b2cd.zip
tcl-bed796e06772e11d807d9394771ef16cf766b2cd.tar.gz
tcl-bed796e06772e11d807d9394771ef16cf766b2cd.tar.bz2
* macosx/tclMacOSXNotify.c (Tcl_InitNotifier, Tcl_WaitForEvent): create
notifier thread lazily upon first call to Tcl_WaitForEvent() rather than in Tcl_InitNotifier(). Allows calling exeve() in processes where the event loop has not yet been run (Darwin's execve() fails in processes with more than one thread), in particular allows embedders to call fork() followed by execve(), previously the pthread_atfork() child handler's call to Tcl_InitNotifier() would immediately recreate the notifier thread in the child after a fork. * macosx/tclMacOSXNotify.c (Tcl_InitNotifier): add support for * unix/tclUnixFCmd.c (DoRenameFile, CopyFileAtts): weakly importing * unix/tclUnixInit.c (TclpSetInitialEncodings): symbols not available on OSX 10.2 or 10.3, enables binaires built on later OSX versions to run on earlier ones. * macosx/README: document how to enable weak-linking; cleanup. * unix/tclUnixPort.h: add support for weak-linking; conditionalize AvailabilityMacros.h inclusion; only disable realpath on 10.2 or earlier when threads are enabled. * unix/tclLoadDyld.c (TclpLoadMemoryGetBuffer): change runtime Darwin * unix/tclUnixInit.c (TclpInitPlatform): release check to use global initialized once. * unix/tclUnixFCmd.c (DoRenameFile, TclpObjNormalizePath): add runtime Darwin release check to determine if realpath is threadsafe. * unix/configure.in: add check on Darwin for compiler support of weak * unix/tcl.m4: import and for AvailabilityMacros.h header; move Darwin specific checks & defines that are only relevant to the tcl build out of tcl.m4; restrict framework option to Darwin; cleanup quoting. * unix/configure: autoconf-2.13 * unix/tclLoadDyld.c (TclpLoadMemory): * unix/tclUnixPipe.c (TclpCreateProcess): fix signed-with-unsigned comparison and other warnings from gcc4 -Wextra.
Diffstat (limited to 'macosx/tclMacOSXNotify.c')
-rw-r--r--macosx/tclMacOSXNotify.c138
1 files changed, 104 insertions, 34 deletions
diff --git a/macosx/tclMacOSXNotify.c b/macosx/tclMacOSXNotify.c
index bdda1ce..f9cd046 100644
--- a/macosx/tclMacOSXNotify.c
+++ b/macosx/tclMacOSXNotify.c
@@ -13,7 +13,7 @@
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclMacOSXNotify.c,v 1.1.2.6 2006/05/27 05:23:04 das Exp $
+ * RCS: @(#) $Id: tclMacOSXNotify.c,v 1.1.2.7 2006/07/20 06:21:42 das Exp $
*/
#include "tclInt.h"
@@ -164,6 +164,32 @@ static int receivePipe = -1; /* Output end of triggerPipe */
#include <libkern/OSAtomic.h>
+#if defined(HAVE_WEAK_IMPORT) && MAC_OS_X_VERSION_MIN_REQUIRED < 1040
+/*
+ * Support for weakly importing spinlock API.
+ */
+#define WEAK_IMPORT_SPINLOCKLOCK
+extern void OSSpinLockLock(OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE;
+extern void OSSpinLockUnlock(OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE;
+extern void _spin_lock(OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE;
+extern void _spin_unlock(OSSpinLock *lock) WEAK_IMPORT_ATTRIBUTE;
+static void (* lockLock)(OSSpinLock *lock) = NULL;
+static void (* lockUnlock)(OSSpinLock *lock) = NULL;
+static pthread_once_t spinLockLockInitControl = PTHREAD_ONCE_INIT;
+static void SpinLockLockInit(void) {
+ lockLock = OSSpinLockLock != NULL ? OSSpinLockLock : _spin_lock;
+ lockUnlock = OSSpinLockUnlock != NULL ? OSSpinLockUnlock : _spin_unlock;
+ if (lockLock == NULL || lockUnlock == NULL) {
+ Tcl_Panic("SpinLockLockInit: no spinlock API available.");
+ }
+}
+#define SpinLockLock(p) lockLock(p)
+#define SpinLockUnlock(p) lockUnlock(p)
+#else
+#define SpinLockLock(p) OSSpinLockLock(p)
+#define SpinLockUnlock(p) OSSpinLockUnlock(p)
+#endif /* HAVE_WEAK_IMPORT */
+
#else
/*
* Otherwise, use commpage spinlock SPI directly.
@@ -172,8 +198,8 @@ static int receivePipe = -1; /* Output end of triggerPipe */
typedef uint32_t OSSpinLock;
extern void _spin_lock(OSSpinLock *lock);
extern void _spin_unlock(OSSpinLock *lock);
-#define OSSpinLockLock(p) _spin_lock(p)
-#define OSSpinLockUnlock(p) _spin_unlock(p)
+#define SpinLockLock(p) _spin_lock(p)
+#define SpinLockUnlock(p) _spin_unlock(p)
#endif /* HAVE_LIBKERN_OSATOMIC_H && HAVE_OSSPINLOCKLOCK */
@@ -188,10 +214,10 @@ static OSSpinLock notifierLock = 0;
* Macros abstracting notifier locking/unlocking
*/
-#define LOCK_NOTIFIER_INIT OSSpinLockLock(&notifierInitLock)
-#define UNLOCK_NOTIFIER_INIT OSSpinLockUnlock(&notifierInitLock)
-#define LOCK_NOTIFIER OSSpinLockLock(&notifierLock)
-#define UNLOCK_NOTIFIER OSSpinLockUnlock(&notifierLock)
+#define LOCK_NOTIFIER_INIT SpinLockLock(&notifierInitLock)
+#define UNLOCK_NOTIFIER_INIT SpinLockUnlock(&notifierInitLock)
+#define LOCK_NOTIFIER SpinLockLock(&notifierLock)
+#define UNLOCK_NOTIFIER SpinLockUnlock(&notifierLock)
/*
* The pollState bits
@@ -223,7 +249,13 @@ static int atForkInit = 0;
static void AtForkPrepare(void);
static void AtForkParent(void);
static void AtForkChild(void);
-#endif
+#if defined(HAVE_WEAK_IMPORT) && MAC_OS_X_VERSION_MIN_REQUIRED < 1040
+/* Support for weakly importing pthread_atfork. */
+#define WEAK_IMPORT_PTHREAD_ATFORK
+extern int pthread_atfork(void (*prepare)(void), void (*parent)(void),
+ void (*child)(void)) WEAK_IMPORT_ATTRIBUTE;
+#endif /* HAVE_WEAK_IMPORT */
+#endif /* HAVE_PTHREAD_ATFORK */
/*
*----------------------------------------------------------------------
@@ -248,8 +280,17 @@ Tcl_InitNotifier(void)
tsdPtr->eventReady = 0;
+#ifdef WEAK_IMPORT_SPINLOCKLOCK
+ /*
+ * Initialize support for weakly imported spinlock API.
+ */
+ if (pthread_once(&spinLockLockInitControl, SpinLockLockInit)) {
+ Tcl_Panic("Tcl_InitNotifier: pthread_once failed.");
+ }
+#endif
+
/*
- * Initialize CFRunLoopSource and add it to CFRunLoop of this thread
+ * Initialize CFRunLoopSource and add it to CFRunLoop of this thread.
*/
if (!tsdPtr->runLoop) {
@@ -268,28 +309,31 @@ Tcl_InitNotifier(void)
tsdPtr->runLoop = runLoop;
}
- /*
- * Initialize trigger pipe and start the Notifier thread if necessary.
- */
-
LOCK_NOTIFIER_INIT;
#ifdef HAVE_PTHREAD_ATFORK
/*
- * Install pthread_atfork handlers to reinstall the notifier thread in the
+ * Install pthread_atfork handlers to reinitialize the notifier in the
* child of a fork.
*/
- if (!atForkInit) {
+ if (
+#ifdef WEAK_IMPORT_PTHREAD_ATFORK
+ pthread_atfork != NULL &&
+#endif
+ !atForkInit) {
int result = pthread_atfork(AtForkPrepare, AtForkParent, AtForkChild);
if (result) {
- Tcl_Panic("Tcl_InitNotifier: pthread_atfork failed");
+ Tcl_Panic("Tcl_InitNotifier: pthread_atfork failed.");
}
atForkInit = 1;
}
#endif
if (notifierCount == 0) {
- int fds[2], status, result;
- pthread_attr_t attr;
+ int fds[2], status;
+
+ /*
+ * Initialize trigger pipe.
+ */
if (pipe(fds) != 0) {
Tcl_Panic("Tcl_InitNotifier: could not create trigger pipe.");
@@ -309,16 +353,13 @@ Tcl_InitNotifier(void)
receivePipe = fds[0];
triggerPipe = fds[1];
- pthread_attr_init(&attr);
- pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
- pthread_attr_setstacksize(&attr, 60 * 1024);
- result = pthread_create(&notifierThread, &attr,
- (void * (*)(void *))NotifierThreadProc, NULL);
- pthread_attr_destroy(&attr);
- if (result) {
- Tcl_Panic("Tcl_InitNotifier: unable to start notifier thread.");
- }
+ /*
+ * Create notifier thread lazily in Tcl_WaitForEvent() to avoid
+ * interfering with fork() followed immediately by execve()
+ * (cannot execve() when more than one thread is present).
+ */
+
+ notifierThread = 0;
}
notifierCount++;
UNLOCK_NOTIFIER_INIT;
@@ -379,9 +420,12 @@ Tcl_FinalizeNotifier(clientData)
write(triggerPipe, "q", 1);
close(triggerPipe);
- result = pthread_join(notifierThread, NULL);
- if (result) {
- Tcl_Panic("Tcl_FinalizeNotifier: unable to join notifier thread.");
+ if (notifierThread) {
+ result = pthread_join(notifierThread, NULL);
+ if (result) {
+ Tcl_Panic("Tcl_FinalizeNotifier: unable to join notifier thread.");
+ }
+ notifierThread = 0;
}
close(receivePipe);
@@ -764,6 +808,28 @@ Tcl_WaitForEvent(timePtr)
}
/*
+ * Start notifier thread if necessary.
+ */
+
+ LOCK_NOTIFIER_INIT;
+ if (!notifierThread) {
+ int result;
+ pthread_attr_t attr;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+ pthread_attr_setstacksize(&attr, 60 * 1024);
+ result = pthread_create(&notifierThread, &attr,
+ (void * (*)(void *))NotifierThreadProc, NULL);
+ pthread_attr_destroy(&attr);
+ if (result || !notifierThread) {
+ Tcl_Panic("Tcl_WaitForEvent: unable to start notifier thread.");
+ }
+ }
+ UNLOCK_NOTIFIER_INIT;
+
+ /*
* Place this thread on the list of interested threads, signal the
* notifier thread, and wait for a response or a timeout.
*/
@@ -1138,9 +1204,13 @@ AtForkChild(void)
}
if (notifierCount > 0) {
notifierCount = 0;
- /* Note that Tcl_FinalizeNotifier does not use its clientData
- * parameter, so discard return value of Tcl_InitNotifier here and
- * leave stale clientData in tclNotify.c's ThreadSpecificData.
+ /*
+ * Assume that the return value of Tcl_InitNotifier() in the child
+ * will be identical to the one stored as clientData in tclNotify.c's
+ * ThreadSpecificData by the parent's TclInitNotifier(), so discard
+ * the return value here. This assumption may require the fork() to
+ * be executed in the main thread of the parent, otherwise
+ * Tcl_AlertNotifier() may break in the child.
*/
Tcl_InitNotifier();
}