From 682791cc06ff9a97bb834ec6e43bbef12687d80a Mon Sep 17 00:00:00 2001 From: das Date: Sat, 14 May 2005 20:46:43 +0000 Subject: * generic/tclInt.decls: * generic/tclTest.c: * generic/tclUtil.c: * win/tclWin32Dll.c: fixed link error due to direct access by tclTest.c to the MODULE_SCOPE tclPlatform global: renamed existing TclWinGetPlatform() accessor to TclGetPlatform() and moved it to generic code so that it can be used by on all platforms where MODULE_SCOPE is enforced. * macosx/tclMacOSXBundle.c: * unix/tclUnixInit.c: * unix/tcl.m4 (Darwin): made use of CoreFoundation API configurable and added test of CoreFoundation availablility to allow building on ppc64, replaced HAVE_CFBUNDLE by HAVE_COREFOUNDATION; test for availability of Tiger or later OSSpinLockLock API. * unix/tclUnixNotfy.c: * unix/Makefile.in: * macosx/tclMacOSXNotify.c (new file): when CoreFoundation is available, use new CFRunLoop based notifier: allows easy integration with other event loops on Mac OS X, in particular the TkAqua Carbon event loop is now integrated via a standard tcl event source (instead of TkAqua upon loading having to finalize the exsting notifier and replace it with its custom version). [Patch 1202052] * tests/unixNotfy.test: don't run unthreaded tests on Darwin since notifier may be using threads even in unthreaded core. * unix/tclUnixPort.h: * unix/tcl.m4 (Darwin): test for thread-unsafe realpath durning configure, as Darwin 7 and later realpath is threadsafe. * macosx/Makefile: enable configure caching. * unix/configure.in: wrap tclConfig.h header in #ifndef _TCLCONFIG so that it can be included more than once without warnings from gcc4.0 (as happens e.g. when including both tclInt.h and tclPort.h) * macosx/tclMacOSXBundle.c: * unix/tclUnixChan.c: * unix/tclLoadDyld.c: * unix/tclUnixInit.c: fixed gcc 4.0 warnings. * unix/configure: autoconf-2.59 * unix/tclConfig.h.in: autoheader-2.59 * generic/tclIntDecls.h: * generic/tclIntPlatDecls.h: * generic/tclStubInit.c: make genstubs --- ChangeLog | 52 +++ generic/tclInt.decls | 13 +- generic/tclIntDecls.h | 12 +- generic/tclIntPlatDecls.h | 15 +- generic/tclStubInit.c | 5 +- generic/tclTest.c | 14 +- generic/tclUtil.c | 25 +- macosx/Makefile | 4 +- macosx/tclMacOSXBundle.c | 9 +- macosx/tclMacOSXNotify.c | 1048 +++++++++++++++++++++++++++++++++++++++++++++ tests/unixNotfy.test | 6 +- unix/Makefile.in | 13 +- unix/configure | 439 ++++++++++++++++++- unix/configure.in | 8 +- unix/tcl.m4 | 39 +- unix/tclConfig.h.in | 34 +- unix/tclLoadDyld.c | 12 +- unix/tclUnixChan.c | 20 +- unix/tclUnixInit.c | 24 +- unix/tclUnixNotfy.c | 6 +- unix/tclUnixPort.h | 9 +- win/tclWin32Dll.c | 25 +- 22 files changed, 1713 insertions(+), 119 deletions(-) create mode 100644 macosx/tclMacOSXNotify.c diff --git a/ChangeLog b/ChangeLog index 2c41c95..2838972 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,55 @@ +2005-05-14 Daniel Steffen + + * generic/tclInt.decls: + * generic/tclTest.c: + * generic/tclUtil.c: + * win/tclWin32Dll.c: fixed link error due to direct access by + tclTest.c to the MODULE_SCOPE tclPlatform global: renamed existing + TclWinGetPlatform() accessor to TclGetPlatform() and moved it to + generic code so that it can be used by on all platforms where + MODULE_SCOPE is enforced. + + * macosx/tclMacOSXBundle.c: + * unix/tclUnixInit.c: + * unix/tcl.m4 (Darwin): made use of CoreFoundation API configurable + and added test of CoreFoundation availablility to allow building on + ppc64, replaced HAVE_CFBUNDLE by HAVE_COREFOUNDATION; test for + availability of Tiger or later OSSpinLockLock API. + + * unix/tclUnixNotfy.c: + * unix/Makefile.in: + * macosx/tclMacOSXNotify.c (new file): when CoreFoundation is + available, use new CFRunLoop based notifier: allows easy integration + with other event loops on Mac OS X, in particular the TkAqua Carbon + event loop is now integrated via a standard tcl event source (instead + of TkAqua upon loading having to finalize the exsting notifier and + replace it with its custom version). [Patch 1202052] + + * tests/unixNotfy.test: don't run unthreaded tests on Darwin + since notifier may be using threads even in unthreaded core. + + * unix/tclUnixPort.h: + * unix/tcl.m4 (Darwin): test for thread-unsafe realpath durning + configure, as Darwin 7 and later realpath is threadsafe. + + * macosx/Makefile: enable configure caching. + + * unix/configure.in: wrap tclConfig.h header in #ifndef _TCLCONFIG so + that it can be included more than once without warnings from gcc4.0 + (as happens e.g. when including both tclInt.h and tclPort.h) + + * macosx/tclMacOSXBundle.c: + * unix/tclUnixChan.c: + * unix/tclLoadDyld.c: + * unix/tclUnixInit.c: fixed gcc 4.0 warnings. + + * unix/configure: autoconf-2.59 + * unix/tclConfig.h.in: autoheader-2.59 + + * generic/tclIntDecls.h: + * generic/tclIntPlatDecls.h: + * generic/tclStubInit.c: make genstubs + 2005-05-13 Kevin Kenny * win/tclWin32Dll.c: Further rework of the SEH logic. All diff --git a/generic/tclInt.decls b/generic/tclInt.decls index c457741..f4f29c7 100644 --- a/generic/tclInt.decls +++ b/generic/tclInt.decls @@ -12,7 +12,7 @@ # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # -# RCS: @(#) $Id: tclInt.decls,v 1.88 2005/05/10 18:34:41 kennykb Exp $ +# RCS: @(#) $Id: tclInt.decls,v 1.89 2005/05/14 20:46:44 das Exp $ library tcl @@ -891,6 +891,10 @@ declare 222 generic { declare 223 generic { int TclBN_mp_read_radix(mp_int *a, const char *str, int radix) } +# for use in tclTest.c +declare 224 generic { + TclPlatformType *TclGetPlatform(void) +} ############################################################################## @@ -993,9 +997,10 @@ declare 23 win { declare 24 win { char *TclWinNoBackslash(char *path) } -declare 25 win { - TclPlatformType *TclWinGetPlatform(void) -} +# replaced by generic TclGetPlatform +#declare 25 win { +# TclPlatformType *TclWinGetPlatform(void) +#} declare 26 win { void TclWinSetInterfaces(int wide) } diff --git a/generic/tclIntDecls.h b/generic/tclIntDecls.h index 7364d0f..3e5827e 100644 --- a/generic/tclIntDecls.h +++ b/generic/tclIntDecls.h @@ -11,7 +11,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclIntDecls.h,v 1.79 2005/05/10 18:34:42 kennykb Exp $ + * RCS: @(#) $Id: tclIntDecls.h,v 1.80 2005/05/14 20:46:45 das Exp $ */ #ifndef _TCLINTDECLS @@ -1156,6 +1156,11 @@ EXTERN int TclBN_mp_init _ANSI_ARGS_((mp_int * a)); EXTERN int TclBN_mp_read_radix _ANSI_ARGS_((mp_int * a, const char * str, int radix)); #endif +#ifndef TclGetPlatform_TCL_DECLARED +#define TclGetPlatform_TCL_DECLARED +/* 224 */ +EXTERN TclPlatformType * TclGetPlatform _ANSI_ARGS_((void)); +#endif typedef struct TclIntStubs { int magic; @@ -1400,6 +1405,7 @@ typedef struct TclIntStubs { void (*tclBN_mp_clear) _ANSI_ARGS_((mp_int * a)); /* 221 */ int (*tclBN_mp_init) _ANSI_ARGS_((mp_int * a)); /* 222 */ int (*tclBN_mp_read_radix) _ANSI_ARGS_((mp_int * a, const char * str, int radix)); /* 223 */ + TclPlatformType * (*tclGetPlatform) _ANSI_ARGS_((void)); /* 224 */ } TclIntStubs; #ifdef __cplusplus @@ -2174,6 +2180,10 @@ extern TclIntStubs *tclIntStubsPtr; #define TclBN_mp_read_radix \ (tclIntStubsPtr->tclBN_mp_read_radix) /* 223 */ #endif +#ifndef TclGetPlatform +#define TclGetPlatform \ + (tclIntStubsPtr->tclGetPlatform) /* 224 */ +#endif #endif /* defined(USE_TCL_STUBS) && !defined(USE_TCL_STUB_PROCS) */ diff --git a/generic/tclIntPlatDecls.h b/generic/tclIntPlatDecls.h index a3c900c..e9c1184 100644 --- a/generic/tclIntPlatDecls.h +++ b/generic/tclIntPlatDecls.h @@ -9,7 +9,7 @@ * Copyright (c) 1998-1999 by Scriptics Corporation. * All rights reserved. * - * RCS: @(#) $Id: tclIntPlatDecls.h,v 1.26 2004/11/03 19:13:40 davygrvy Exp $ + * RCS: @(#) $Id: tclIntPlatDecls.h,v 1.27 2005/05/14 20:46:45 das Exp $ */ #ifndef _TCLINTPLATDECLS @@ -249,11 +249,7 @@ EXTERN char * TclpGetTZName _ANSI_ARGS_((int isdst)); /* 24 */ EXTERN char * TclWinNoBackslash _ANSI_ARGS_((char * path)); #endif -#ifndef TclWinGetPlatform_TCL_DECLARED -#define TclWinGetPlatform_TCL_DECLARED -/* 25 */ -EXTERN TclPlatformType * TclWinGetPlatform _ANSI_ARGS_((void)); -#endif +/* Slot 25 is reserved */ #ifndef TclWinSetInterfaces_TCL_DECLARED #define TclWinSetInterfaces_TCL_DECLARED /* 26 */ @@ -348,7 +344,7 @@ typedef struct TclIntPlatStubs { TclFile (*tclpCreateTempFile) _ANSI_ARGS_((CONST char * contents)); /* 22 */ char * (*tclpGetTZName) _ANSI_ARGS_((int isdst)); /* 23 */ char * (*tclWinNoBackslash) _ANSI_ARGS_((char * path)); /* 24 */ - TclPlatformType * (*tclWinGetPlatform) _ANSI_ARGS_((void)); /* 25 */ + void *reserved25; void (*tclWinSetInterfaces) _ANSI_ARGS_((int wide)); /* 26 */ void (*tclWinFlushDirtyChannels) _ANSI_ARGS_((void)); /* 27 */ void (*tclWinResetInterfaces) _ANSI_ARGS_((void)); /* 28 */ @@ -520,10 +516,7 @@ extern TclIntPlatStubs *tclIntPlatStubsPtr; #define TclWinNoBackslash \ (tclIntPlatStubsPtr->tclWinNoBackslash) /* 24 */ #endif -#ifndef TclWinGetPlatform -#define TclWinGetPlatform \ - (tclIntPlatStubsPtr->tclWinGetPlatform) /* 25 */ -#endif +/* Slot 25 is reserved */ #ifndef TclWinSetInterfaces #define TclWinSetInterfaces \ (tclIntPlatStubsPtr->tclWinSetInterfaces) /* 26 */ diff --git a/generic/tclStubInit.c b/generic/tclStubInit.c index 8240c9c..ebe7d40 100644 --- a/generic/tclStubInit.c +++ b/generic/tclStubInit.c @@ -8,7 +8,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclStubInit.c,v 1.116 2005/05/10 18:34:49 kennykb Exp $ + * RCS: @(#) $Id: tclStubInit.c,v 1.117 2005/05/14 20:46:45 das Exp $ */ #include "tclInt.h" @@ -308,6 +308,7 @@ TclIntStubs tclIntStubs = { TclBN_mp_clear, /* 221 */ TclBN_mp_init, /* 222 */ TclBN_mp_read_radix, /* 223 */ + TclGetPlatform, /* 224 */ }; TclIntPlatStubs tclIntPlatStubs = { @@ -356,7 +357,7 @@ TclIntPlatStubs tclIntPlatStubs = { TclpCreateTempFile, /* 22 */ TclpGetTZName, /* 23 */ TclWinNoBackslash, /* 24 */ - TclWinGetPlatform, /* 25 */ + NULL, /* 25 */ TclWinSetInterfaces, /* 26 */ TclWinFlushDirtyChannels, /* 27 */ TclWinResetInterfaces, /* 28 */ diff --git a/generic/tclTest.c b/generic/tclTest.c index 68b8963..22859a7 100644 --- a/generic/tclTest.c +++ b/generic/tclTest.c @@ -14,7 +14,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclTest.c,v 1.89 2005/05/10 18:34:50 kennykb Exp $ + * RCS: @(#) $Id: tclTest.c,v 1.90 2005/05/14 20:46:46 das Exp $ */ #define TCL_TEST @@ -2457,11 +2457,7 @@ TestgetplatformCmd(clientData, interp, argc, argv) static CONST char *platformStrings[] = { "unix", "mac", "windows" }; TclPlatformType *platform; -#ifdef __WIN32__ - platform = TclWinGetPlatform(); -#else - platform = &tclPlatform; -#endif + platform = TclGetPlatform(); if (argc != 1) { Tcl_AppendResult(interp, "wrong # arguments: should be \"", argv[0], @@ -3697,11 +3693,7 @@ TestsetplatformCmd(clientData, interp, argc, argv) size_t length; TclPlatformType *platform; -#ifdef __WIN32__ - platform = TclWinGetPlatform(); -#else - platform = &tclPlatform; -#endif + platform = TclGetPlatform(); if (argc != 2) { Tcl_AppendResult(interp, "wrong # arguments: should be \"", argv[0], diff --git a/generic/tclUtil.c b/generic/tclUtil.c index 278380d..2c787c2 100644 --- a/generic/tclUtil.c +++ b/generic/tclUtil.c @@ -11,7 +11,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclUtil.c,v 1.59 2005/05/10 18:34:52 kennykb Exp $ + * RCS: @(#) $Id: tclUtil.c,v 1.60 2005/05/14 20:46:46 das Exp $ */ #include "tclInt.h" @@ -2993,3 +2993,26 @@ TclpGetTime(timePtr) { Tcl_GetTime(timePtr); } + +/* + *---------------------------------------------------------------------- + * + * TclGetPlatform -- + * + * This is a kludge that allows the test library to get access + * the internal tclPlatform variable. + * + * Results: + * Returns a pointer to the tclPlatform variable. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +TclPlatformType * +TclGetPlatform() +{ + return &tclPlatform; +} diff --git a/macosx/Makefile b/macosx/Makefile index bc469d8..5914122 100644 --- a/macosx/Makefile +++ b/macosx/Makefile @@ -3,7 +3,7 @@ # Makefile to build Tcl on Mac OS X packaged as a Framework # uses standard unix build system in tcl/unix # -# RCS: @(#) $Id: Makefile,v 1.18 2004/11/19 06:28:29 das Exp $ +# RCS: @(#) $Id: Makefile,v 1.19 2005/05/14 20:46:46 das Exp $ # ######################################################################################################## @@ -124,7 +124,7 @@ export CPPROG := cp -p ${PROJECT}: install-${PROJECT} ${OBJ_DIR}/Makefile: ${UNIX_DIR}/Makefile.in ${UNIX_DIR}/configure - mkdir -p ${OBJ_DIR} && cd ${OBJ_DIR} && ${UNIX_DIR}/configure \ + mkdir -p ${OBJ_DIR} && cd ${OBJ_DIR} && ${UNIX_DIR}/configure -C \ --prefix=${PREFIX} --bindir=${BINDIR} --libdir=${LIBDIR} \ --includedir=${INCLUDEDIR} --mandir=${MANDIR} --enable-threads \ --enable-framework ${CONFIGURE_ARGS} ${EXTRA_CONFIGURE_ARGS} diff --git a/macosx/tclMacOSXBundle.c b/macosx/tclMacOSXBundle.c index 8d50494..75b871c 100644 --- a/macosx/tclMacOSXBundle.c +++ b/macosx/tclMacOSXBundle.c @@ -51,8 +51,11 @@ * license. */ +#ifdef HAVE_COREFOUNDATION #include #include +#endif /* HAVE_COREFOUNDATION */ + #include "tcl.h" /* @@ -119,6 +122,7 @@ Tcl_MacOSXOpenVersionedBundleResources( int maxPathLen, char *libraryPath) { +#ifdef HAVE_COREFOUNDATION CFBundleRef bundleRef; CFStringRef bundleNameRef; CFURLRef libURL; @@ -199,7 +203,7 @@ Tcl_MacOSXOpenVersionedBundleResources( */ CFURLGetFileSystemRepresentation(libURL, TRUE, - libraryPath, maxPathLen); + (unsigned char*) libraryPath, maxPathLen); CFRelease(libURL); } } @@ -209,4 +213,7 @@ Tcl_MacOSXOpenVersionedBundleResources( } else { return TCL_ERROR; } +#else /* HAVE_COREFOUNDATION */ + return TCL_ERROR; +#endif /* HAVE_COREFOUNDATION */ } diff --git a/macosx/tclMacOSXNotify.c b/macosx/tclMacOSXNotify.c new file mode 100644 index 0000000..9de3c98 --- /dev/null +++ b/macosx/tclMacOSXNotify.c @@ -0,0 +1,1048 @@ +/* + * tclMacOSXNotify.c -- + * + * This file contains the implementation of a merged + * CFRunLoop/select-based notifier, which is the lowest-level part + * of the Tcl event loop. This file works together with + * generic/tclNotify.c. + * + * Copyright (c) 1995-1997 Sun Microsystems, Inc. + * Copyright 2001, Apple Computer, Inc. + * Copyright 2005, Tcl Core Team. + * + * 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 2005/05/14 20:46:46 das Exp $ + */ + +#ifdef HAVE_COREFOUNDATION /* Traditional unix select-based notifier + * is in tclUnixNotfy.c */ +#include "tclInt.h" +#include +#include + +extern TclStubs tclStubs; +extern Tcl_NotifierProcs tclOriginalNotifier; + +/* + * This structure is used to keep track of the notifier info for a + * a registered file. + */ + +typedef struct FileHandler { + int fd; + int mask; /* Mask of desired events: TCL_READABLE, + * etc. */ + int readyMask; /* Mask of events that have been seen since the + * last time file handlers were invoked for + * this file. */ + Tcl_FileProc *proc; /* Procedure to call, in the style of + * Tcl_CreateFileHandler. */ + ClientData clientData; /* Argument to pass to proc. */ + struct FileHandler *nextPtr;/* Next in list of all files we care about. */ +} FileHandler; + +/* + * The following structure is what is added to the Tcl event queue when + * file handlers are ready to fire. + */ + +typedef struct FileHandlerEvent { + Tcl_Event header; /* Information that is standard for + * all events. */ + int fd; /* File descriptor that is ready. Used + * to find the FileHandler structure for + * the file (can't point directly to the + * FileHandler structure because it could + * go away while the event is queued). */ +} FileHandlerEvent; + +/* + * + * The following structure contains a set of select() masks to track + * readable, writable, and exceptional conditions. + */ + +typedef struct SelectMasks { + fd_set readable; + fd_set writable; + fd_set exceptional; +} SelectMasks; + +/* + * The following static structure contains the state information for the + * select based implementation of the Tcl notifier. One of these structures + * is created for each thread that is using the notifier. + */ + +typedef struct ThreadSpecificData { + FileHandler *firstFileHandlerPtr; + /* Pointer to head of file handler list. */ + + SelectMasks checkMasks; /* This structure is used to build up the masks + * to be used in the next call to select. + * Bits are set in response to calls to + * Tcl_CreateFileHandler. */ + SelectMasks readyMasks; /* This array reflects the readable/writable + * conditions that were found to exist by the + * last call to select. */ + int numFdBits; /* Number of valid bits in checkMasks + * (one more than highest fd for which + * Tcl_WatchFile has been called). */ + int onList; /* True if it is in this list */ + unsigned int pollState; /* pollState is used to implement a polling + * handshake between each thread and the + * notifier thread. Bits defined below. */ + struct ThreadSpecificData *nextPtr, *prevPtr; + /* All threads that are currently waiting on + * an event have their ThreadSpecificData + * structure on a doubly-linked listed formed + * from these pointers. You must hold the + * notifierLock before accessing these + * fields. */ + CFRunLoopSourceRef runLoopSource; + /* Any other thread alerts a notifier + * that an event is ready to be processed + * by signaling this CFRunLoopSource. */ + CFRunLoopRef runLoop; /* This thread's CFRunLoop, needs to be woken + * up whenever the runLoopSource is signaled. */ + int eventReady; /* True if an event is ready to be processed. */ +} ThreadSpecificData; + +static Tcl_ThreadDataKey dataKey; + +/* + * The following static indicates the number of threads that have + * initialized notifiers. + * + * You must hold the notifierInitLock before accessing this variable. + */ + +static int notifierCount = 0; + +/* + * The following variable points to the head of a doubly-linked list of + * of ThreadSpecificData structures for all threads that are currently + * waiting on an event. + * + * You must hold the notifierLock before accessing this list. + */ + +static ThreadSpecificData *waitingListPtr = NULL; + +/* + * The notifier thread spends all its time in select() waiting for a + * file descriptor associated with one of the threads on the waitingListPtr + * list to do something interesting. But if the contents of the + * waitingListPtr list ever changes, we need to wake up and restart + * the select() system call. You can wake up the notifier thread by + * writing a single byte to the file descriptor defined below. This + * file descriptor is the input-end of a pipe and the notifier thread is + * listening for data on the output-end of the same pipe. Hence writing + * to this file descriptor will cause the select() system call to return + * and wake up the notifier thread. + * + * You must hold the notifierLock lock before writing to the pipe. + */ + +static int triggerPipe = -1; +static int receivePipe = -1; /* Output end of triggerPipe */ + +/* + * We use Darwin-native spinlocks instead of pthread mutexes for notifier + * locking: this radically simplifies the implementation and lowers + * overhead. Note that these are not pure spinlocks, they employ various + * strategies to back off, making them immune to most priority-inversion + * livelocks (c.f. man 3 OSSpinLockLock). + */ + +#if defined(HAVE_LIBKERN_OSATOMIC_H) && defined(HAVE_OSSPINLOCKLOCK) +/* Use OSSpinLock API where available (Tiger or later) */ +#include +#else +/* Otherwise, use commpage spinlock SPI directly */ +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) +#endif + +/* + * These spinlocks lock access to the global notifier state. + */ + +static OSSpinLock notifierInitLock = 0; +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) + +/* + * The pollState bits + * POLL_WANT is set by each thread before it waits on its condition + * variable. It is checked by the notifier before it does + * select. + * POLL_DONE is set by the notifier if it goes into select after + * seeing POLL_WANT. The idea is to ensure it tries a select + * with the same bits the initial thread had set. + */ +#define POLL_WANT 0x1 +#define POLL_DONE 0x2 + +/* + * This is the thread ID of the notifier thread that does select. + */ +static pthread_t notifierThread; + +/* + * Static routines defined in this file. + */ + +static void NotifierThreadProc(ClientData clientData); +static int FileHandlerEventProc(Tcl_Event *evPtr, int flags); + +/* + *---------------------------------------------------------------------- + * + * Tcl_InitNotifier -- + * + * Initializes the platform specific notifier state. + * + * Results: + * Returns a handle to the notifier state for this thread.. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +ClientData +Tcl_InitNotifier() +{ + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + + tsdPtr->eventReady = 0; + + /* + * Initialize CFRunLoopSource and add it to CFRunLoop of this thread + */ + + if (!tsdPtr->runLoop) { + CFRunLoopRef runLoop = CFRunLoopGetCurrent(); + CFRunLoopSourceRef runLoopSource; + CFRunLoopSourceContext runLoopSourceContext; + + bzero(&runLoopSourceContext, sizeof(CFRunLoopSourceContext)); + runLoopSourceContext.info = tsdPtr; + runLoopSource = CFRunLoopSourceCreate(NULL, 0, &runLoopSourceContext); + if (!runLoopSource) { + Tcl_Panic("Tcl_InitNotifier: could not create CFRunLoopSource."); + } + CFRunLoopAddSource(runLoop, runLoopSource, kCFRunLoopCommonModes); + CFRelease(runLoopSource); + tsdPtr->runLoopSource = runLoopSource; + tsdPtr->runLoop = runLoop; + } + + /* + * Initialize trigger pipe and start the Notifier thread if necessary. + */ + + LOCK_NOTIFIER_INIT; + if (notifierCount == 0) { + int fds[2], status, result; + pthread_attr_t attr; + + if (pipe(fds) != 0) { + Tcl_Panic("Tcl_InitNotifier: could not create trigger pipe."); + } + + status = fcntl(fds[0], F_GETFL); + status |= O_NONBLOCK; + if (fcntl(fds[0], F_SETFL, status) < 0) { + Tcl_Panic("Tcl_InitNotifier: could not make receive pipe non blocking."); + } + status = fcntl(fds[1], F_GETFL); + status |= O_NONBLOCK; + if (fcntl(fds[1], F_SETFL, status) < 0) { + Tcl_Panic("Tcl_InitNotifier: could not make trigger pipe non blocking."); + } + + 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."); + } + } + notifierCount++; + UNLOCK_NOTIFIER_INIT; + + return (ClientData) tsdPtr; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_FinalizeNotifier -- + * + * This function is called to cleanup the notifier state before + * a thread is terminated. + * + * Results: + * None. + * + * Side effects: + * May terminate the background notifier thread if this is the + * last notifier instance. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_FinalizeNotifier(clientData) + ClientData clientData; /* Not used. */ +{ + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + + LOCK_NOTIFIER_INIT; + notifierCount--; + + /* + * If this is the last thread to use the notifier, close the notifier + * pipe and wait for the background thread to terminate. + */ + + if (notifierCount == 0) { + int result; + + if (triggerPipe < 0) { + Tcl_Panic("Tcl_FinalizeNotifier: notifier pipe not initialized."); + } + + /* + * Send "q" message to the notifier thread so that it will + * terminate. The notifier will return from its call to select() + * and notice that a "q" message has arrived, it will then close + * its side of the pipe and terminate its thread. Note the we can + * not just close the pipe and check for EOF in the notifier + * thread because if a background child process was created with + * exec, select() would not register the EOF on the pipe until the + * child processes had terminated. [Bug: 4139] + */ + write(triggerPipe, "q", 1); + close(triggerPipe); + + result = pthread_join(notifierThread, NULL); + if (result) { + Tcl_Panic("Tcl_FinalizeNotifier: unable to join notifier thread."); + } + + close(receivePipe); + triggerPipe = -1; + } + UNLOCK_NOTIFIER_INIT; + + LOCK_NOTIFIER; /* for concurrency with Tcl_AlertNotifier */ + if (tsdPtr->runLoop) { + tsdPtr->runLoop = NULL; + /* Remove runLoopSource from all CFRunLoops and release it */ + CFRunLoopSourceInvalidate(tsdPtr->runLoopSource); + tsdPtr->runLoopSource = NULL; + } + UNLOCK_NOTIFIER; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_AlertNotifier -- + * + * Wake up the specified notifier from any thread. This routine + * is called by the platform independent notifier code whenever + * the Tcl_ThreadAlert routine is called. This routine is + * guaranteed not to be called on a given notifier after + * Tcl_FinalizeNotifier is called for that notifier. + * + * Results: + * None. + * + * Side effects: + * Signals the notifier condition variable for the specified + * notifier. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_AlertNotifier(clientData) + ClientData clientData; +{ + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) clientData; + LOCK_NOTIFIER; + if (tsdPtr->runLoop) { + tsdPtr->eventReady = 1; + CFRunLoopSourceSignal(tsdPtr->runLoopSource); + CFRunLoopWakeUp(tsdPtr->runLoop); + } + UNLOCK_NOTIFIER; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_SetTimer -- + * + * This procedure sets the current notifier timer value. This + * interface is not implemented in this notifier because we are + * always running inside of Tcl_DoOneEvent. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_SetTimer(timePtr) + Tcl_Time *timePtr; /* Timeout value, may be NULL. */ +{ + /* + * The interval timer doesn't do anything in this implementation, + * because the only event loop is via Tcl_DoOneEvent, which passes + * timeout values to Tcl_WaitForEvent. + */ + + if (tclStubs.tcl_SetTimer != tclOriginalNotifier.setTimerProc) { + tclStubs.tcl_SetTimer(timePtr); + } +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_ServiceModeHook -- + * + * This function is invoked whenever the service mode changes. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_ServiceModeHook(mode) + int mode; /* Either TCL_SERVICE_ALL, or + * TCL_SERVICE_NONE. */ +{ +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_CreateFileHandler -- + * + * This procedure registers a file handler with the select notifier. + * + * Results: + * None. + * + * Side effects: + * Creates a new file handler structure. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_CreateFileHandler(fd, mask, proc, clientData) + int fd; /* Handle of stream to watch. */ + int mask; /* OR'ed combination of TCL_READABLE, + * TCL_WRITABLE, and TCL_EXCEPTION: + * indicates conditions under which + * proc should be called. */ + Tcl_FileProc *proc; /* Procedure to call for each + * selected event. */ + ClientData clientData; /* Arbitrary data to pass to proc. */ +{ + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + FileHandler *filePtr; + + if (tclStubs.tcl_CreateFileHandler != tclOriginalNotifier.createFileHandlerProc) { + tclStubs.tcl_CreateFileHandler(fd, mask, proc, clientData); + return; + } + + for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL; + filePtr = filePtr->nextPtr) { + if (filePtr->fd == fd) { + break; + } + } + if (filePtr == NULL) { + filePtr = (FileHandler*) ckalloc(sizeof(FileHandler)); + filePtr->fd = fd; + filePtr->readyMask = 0; + filePtr->nextPtr = tsdPtr->firstFileHandlerPtr; + tsdPtr->firstFileHandlerPtr = filePtr; + } + filePtr->proc = proc; + filePtr->clientData = clientData; + filePtr->mask = mask; + + /* + * Update the check masks for this file. + */ + + if (mask & TCL_READABLE) { + FD_SET(fd, &(tsdPtr->checkMasks.readable)); + } else { + FD_CLR(fd, &(tsdPtr->checkMasks.readable)); + } + if (mask & TCL_WRITABLE) { + FD_SET(fd, &(tsdPtr->checkMasks.writable)); + } else { + FD_CLR(fd, &(tsdPtr->checkMasks.writable)); + } + if (mask & TCL_EXCEPTION) { + FD_SET(fd, &(tsdPtr->checkMasks.exceptional)); + } else { + FD_CLR(fd, &(tsdPtr->checkMasks.exceptional)); + } + if (tsdPtr->numFdBits <= fd) { + tsdPtr->numFdBits = fd+1; + } +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_DeleteFileHandler -- + * + * Cancel a previously-arranged callback arrangement for + * a file. + * + * Results: + * None. + * + * Side effects: + * If a callback was previously registered on file, remove it. + * + *---------------------------------------------------------------------- + */ + +void +Tcl_DeleteFileHandler(fd) + int fd; /* Stream id for which to remove callback procedure. */ +{ + FileHandler *filePtr, *prevPtr; + int i; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + + if (tclStubs.tcl_DeleteFileHandler != tclOriginalNotifier.deleteFileHandlerProc) { + tclStubs.tcl_DeleteFileHandler(fd); + return; + } + + /* + * Find the entry for the given file (and return if there isn't one). + */ + + for (prevPtr = NULL, filePtr = tsdPtr->firstFileHandlerPtr; ; + prevPtr = filePtr, filePtr = filePtr->nextPtr) { + if (filePtr == NULL) { + return; + } + if (filePtr->fd == fd) { + break; + } + } + + /* + * Update the check masks for this file. + */ + + if (filePtr->mask & TCL_READABLE) { + FD_CLR(fd, &(tsdPtr->checkMasks.readable)); + } + if (filePtr->mask & TCL_WRITABLE) { + FD_CLR(fd, &(tsdPtr->checkMasks.writable)); + } + if (filePtr->mask & TCL_EXCEPTION) { + FD_CLR(fd, &(tsdPtr->checkMasks.exceptional)); + } + + /* + * Find current max fd. + */ + + if (fd+1 == tsdPtr->numFdBits) { + tsdPtr->numFdBits = 0; + for (i = fd-1; i >= 0; i--) { + if (FD_ISSET(i, &(tsdPtr->checkMasks.readable)) + || FD_ISSET(i, &(tsdPtr->checkMasks.writable)) + || FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) { + tsdPtr->numFdBits = i+1; + break; + } + } + } + + /* + * Clean up information in the callback record. + */ + + if (prevPtr == NULL) { + tsdPtr->firstFileHandlerPtr = filePtr->nextPtr; + } else { + prevPtr->nextPtr = filePtr->nextPtr; + } + ckfree((char *) filePtr); +} + +/* + *---------------------------------------------------------------------- + * + * FileHandlerEventProc -- + * + * This procedure is called by Tcl_ServiceEvent when a file event + * reaches the front of the event queue. This procedure is + * responsible for actually handling the event by invoking the + * callback for the file handler. + * + * Results: + * Returns 1 if the event was handled, meaning it should be removed + * from the queue. Returns 0 if the event was not handled, meaning + * it should stay on the queue. The only time the event isn't + * handled is if the TCL_FILE_EVENTS flag bit isn't set. + * + * Side effects: + * Whatever the file handler's callback procedure does. + * + *---------------------------------------------------------------------- + */ + +static int +FileHandlerEventProc(evPtr, flags) + Tcl_Event *evPtr; /* Event to service. */ + int flags; /* Flags that indicate what events to + * handle, such as TCL_FILE_EVENTS. */ +{ + int mask; + FileHandler *filePtr; + FileHandlerEvent *fileEvPtr = (FileHandlerEvent *) evPtr; + ThreadSpecificData *tsdPtr; + + if (!(flags & TCL_FILE_EVENTS)) { + return 0; + } + + /* + * Search through the file handlers to find the one whose handle matches + * the event. We do this rather than keeping a pointer to the file + * handler directly in the event, so that the handler can be deleted + * while the event is queued without leaving a dangling pointer. + */ + + tsdPtr = TCL_TSD_INIT(&dataKey); + for (filePtr = tsdPtr->firstFileHandlerPtr; filePtr != NULL; + filePtr = filePtr->nextPtr) { + if (filePtr->fd != fileEvPtr->fd) { + continue; + } + + /* + * The code is tricky for two reasons: + * 1. The file handler's desired events could have changed + * since the time when the event was queued, so AND the + * ready mask with the desired mask. + * 2. The file could have been closed and re-opened since + * the time when the event was queued. This is why the + * ready mask is stored in the file handler rather than + * the queued event: it will be zeroed when a new + * file handler is created for the newly opened file. + */ + + mask = filePtr->readyMask & filePtr->mask; + filePtr->readyMask = 0; + if (mask != 0) { + (*filePtr->proc)(filePtr->clientData, mask); + } + break; + } + return 1; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_WaitForEvent -- + * + * This function is called by Tcl_DoOneEvent to wait for new + * events on the message queue. If the block time is 0, then + * Tcl_WaitForEvent just polls without blocking. + * + * Results: + * Returns -1 if the select would block forever, otherwise + * returns 0. + * + * Side effects: + * Queues file events that are detected by the select. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_WaitForEvent(timePtr) + Tcl_Time *timePtr; /* Maximum block time, or NULL. */ +{ + FileHandler *filePtr; + FileHandlerEvent *fileEvPtr; + int mask; + Tcl_Time myTime; + int waitForFiles; + Tcl_Time *myTimePtr; + ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey); + + if (tclStubs.tcl_WaitForEvent != tclOriginalNotifier.waitForEventProc) { + return tclStubs.tcl_WaitForEvent(timePtr); + } + + if (timePtr != NULL) { + /* TIP #233 (Virtualized Time). Is virtual time in effect ? + * And do we actually have something to scale ? If yes to both + * then we call the handler to do this scaling */ + + myTime.sec = timePtr->sec; + myTime.usec = timePtr->usec; + + if (myTime.sec != 0 || myTime.usec != 0) { + (*tclScaleTimeProcPtr) (&myTime, tclTimeClientData); + } + + myTimePtr = &myTime; + } else { + myTimePtr = NULL; + } + + /* + * Place this thread on the list of interested threads, signal the + * notifier thread, and wait for a response or a timeout. + */ + + LOCK_NOTIFIER; + + waitForFiles = (tsdPtr->numFdBits > 0); + if (myTimePtr != NULL && myTimePtr->sec == 0 && myTimePtr->usec == 0) { + /* + * Cannot emulate a polling select with a polling condition variable. + * Instead, pretend to wait for files and tell the notifier + * thread what we are doing. The notifier thread makes sure + * it goes through select with its select mask in the same state + * as ours currently is. We block until that happens. + */ + + waitForFiles = 1; + tsdPtr->pollState = POLL_WANT; + myTimePtr = NULL; + } else { + tsdPtr->pollState = 0; + } + + if (waitForFiles) { + /* + * Add the ThreadSpecificData structure of this thread to the list + * of ThreadSpecificData structures of all threads that are waiting + * on file events. + */ + + tsdPtr->nextPtr = waitingListPtr; + if (waitingListPtr) { + waitingListPtr->prevPtr = tsdPtr; + } + tsdPtr->prevPtr = 0; + waitingListPtr = tsdPtr; + tsdPtr->onList = 1; + + write(triggerPipe, "", 1); + } + + FD_ZERO(&(tsdPtr->readyMasks.readable)); + FD_ZERO(&(tsdPtr->readyMasks.writable)); + FD_ZERO(&(tsdPtr->readyMasks.exceptional)); + + if (!tsdPtr->eventReady) { + CFTimeInterval waitTime; + + if (myTimePtr == NULL) { + waitTime = 1.0e10; /* Wait forever, as per CFRunLoop.c */ + } else { + waitTime = myTimePtr->sec + 1.0e-6 * myTimePtr->usec; + } + UNLOCK_NOTIFIER; + CFRunLoopRunInMode(kCFRunLoopDefaultMode, waitTime, TRUE); + LOCK_NOTIFIER; + } + tsdPtr->eventReady = 0; + + if (waitForFiles && tsdPtr->onList) { + /* + * Remove the ThreadSpecificData structure of this thread from the + * waiting list. Alert the notifier thread to recompute its select + * masks - skipping this caused a hang when trying to close a pipe + * which the notifier thread was still doing a select on. + */ + + if (tsdPtr->prevPtr) { + tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr; + } else { + waitingListPtr = tsdPtr->nextPtr; + } + if (tsdPtr->nextPtr) { + tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr; + } + tsdPtr->nextPtr = tsdPtr->prevPtr = NULL; + tsdPtr->onList = 0; + write(triggerPipe, "", 1); + } + + + /* + * Queue all detected file events before returning. + */ + + for (filePtr = tsdPtr->firstFileHandlerPtr; (filePtr != NULL); + filePtr = filePtr->nextPtr) { + + mask = 0; + if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.readable))) { + mask |= TCL_READABLE; + } + if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.writable))) { + mask |= TCL_WRITABLE; + } + if (FD_ISSET(filePtr->fd, &(tsdPtr->readyMasks.exceptional))) { + mask |= TCL_EXCEPTION; + } + + if (!mask) { + continue; + } + + /* + * Don't bother to queue an event if the mask was previously + * non-zero since an event must still be on the queue. + */ + + if (filePtr->readyMask == 0) { + fileEvPtr = (FileHandlerEvent *) ckalloc(sizeof(FileHandlerEvent)); + fileEvPtr->header.proc = FileHandlerEventProc; + fileEvPtr->fd = filePtr->fd; + Tcl_QueueEvent((Tcl_Event *) fileEvPtr, TCL_QUEUE_TAIL); + } + filePtr->readyMask = mask; + } + UNLOCK_NOTIFIER; + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * NotifierThreadProc -- + * + * This routine is the initial (and only) function executed by the + * special notifier thread. Its job is to wait for file descriptors + * to become readable or writable or to have an exception condition + * and then to notify other threads who are interested in this + * information by signalling a condition variable. Other threads + * can signal this notifier thread of a change in their interests + * by writing a single byte to a special pipe that the notifier + * thread is monitoring. + * + * Result: + * None. Once started, this routine never exits. It dies with + * the overall process. + * + * Side effects: + * The trigger pipe used to signal the notifier thread is created + * when the notifier thread first starts. + * + *---------------------------------------------------------------------- + */ + +static void +NotifierThreadProc(clientData) + ClientData clientData; /* Not used. */ +{ + ThreadSpecificData *tsdPtr; + fd_set readableMask; + fd_set writableMask; + fd_set exceptionalMask; + int i, numFdBits = 0; + long found; + struct timeval poll = {0., 0.}, *timePtr; + char buf[2]; + + /* + * Look for file events and report them to interested threads. + */ + + while (1) { + FD_ZERO(&readableMask); + FD_ZERO(&writableMask); + FD_ZERO(&exceptionalMask); + + /* + * Compute the logical OR of the select masks from all the + * waiting notifiers. + */ + + LOCK_NOTIFIER; + timePtr = NULL; + for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) { + for (i = tsdPtr->numFdBits-1; i >= 0; --i) { + if (FD_ISSET(i, &(tsdPtr->checkMasks.readable))) { + FD_SET(i, &readableMask); + } + if (FD_ISSET(i, &(tsdPtr->checkMasks.writable))) { + FD_SET(i, &writableMask); + } + if (FD_ISSET(i, &(tsdPtr->checkMasks.exceptional))) { + FD_SET(i, &exceptionalMask); + } + } + if (tsdPtr->numFdBits > numFdBits) { + numFdBits = tsdPtr->numFdBits; + } + if (tsdPtr->pollState & POLL_WANT) { + /* + * Here we make sure we go through select() with the same + * mask bits that were present when the thread tried to poll. + */ + + tsdPtr->pollState |= POLL_DONE; + timePtr = &poll; + } + } + UNLOCK_NOTIFIER; + + /* + * Set up the select mask to include the receive pipe. + */ + + if (receivePipe >= numFdBits) { + numFdBits = receivePipe + 1; + } + FD_SET(receivePipe, &readableMask); + + if (select(numFdBits, &readableMask, &writableMask, &exceptionalMask, + timePtr) == -1) { + /* + * Try again immediately on an error. + */ + + continue; + } + + /* + * Alert any threads that are waiting on a ready file descriptor. + */ + + LOCK_NOTIFIER; + for (tsdPtr = waitingListPtr; tsdPtr; tsdPtr = tsdPtr->nextPtr) { + found = 0; + + for (i = tsdPtr->numFdBits-1; i >= 0; --i) { + if (FD_ISSET(i, &(tsdPtr->checkMasks.readable)) + && FD_ISSET(i, &readableMask)) { + FD_SET(i, &(tsdPtr->readyMasks.readable)); + found = 1; + } + if (FD_ISSET(i, &(tsdPtr->checkMasks.writable)) + && FD_ISSET(i, &writableMask)) { + FD_SET(i, &(tsdPtr->readyMasks.writable)); + found = 1; + } + if (FD_ISSET(i, &(tsdPtr->checkMasks.exceptional)) + && FD_ISSET(i, &exceptionalMask)) { + FD_SET(i, &(tsdPtr->readyMasks.exceptional)); + found = 1; + } + } + + if (found || (tsdPtr->pollState & POLL_DONE)) { + tsdPtr->eventReady = 1; + if (tsdPtr->onList) { + /* + * Remove the ThreadSpecificData structure of this + * thread from the waiting list. This prevents us from + * continuously spining on select until the other + * threads runs and services the file event. + */ + + if (tsdPtr->prevPtr) { + tsdPtr->prevPtr->nextPtr = tsdPtr->nextPtr; + } else { + waitingListPtr = tsdPtr->nextPtr; + } + if (tsdPtr->nextPtr) { + tsdPtr->nextPtr->prevPtr = tsdPtr->prevPtr; + } + tsdPtr->nextPtr = tsdPtr->prevPtr = NULL; + tsdPtr->onList = 0; + tsdPtr->pollState = 0; + } + if (tsdPtr->runLoop) { + CFRunLoopSourceSignal(tsdPtr->runLoopSource); + CFRunLoopWakeUp(tsdPtr->runLoop); + } + } + } + UNLOCK_NOTIFIER; + + /* + * Consume the next byte from the notifier pipe if the pipe was + * readable. Note that there may be multiple bytes pending, but + * to avoid a race condition we only read one at a time. + */ + + if (FD_ISSET(receivePipe, &readableMask)) { + i = read(receivePipe, buf, 1); + + if ((i == 0) || ((i == 1) && (buf[0] == 'q'))) { + /* + * Someone closed the write end of the pipe or sent us a + * Quit message [Bug: 4139] and then closed the write end + * of the pipe so we need to shut down the notifier thread. + */ + + break; + } + } + } + pthread_exit (0); +} + +#endif /* HAVE_COREFOUNDATION */ diff --git a/tests/unixNotfy.test b/tests/unixNotfy.test index fc9cdde..42090d9 100644 --- a/tests/unixNotfy.test +++ b/tests/unixNotfy.test @@ -10,7 +10,7 @@ # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # -# RCS: @(#) $Id: unixNotfy.test,v 1.17 2004/06/24 10:34:12 dkf Exp $ +# RCS: @(#) $Id: unixNotfy.test,v 1.18 2005/05/14 20:46:46 das Exp $ # The tests should not be run if you have a notifier which is unable to # detect infinite vwaits, as the tests below will hang. The presence of @@ -24,8 +24,10 @@ if {[lsearch [namespace children] ::tcltest] == -1} { # When run in a Tk shell, these tests hang. testConstraint noTk [expr {![info exists tk_version]}] testConstraint testthread [expr {[info commands testthread] != {}}] +# Darwin always uses a threaded notifier testConstraint unthreaded [expr { - ![info exist tcl_platform(threaded)] || !$tcl_platform(threaded) + (![info exist tcl_platform(threaded)] || !$tcl_platform(threaded)) + && $tcl_platform(os) ne "Darwin" }] # The next two tests will hang if threads are enabled because the notifier diff --git a/unix/Makefile.in b/unix/Makefile.in index c5f8da2..7245368 100644 --- a/unix/Makefile.in +++ b/unix/Makefile.in @@ -5,7 +5,7 @@ # "autoconf" program (constructs like "@foo@" will get replaced in the # actual Makefile. # -# RCS: @(#) $Id: Makefile.in,v 1.166 2005/05/13 17:12:19 dgp Exp $ +# RCS: @(#) $Id: Makefile.in,v 1.167 2005/05/14 20:46:46 das Exp $ VERSION = @TCL_VERSION@ MAJOR_VERSION = @TCL_MAJOR_VERSION@ @@ -323,7 +323,7 @@ TOMMATH_OBJS = bncore.o bn_reverse.o bn_fast_s_mp_mul_digs.o \ STUB_LIB_OBJS = tclStubLib.o ${COMPAT_OBJS} -MAC_OSX_OBJS = tclMacOSXBundle.o tclMacOSXFCmd.o +MAC_OSX_OBJS = tclMacOSXBundle.o tclMacOSXFCmd.o tclMacOSXNotify.o OBJS = ${GENERIC_OBJS} ${TOMMATH_OBJS} ${UNIX_OBJS} ${NOTIFY_OBJS} \ ${COMPAT_OBJS} @DL_OBJS@ @PLAT_OBJS@ @@ -504,7 +504,8 @@ DL_SRCS = \ MAC_OSX_SRCS = \ $(MAC_OSX_DIR)/tclMacOSXBundle.c \ - $(MAC_OSX_DIR)/tclMacOSXFCmd.c + $(MAC_OSX_DIR)/tclMacOSXFCmd.c \ + $(MAC_OSX_DIR)/tclMacOSXNotify.c # Note: don't include DL_SRCS or MAC_OSX_SRCS in SRCS: most of those # files won't compile on the current machine, and they will cause @@ -825,6 +826,9 @@ install-private-headers: libraries do \ $(INSTALL_DATA) $$i $(PRIVATE_INCLUDE_INSTALL_DIR); \ done; + @if test -f tclConfig.h; then\ + $(INSTALL_DATA) tclConfig.h $(PRIVATE_INCLUDE_INSTALL_DIR); \ + fi; Makefile: $(UNIX_DIR)/Makefile.in $(DLTEST_DIR)/Makefile.in $(SHELL) config.status @@ -1352,6 +1356,9 @@ tclMacOSXBundle.o: $(MAC_OSX_DIR)/tclMacOSXBundle.c tclMacOSXFCmd.o: $(MAC_OSX_DIR)/tclMacOSXFCmd.c $(CC) -c $(CC_SWITCHES) $(MAC_OSX_DIR)/tclMacOSXFCmd.c +tclMacOSXNotify.o: $(MAC_OSX_DIR)/tclMacOSXNotify.c + $(CC) -c $(CC_SWITCHES) $(MAC_OSX_DIR)/tclMacOSXNotify.c + # The following targets are not completely general. They are provide # purely for documentation purposes so people who are interested in # the Xt based notifier can modify them to suit their own installation. diff --git a/unix/configure b/unix/configure index 3496807..95d4611 100755 --- a/unix/configure +++ b/unix/configure @@ -851,6 +851,7 @@ Optional Features: --enable-shared build and link with shared libraries --enable-shared --enable-64bit enable 64bit support (where applicable) --enable-64bit-vis enable 64bit Sparc VIS support + --enable-corefoundation use CoreFoundation API --enable-corefoundation --disable-load disallow dynamic loading and "load" command --enable-symbols build with debugging symbols --disable-symbols --enable-langinfo use nl_langinfo if possible to determine @@ -1322,6 +1323,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu + TCL_VERSION=8.5 TCL_MAJOR_VERSION=8 TCL_MINOR_VERSION=5 @@ -7913,19 +7915,344 @@ echo "${ECHO_T}$tcl_cv_ld_search_paths_first" >&6 CC_SEARCH_FLAGS="" LD_SEARCH_FLAGS="" LD_LIBRARY_PATH_VAR="DYLD_LIBRARY_PATH" - PLAT_OBJS="\${MAC_OSX_OBJS}" - PLAT_SRCS="\${MAC_OSX_SRCS}" + PLAT_OBJS='${MAC_OSX_OBJS}' + PLAT_SRCS='${MAC_OSX_SRCS}' TCL_SHLIB_LD_EXTRAS='-compatibility_version ${VERSION} -current_version ${VERSION} -install_name ${DYLIB_INSTALL_DIR}/${TCL_LIB_FILE} -seg1addr 0xa000000' TK_SHLIB_LD_EXTRAS=' -compatibility_version ${VERSION} -current_version ${VERSION} -install_name ${DYLIB_INSTALL_DIR}/${TK_LIB_FILE} -seg1addr 0xb000000 -unexported_symbols_list $$(f=$(TCL_STUB_LIB_FILE).E && nm -gjp $(TCL_BIN_DIR)/$(TCL_STUB_LIB_FILE) | tail +3 > $$f && echo $$f)' - LIBS="$LIBS -framework CoreFoundation" + echo "$as_me:$LINENO: checking whether to use CoreFoundation" >&5 +echo $ECHO_N "checking whether to use CoreFoundation... $ECHO_C" >&6 + # Check whether --enable-corefoundation or --disable-corefoundation was given. +if test "${enable_corefoundation+set}" = set; then + enableval="$enable_corefoundation" + tcl_corefoundation=$enableval +else + tcl_corefoundation=yes +fi; + echo "$as_me:$LINENO: result: $tcl_corefoundation" >&5 +echo "${ECHO_T}$tcl_corefoundation" >&6 + if test $tcl_corefoundation = yes; then + echo "$as_me:$LINENO: checking for CoreFoundation.framework" >&5 +echo $ECHO_N "checking for CoreFoundation.framework... $ECHO_C" >&6 +if test "${tcl_cv_lib_corefoundation+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + hold_libs=$LIBS + LIBS="$LIBS -framework CoreFoundation" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +CFBundleRef b = CFBundleGetMainBundle(); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + tcl_cv_lib_corefoundation=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +tcl_cv_lib_corefoundation=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LIBS=$hold_libs +fi +echo "$as_me:$LINENO: result: $tcl_cv_lib_corefoundation" >&5 +echo "${ECHO_T}$tcl_cv_lib_corefoundation" >&6 + if test $tcl_cv_lib_corefoundation = yes; then + LIBS="$LIBS -framework CoreFoundation" cat >>confdefs.h <<\_ACEOF -#define MAC_OSX_TCL 1 +#define HAVE_COREFOUNDATION 1 _ACEOF + fi + fi + +for ac_header in libkern/OSAtomic.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------ ## +## Report this to the tcl lists. ## +## ------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_func in OSSpinLockLock +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + cat >>confdefs.h <<\_ACEOF -#define HAVE_CFBUNDLE 1 +#define MAC_OSX_TCL 1 _ACEOF @@ -7943,6 +8270,103 @@ cat >>confdefs.h <<\_ACEOF #define MODULE_SCOPE __private_extern__ _ACEOF + # prior to Darwin 7, realpath is not threadsafe, so don't + # use it when threads are enabled, c.f. bug # 711232: + echo "$as_me:$LINENO: checking for realpath" >&5 +echo $ECHO_N "checking for realpath... $ECHO_C" >&6 +if test "${ac_cv_func_realpath+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define realpath to an innocuous variant, in case declares realpath. + For example, HP-UX 11i declares gettimeofday. */ +#define realpath innocuous_realpath + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char realpath (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef realpath + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char realpath (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_realpath) || defined (__stub___realpath) +choke me +#else +char (*f) () = realpath; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != realpath; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_realpath=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func_realpath=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func_realpath" >&5 +echo "${ECHO_T}$ac_cv_func_realpath" >&6 + + if test "$ac_cv_func_realpath" = yes -a "${TCL_THREADS}" = 1 \ + -a `uname -r | awk -F. '{print $1}'` -lt 7 ; then + ac_cv_func_realpath=no + fi ;; NEXTSTEP-*) SHLIB_CFLAGS="" @@ -14904,6 +15328,11 @@ echo "${ECHO_T}framework" >&6 echo "$as_me: WARNING: \"Frameworks can only be built if --enable-shared is yes\"" >&2;} FRAMEWORK_BUILD=0 fi + if test $tcl_corefoundation = no; then + { echo "$as_me:$LINENO: WARNING: \"Frameworks can only be used when CoreFoundation is available\"" >&5 +echo "$as_me: WARNING: \"Frameworks can only be used when CoreFoundation is available\"" >&2;} + FRAMEWORK_BUILD=0 + fi else echo "$as_me:$LINENO: result: standard shared library" >&5 echo "${ECHO_T}standard shared library" >&6 diff --git a/unix/configure.in b/unix/configure.in index 7403e15..8c9a91c 100644 --- a/unix/configure.in +++ b/unix/configure.in @@ -3,12 +3,16 @@ dnl This file is an input file used by the GNU "autoconf" program to dnl generate the file "configure", which is run during Tcl installation dnl to configure the system for the local environment. # -# RCS: @(#) $Id: configure.in,v 1.128 2005/05/10 18:35:26 kennykb Exp $ +# RCS: @(#) $Id: configure.in,v 1.129 2005/05/14 20:46:48 das Exp $ AC_INIT([tcl],[8.5]) AC_PREREQ(2.57) + dnl AC_CONFIG_HEADERS([tclConfig.h]) -dnl AC_CONFIG_COMMANDS_PRE([DEFS=-DHAVE_TCL_CONFIG_H]) +dnl AC_CONFIG_COMMANDS_PRE([DEFS="-DHAVE_TCL_CONFIG_H -imacros tclConfig.h"]) +dnl AH_TOP([#ifndef _TCLCONFIG +dnl #define _TCLCONFIG]) +dnl AH_BOTTOM([#endif /* _TCLCONFIG */]) TCL_VERSION=8.5 TCL_MAJOR_VERSION=8 diff --git a/unix/tcl.m4 b/unix/tcl.m4 index bbed997..fc4247f 100644 --- a/unix/tcl.m4 +++ b/unix/tcl.m4 @@ -402,6 +402,10 @@ AC_DEFUN(SC_ENABLE_FRAMEWORK, [ AC_MSG_WARN("Frameworks can only be built if --enable-shared is yes") FRAMEWORK_BUILD=0 fi + if test $tcl_corefoundation = no; then + AC_MSG_WARN("Frameworks can only be used when CoreFoundation is available") + FRAMEWORK_BUILD=0 + fi else AC_MSG_RESULT([standard shared library]) FRAMEWORK_BUILD=0 @@ -1406,17 +1410,42 @@ dnl AC_CHECK_TOOL(AR, ar) CC_SEARCH_FLAGS="" LD_SEARCH_FLAGS="" LD_LIBRARY_PATH_VAR="DYLD_LIBRARY_PATH" - PLAT_OBJS="\${MAC_OSX_OBJS}" - PLAT_SRCS="\${MAC_OSX_SRCS}" + PLAT_OBJS='${MAC_OSX_OBJS}' + PLAT_SRCS='${MAC_OSX_SRCS}' TCL_SHLIB_LD_EXTRAS='-compatibility_version ${VERSION} -current_version ${VERSION} -install_name ${DYLIB_INSTALL_DIR}/${TCL_LIB_FILE} -seg1addr 0xa000000' TK_SHLIB_LD_EXTRAS=' -compatibility_version ${VERSION} -current_version ${VERSION} -install_name ${DYLIB_INSTALL_DIR}/${TK_LIB_FILE} -seg1addr 0xb000000 -unexported_symbols_list $$(f=$(TCL_STUB_LIB_FILE).E && nm -gjp $(TCL_BIN_DIR)/$(TCL_STUB_LIB_FILE) | tail +3 > $$f && echo $$f)' - LIBS="$LIBS -framework CoreFoundation" - AC_DEFINE(MAC_OSX_TCL, 1, ["Is this a Mac I see before me?"]) - AC_DEFINE(HAVE_CFBUNDLE, 1, [Do we have access to Mac bundles?]) + AC_MSG_CHECKING([whether to use CoreFoundation]) + AC_ARG_ENABLE(corefoundation, [ --enable-corefoundation use CoreFoundation API [--enable-corefoundation]], + [tcl_corefoundation=$enableval], [tcl_corefoundation=yes]) + AC_MSG_RESULT([$tcl_corefoundation]) + if test $tcl_corefoundation = yes; then + AC_CACHE_CHECK([for CoreFoundation.framework], tcl_cv_lib_corefoundation, [ + hold_libs=$LIBS + LIBS="$LIBS -framework CoreFoundation" + AC_TRY_LINK([#include ], + [CFBundleRef b = CFBundleGetMainBundle();], + tcl_cv_lib_corefoundation=yes, tcl_cv_lib_corefoundation=no) + LIBS=$hold_libs]) + if test $tcl_cv_lib_corefoundation = yes; then + LIBS="$LIBS -framework CoreFoundation" + AC_DEFINE(HAVE_COREFOUNDATION, 1, + [Do we have access to Darwin CoreFoundation.framework ?]) + fi + fi + AC_CHECK_HEADERS(libkern/OSAtomic.h) + AC_CHECK_FUNCS(OSSpinLockLock) + AC_DEFINE(MAC_OSX_TCL, 1, [Is this a Mac I see before me?]) AC_DEFINE(USE_VFORK, 1, [Should we use vfork() instead of fork()?]) AC_DEFINE(TCL_DEFAULT_ENCODING,"utf-8", [Are we to override what our default encoding is?]) AC_DEFINE(MODULE_SCOPE, __private_extern__, [Linker support for module scope symbols]) + # prior to Darwin 7, realpath is not threadsafe, so don't + # use it when threads are enabled, c.f. bug # 711232: + AC_CHECK_FUNC(realpath) + if test "$ac_cv_func_realpath" = yes -a "${TCL_THREADS}" = 1 \ + -a `uname -r | awk -F. '{print [$]1}'` -lt 7 ; then + ac_cv_func_realpath=no + fi ;; NEXTSTEP-*) SHLIB_CFLAGS="" diff --git a/unix/tclConfig.h.in b/unix/tclConfig.h.in index 1f83e5c..0806e0d 100644 --- a/unix/tclConfig.h.in +++ b/unix/tclConfig.h.in @@ -1,5 +1,8 @@ /* tclConfig.h.in. Generated from configure.in by autoheader. */ +#ifndef _TCLCONFIG +#define _TCLCONFIG + /* Is pthread_attr_get_np() declared in ? */ #undef ATTRGETNP_NOT_DECLARED @@ -12,12 +15,12 @@ /* Do we have BSDgettimeofday()? */ #undef HAVE_BSDGETTIMEOFDAY -/* Do we have access to Mac bundles? */ -#undef HAVE_CFBUNDLE - /* Define to 1 if you have the `chflags' function. */ #undef HAVE_CHFLAGS +/* Do we have access to Darwin CoreFoundation.framework ? */ +#undef HAVE_COREFOUNDATION + /* Define to 1 if you have the `getattrlist' function. */ #undef HAVE_GETATTRLIST @@ -33,12 +36,18 @@ /* Do we have nl_langinfo()? */ #undef HAVE_LANGINFO +/* Define to 1 if you have the header file. */ +#undef HAVE_LIBKERN_OSATOMIC_H + /* Do we have ? */ #undef HAVE_LIMITS_H /* Define to 1 if you have the `localtime_r' function. */ #undef HAVE_LOCALTIME_R +/* Define to 1 if you have the `lseek64' function. */ +#undef HAVE_LSEEK64 + /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H @@ -48,9 +57,15 @@ /* Do we have ? */ #undef HAVE_NET_ERRNO_H +/* Define to 1 if you have the `open64' function. */ +#undef HAVE_OPEN64 + /* Define to 1 if you have the `opendir' function. */ #undef HAVE_OPENDIR +/* Define to 1 if you have the `OSSpinLockLock' function. */ +#undef HAVE_OSSPINLOCKLOCK + /* Do we want a BSD-like thread-attribute interface? */ #undef HAVE_PTHREAD_ATTR_GET_NP @@ -78,9 +93,6 @@ /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H -/* Define to 1 if you have the `strstr' function. */ -#undef HAVE_STRSTR - /* Define to 1 if you have the `strtol' function. */ #undef HAVE_STRTOL @@ -159,9 +171,12 @@ /* Define to 1 if you have the `waitpid' function. */ #undef HAVE_WAITPID -/* "Is this a Mac I see before me?" */ +/* Is this a Mac I see before me? */ #undef MAC_OSX_TCL +/* Linker support for module scope symbols */ +#undef MODULE_SCOPE + /* Do we have ? */ #undef NO_DIRENT_H @@ -261,9 +276,6 @@ /* Are bytecode statistics enabled? */ #undef TCL_COMPILE_STATS -/* What extra letters do we insert for debugging binary code? */ -#undef TCL_DBGX - /* Are we to override what our default encoding is? */ #undef TCL_DEFAULT_ENCODING @@ -386,3 +398,5 @@ /* Define to `int' if doesn't define. */ #undef uid_t + +#endif /* _TCLCONFIG */ diff --git a/unix/tclLoadDyld.c b/unix/tclLoadDyld.c index af14fd2..c131050 100644 --- a/unix/tclLoadDyld.c +++ b/unix/tclLoadDyld.c @@ -11,7 +11,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclLoadDyld.c,v 1.15 2004/04/06 22:25:56 dgp Exp $ + * RCS: @(#) $Id: tclLoadDyld.c,v 1.16 2005/05/14 20:46:48 das Exp $ */ #include "tclInt.h" @@ -23,7 +23,7 @@ typedef struct Tcl_DyldModuleHandle { } Tcl_DyldModuleHandle; typedef struct Tcl_DyldLoadHandle { - const struct mach_header *dyld_lib; + CONST struct mach_header *dyld_lib; Tcl_DyldModuleHandle *firstModuleHandle; } Tcl_DyldLoadHandle; @@ -59,7 +59,7 @@ TclpDlopen(interp, pathPtr, loadHandle, unloadProcPtr) * this file. */ { Tcl_DyldLoadHandle *dyldLoadHandle; - const struct mach_header *dyld_lib; + CONST struct mach_header *dyld_lib; CONST char *native; /* @@ -89,7 +89,7 @@ TclpDlopen(interp, pathPtr, loadHandle, unloadProcPtr) if (!dyld_lib) { NSLinkEditErrors editError; - char *name, *msg; + CONST char *name, *msg; NSLinkEditError(&editError, &errno, &name, &msg); Tcl_AppendResult(interp, msg, (char *) NULL); return TCL_ERROR; @@ -152,7 +152,7 @@ TclpFindSymbol(interp, loadHandle, symbol) } } else { NSLinkEditErrors editError; - char *name, *msg; + CONST char *name, *msg; NSLinkEditError(&editError, &errno, &name, &msg); Tcl_AppendResult(interp, msg, (char *) NULL); } @@ -198,7 +198,7 @@ TclpUnloadFile(loadHandle) dyldModuleHandle = dyldModuleHandle->nextModuleHandle; ckfree(ptr); } - ckfree(dyldLoadHandle); + ckfree((char*) dyldLoadHandle); } /* diff --git a/unix/tclUnixChan.c b/unix/tclUnixChan.c index b6a5555..abc2edc 100644 --- a/unix/tclUnixChan.c +++ b/unix/tclUnixChan.c @@ -10,7 +10,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclUnixChan.c,v 1.56 2005/05/10 18:35:27 kennykb Exp $ + * RCS: @(#) $Id: tclUnixChan.c,v 1.57 2005/05/14 20:46:48 das Exp $ */ #include "tclInt.h" /* Internal definitions for Tcl. */ @@ -43,6 +43,13 @@ #define SUPPORTS_TTY +#undef DIRECT_BAUD +#ifdef B4800 +# if (B4800 == 4800) +# define DIRECT_BAUD +# endif /* B4800 == 4800 */ +#endif /* B4800 */ + #ifdef USE_TERMIOS # include # ifdef HAVE_SYS_IOCTL_H @@ -261,11 +268,15 @@ static int TtyCloseProc _ANSI_ARGS_((ClientData instanceData, Tcl_Interp *interp)); static void TtyGetAttributes _ANSI_ARGS_((int fd, TtyAttrs *ttyPtr)); +#ifndef DIRECT_BAUD static int TtyGetBaud _ANSI_ARGS_((unsigned long speed)); +#endif static int TtyGetOptionProc _ANSI_ARGS_((ClientData instanceData, Tcl_Interp *interp, CONST char *optionName, Tcl_DString *dsPtr)); +#ifndef DIRECT_BAUD static unsigned long TtyGetSpeed _ANSI_ARGS_((int baud)); +#endif static FileState * TtyInit _ANSI_ARGS_((int fd, int initialize)); static void TtyModemStatusStr _ANSI_ARGS_((int status, Tcl_DString *dsPtr)); @@ -1190,13 +1201,6 @@ TtyGetOptionProc(instanceData, interp, optionName, dsPtr) } } -#undef DIRECT_BAUD -#ifdef B4800 -# if (B4800 == 4800) -# define DIRECT_BAUD -# endif /* B4800 == 4800 */ -#endif /* B4800 */ - #ifdef DIRECT_BAUD # define TtyGetSpeed(baud) ((unsigned) (baud)) # define TtyGetBaud(speed) ((int) (speed)) diff --git a/unix/tclUnixInit.c b/unix/tclUnixInit.c index 1cd06a4..ec708f2 100644 --- a/unix/tclUnixInit.c +++ b/unix/tclUnixInit.c @@ -7,7 +7,7 @@ * Copyright (c) 1999 by Scriptics Corporation. * All rights reserved. * - * RCS: @(#) $Id: tclUnixInit.c,v 1.56 2005/05/10 18:35:27 kennykb Exp $ + * RCS: @(#) $Id: tclUnixInit.c,v 1.57 2005/05/14 20:46:48 das Exp $ */ #include "tclInt.h" @@ -26,7 +26,7 @@ # include # endif #endif -#ifdef HAVE_CFBUNDLE +#ifdef HAVE_COREFOUNDATION #include #endif @@ -328,11 +328,11 @@ static CONST LocaleTable localeTable[] = { #ifndef TCL_NO_STACK_CHECK static int GetStackSize _ANSI_ARGS_((size_t *stackSizePtr)); #endif /* TCL_NO_STACK_CHECK */ -#ifdef HAVE_CFBUNDLE +#ifdef HAVE_COREFOUNDATION static int MacOSXGetLibraryPath _ANSI_ARGS_(( Tcl_Interp *interp, int maxPathLen, char *tclLibPath)); -#endif /* HAVE_CFBUNDLE */ +#endif /* HAVE_COREFOUNDATION */ /* @@ -511,13 +511,13 @@ TclpInitLibraryPath(valuePtr, lengthPtr, encodingPtr) */ { -#ifdef HAVE_CFBUNDLE +#ifdef HAVE_COREFOUNDATION char tclLibPath[MAXPATHLEN + 1]; if (MacOSXGetLibraryPath(NULL, MAXPATHLEN, tclLibPath) == TCL_OK) { str = tclLibPath; } else -#endif /* HAVE_CFBUNDLE */ +#endif /* HAVE_COREFOUNDATION */ { /* TODO: Pull this value from the TIP 59 table */ str = defaultLibraryDir; @@ -724,7 +724,7 @@ TclpSetVariables(interp) CONST char *user; Tcl_DString ds; -#ifdef HAVE_CFBUNDLE +#ifdef HAVE_COREFOUNDATION char tclLibPath[MAXPATHLEN + 1]; if (MacOSXGetLibraryPath(interp, MAXPATHLEN, tclLibPath) == TCL_OK) { @@ -756,7 +756,7 @@ TclpSetVariables(interp) Tcl_StatBuf statBuf; if((frameworksURL = CFBundleCopyPrivateFrameworksURL(bundleRef))) { if(CFURLGetFileSystemRepresentation(frameworksURL, TRUE, - tclLibPath, MAXPATHLEN) && + (unsigned char*) tclLibPath, MAXPATHLEN) && ! TclOSstat(tclLibPath, &statBuf) && S_ISDIR(statBuf.st_mode)) { Tcl_SetVar(interp, "tcl_pkgPath", tclLibPath, @@ -768,7 +768,7 @@ TclpSetVariables(interp) } if((frameworksURL = CFBundleCopySharedFrameworksURL(bundleRef))) { if(CFURLGetFileSystemRepresentation(frameworksURL, TRUE, - tclLibPath, MAXPATHLEN) && + (unsigned char*) tclLibPath, MAXPATHLEN) && ! TclOSstat(tclLibPath, &statBuf) && S_ISDIR(statBuf.st_mode)) { Tcl_SetVar(interp, "tcl_pkgPath", tclLibPath, @@ -782,7 +782,7 @@ TclpSetVariables(interp) Tcl_SetVar(interp, "tcl_pkgPath", pkgPath, TCL_GLOBAL_ONLY | TCL_APPEND_VALUE); } else -#endif /* HAVE_CFBUNDLE */ +#endif /* HAVE_COREFOUNDATION */ { Tcl_SetVar(interp, "tcl_pkgPath", pkgPath, TCL_GLOBAL_ONLY); } @@ -1131,7 +1131,7 @@ GetStackSize(stackSizePtr) *---------------------------------------------------------------------- */ -#ifdef HAVE_CFBUNDLE +#ifdef HAVE_COREFOUNDATION static int MacOSXGetLibraryPath(Tcl_Interp *interp, int maxPathLen, char *tclLibPath) { @@ -1142,4 +1142,4 @@ MacOSXGetLibraryPath(Tcl_Interp *interp, int maxPathLen, char *tclLibPath) #endif return foundInFramework; } -#endif /* HAVE_CFBUNDLE */ +#endif /* HAVE_COREFOUNDATION */ diff --git a/unix/tclUnixNotfy.c b/unix/tclUnixNotfy.c index 32e0e3d..c80e566 100644 --- a/unix/tclUnixNotfy.c +++ b/unix/tclUnixNotfy.c @@ -11,9 +11,11 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclUnixNotfy.c,v 1.23 2005/05/10 18:35:28 kennykb Exp $ + * RCS: @(#) $Id: tclUnixNotfy.c,v 1.24 2005/05/14 20:46:48 das Exp $ */ +#ifndef HAVE_COREFOUNDATION /* Darwin/Mac OS X CoreFoundation notifier + * is in tclMacOSXNotify.c */ #include "tclInt.h" #include @@ -1080,3 +1082,5 @@ NotifierThreadProc(clientData) TclpThreadExit (0); } #endif + +#endif /* HAVE_COREFOUNDATION */ diff --git a/unix/tclUnixPort.h b/unix/tclUnixPort.h index 59c9709..eb4dbda 100644 --- a/unix/tclUnixPort.h +++ b/unix/tclUnixPort.h @@ -19,7 +19,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclUnixPort.h,v 1.41 2005/05/10 18:35:28 kennykb Exp $ + * RCS: @(#) $Id: tclUnixPort.h,v 1.42 2005/05/14 20:46:48 das Exp $ */ #ifndef _TCLUNIXPORT @@ -563,13 +563,6 @@ EXTERN char * TclpInetNtoa(struct in_addr); * #define gmtime(x) TclpGmtime(x) */ # undef inet_ntoa # define inet_ntoa(x) TclpInetNtoa(x) -# ifdef MAC_OSX_TCL -/* - * On Mac OS X, realpath is currently not - * thread safe, c.f. SF bug # 711232. - */ -# define NO_REALPATH -# endif # ifdef HAVE_PTHREAD_ATTR_GET_NP # define TclpPthreadGetAttrs pthread_attr_get_np # ifdef ATTRGETNP_NOT_DECLARED diff --git a/win/tclWin32Dll.c b/win/tclWin32Dll.c index a0e2d8d..25eb7b5 100644 --- a/win/tclWin32Dll.c +++ b/win/tclWin32Dll.c @@ -9,7 +9,7 @@ * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclWin32Dll.c,v 1.42 2005/05/13 21:20:36 kennykb Exp $ + * RCS: @(#) $Id: tclWin32Dll.c,v 1.43 2005/05/14 20:46:48 das Exp $ */ #include "tclWinInt.h" @@ -627,29 +627,6 @@ TclpCheckStackSpace() } /* - *---------------------------------------------------------------------- - * - * TclWinGetPlatform -- - * - * This is a kludge that allows the test library to get access - * the internal tclPlatform variable. - * - * Results: - * Returns a pointer to the tclPlatform variable. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -TclPlatformType * -TclWinGetPlatform() -{ - return &tclPlatform; -} - -/* *--------------------------------------------------------------------------- * * TclWinSetInterfaces -- -- cgit v0.12