diff options
Diffstat (limited to 'macosx')
-rw-r--r-- | macosx/README | 34 | ||||
-rw-r--r-- | macosx/tclMacOSXNotify.c | 138 |
2 files changed, 127 insertions, 45 deletions
diff --git a/macosx/README b/macosx/README index 46469cb..2475ea2 100644 --- a/macosx/README +++ b/macosx/README @@ -1,7 +1,7 @@ Tcl Mac OS X README ----------------- -RCS: @(#) $Id: README,v 1.1.2.4 2005/11/27 02:34:41 das Exp $ +RCS: @(#) $Id: README,v 1.1.2.5 2006/07/20 06:21:41 das Exp $ This is the README file for the Mac OS X/Darwin version of Tcl. @@ -36,9 +36,12 @@ Mac OS X specific bugs should usually be assigned to 'das' or 'wolfsuit'. - At a minimum, Mac OS X 10.1 is required to run Tcl, but OS X 10.3 or higher is recommended (certain [file] operations behave incorrectly on earlier releases). -- Tcl built on Mac OS X 10.x will not run on 10.y for y < x, on the other hand -Tcl built on 10.y will run on 10.x for y < x (but without any of the fixes and -optimizations that would be available in a binary built on 10.x). +- Unless weak-linking is used, Tcl built on Mac OS X 10.x will not run on 10.y +with y < x; on the other hand Tcl built on 10.y will always run on 10.x with +y <= x (but without any of the fixes and optimizations that would be available +in a binary built on 10.x). +Weak-linking is available on OS X 10.2 or later, it additionally allows Tcl +built on 10.x to run on any 10.y with x > y >= z (for a chosen z >= 2). - Tcl extensions can be installed in any of: $HOME/Library/Tcl /Library/Tcl /Network/Library/Tcl /System/Library/Tcl @@ -79,10 +82,9 @@ http://connect.apple.com (after you register for free ADC membership). (see below for details), but can also be built with the standard unix configure and make buildsystem in tcl/unix as on any other unix platform (indeed, the Makefile is just a wrapper around the unix buildsystem). -The Mac OS X specifc configure flags are --enable-framework and +The Mac OS X specific configure flags are --enable-framework and --disable-corefoundation (which disables CF and notably reverts to the standard -select based notifier, you will only need this if your require use of naked fork -(i.e. not followed by execve) in an unthreaded core). +select based notifier). - It is also possible to build with Apple's IDE via the tcl/macosx/Tcl.pbproj project, this simply calls through to the tcl/macosx/GNUMakefile. @@ -91,12 +93,22 @@ project, this simply calls through to the tcl/macosx/GNUMakefile. export CFLAGS="-arch ppc -arch ppc64 -arch i386 \ -isysroot /Developer/SDKs/MacOSX10.4u.sdk -mmacosx-version-min=10.4" This requires Mac OS X 10.4 and Xcode 2.2 (_not_ Xcode 2.1) and will work on any -of the architectures (on i386 DTKs, the -isysroot is not required). Note that it -is not possible to configure correctly if the current architecture is not +of the architectures (on intel Macs, the -isysroot is not required). Note that +it is not possible to configure correctly if the current architecture is not present in CFLAGS (i.e. -arch `arch` must always be there). Universal builds of Tcl TEA extensions are also possible with CFLAGS set as above, they will be [load]able by universal as well as thin binaries of Tcl. +- To enable weak-linking, set the MACOSX_DEPLOYMENT_TARGET environment variable +to the minimal OS version (>= 10.2) the binaries should be able to run on, e.g: + export MACOSX_DEPLOYMENT_TARGET=10.2 +This requires Mac OS X 10.2 and gcc 3.1; if you have gcc 4 or later you can set +CFLAGS instead: + export CFLAGS="-mmacosx-version-min=10.2" +The Tcl.xcodeproj is setup to produce binaires that can run on 10.2 or later, +except for the 'ReleaseUniversal'configuration, where they require 10.4. +Support for weak-linking was added to the code for 8.4.14/8.5a5. + Detailed Instructions for building with macosx/Makefile ------------------------------------------------------- @@ -127,8 +139,8 @@ instead by passing an INSTALL_ROOT argument to make: - The default Makefile targets will build _both_ debug and optimized versions of the Tcl framework with the standard convention of naming the debug library Tcl.framework/Tcl_debug. -This allows you to dynamically link to the debug libraries at runtime by setting - setenv DYLD_IMAGE_SUFFIX _debug +This allows switching to the debug libraries at runtime by setting + export DYLD_IMAGE_SUFFIX=_debug (c.f. man dyld for more details) If you only want to build and install the debug or optimized build, use the 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(¬ifierInitLock) -#define UNLOCK_NOTIFIER_INIT OSSpinLockUnlock(¬ifierInitLock) -#define LOCK_NOTIFIER OSSpinLockLock(¬ifierLock) -#define UNLOCK_NOTIFIER OSSpinLockUnlock(¬ifierLock) +#define LOCK_NOTIFIER_INIT SpinLockLock(¬ifierInitLock) +#define UNLOCK_NOTIFIER_INIT SpinLockUnlock(¬ifierInitLock) +#define LOCK_NOTIFIER SpinLockLock(¬ifierLock) +#define UNLOCK_NOTIFIER SpinLockUnlock(¬ifierLock) /* * 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(¬ifierThread, &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(¬ifierThread, &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(); } |