Posted to tcl by dgp at Fri Apr 26 14:04:00 GMT 2013view raw
- Index: generic/tclEvent.c
- ==================================================================
- --- generic/tclEvent.c
- +++ generic/tclEvent.c
- @@ -79,12 +79,10 @@
- * in closing of files and pipes.
- */
- static int inExit = 0;
- -static int subsystemsInitialized = 0;
- -
- /*
- * This variable contains the application wide exit handler. It will be called
- * by Tcl_Exit instead of the C-runtime exit if this variable is set to a
- * non-NULL value.
- */
- @@ -1013,31 +1011,40 @@
- * Varied, see the respective initialization routines.
- *
- *-------------------------------------------------------------------------
- */
- +#define INITDEBUG 1
- +typedef enum {
- + DOWN, CHANGING, UP
- +} State;
- +
- +static State initState = DOWN;
- +static Tcl_ThreadId changer = NULL;
- +
- void
- TclInitSubsystems(void)
- {
- - if (inExit != 0) {
- - Tcl_Panic("TclInitSubsystems called while exiting");
- + start:
- + if (initState == UP) {
- + goto done;
- }
- - if (subsystemsInitialized == 0) {
- + if (initState == DOWN) {
- /*
- - * Double check inside the mutex. There are definitly calls back into
- - * this routine from some of the functions below.
- + * Enter the race to become the thread to change initState from
- + * DOWN to UP.
- */
- TclpInitLock();
- - if (subsystemsInitialized == 0) {
- - /*
- - * Have to set this bit here to avoid deadlock with the routines
- - * below us that call into TclInitSubsystems.
- - */
- - subsystemsInitialized = 1;
- + /* Did we win? */
- + if (initState == DOWN) {
- +
- + /* Yes! Record that we are CHANGING the state */
- + initState = CHANGING;
- + changer = Tcl_GetCurrentThread();
- /*
- * Initialize locks used by the memory allocators before anything
- * interesting happens so we can use the allocators in the
- * implementation of self-initializing locks.
- @@ -1059,13 +1066,43 @@
- * mutexes. */
- TclInitIOSubsystem(); /* Inits a tsd key (noop). */
- TclInitEncodingSubsystem(); /* Process wide encoding init. */
- TclpSetInterfaces();
- TclInitNamespaceSubsystem();/* Register ns obj type (mutexed). */
- +
- + /* Record that the state is now UP */
- + initState = UP;
- + changer = NULL;
- + }
- + TclpInitUnlock();
- + } else {
- + /* initState != DOWN */
- + if (changer == Tcl_GetCurrentThread()) {
- + /*
- + * We're the thread changing state and must have reached here via
- + * a recursive call. Return as a no-op to avoid deadlock.
- + * NOTE: This really should not be happening, and the caller
- + * responsible for this recursion is in danger of seeing a
- + * call to TclInitSubsystems() return even though initialization
- + * is not complete. Consider adding a panic here, at least in
- + * some build configurations to hunt down and eliminate such
- + * things.
- + */
- +#if INITDEBUG
- + Tcl_Panic("recursion in TclInitSubsystems");
- +#endif
- + return;
- }
- + /*
- + * A change of state is in progress in another thread. Make
- + * sure we wait for it to finish before trying again.
- + */
- + TclpInitLock();
- TclpInitUnlock();
- + goto start;
- }
- + done:
- TclInitNotifier();
- }
- ^L
- /*
- *----------------------------------------------------------------------
- @@ -1094,15 +1131,21 @@
- * Invoke exit handlers first.
- */
- InvokeExitHandlers();
- - TclpInitLock();
- - if (subsystemsInitialized == 0) {
- + start:
- + if (initState == DOWN) {
- goto alreadyFinalized;
- }
- - subsystemsInitialized = 0;
- +
- + if (initState == UP) {
- +
- + TclpInitLock();
- + if (initState == UP) {
- + initState = CHANGING;
- + changer = Tcl_GetCurrentThread();
- /*
- * Ensure the thread-specific data is initialised as it is used in
- * Tcl_FinalizeThread()
- */
- @@ -1249,10 +1292,26 @@
- /*
- * At this point, there should no longer be any ckalloc'ed memory.
- */
- TclFinalizeMemorySubsystem();
- +
- + initState = DOWN;
- + changer = NULL;
- + }
- + TclpInitUnlock();
- + } else {
- + if (changer == Tcl_GetCurrentThread()) {
- +#if INITDEBUG
- + Tcl_Panic("recursion in Tcl_Finalize()");
- +#endif
- + return;
- + }
- + TclpInitLock();
- + TclpInitUnlock();
- + goto start;
- + }
- alreadyFinalized:
- TclFinalizeLock();
- }
- ^L