Compare commits

...
Author SHA1 Message Date
jan.nijtmans 4a3fe30a06 minor bug: Don't TclpInitUnlock() here, because the following TclFinalizeLock() already does an implicit unlock. 2013-06-19 08:56:28 +00:00
dgp 40604a5152 Make the (cosmetic) change to 4-state system, separating RISING and FALLING. 2013-06-18 15:45:37 +00:00
dgp a7342b6aab merge 8.5 2013-06-18 13:58:13 +00:00
dgp 35c47e91a9 Merge to near-8.5-branch-tip. 2013-06-18 13:27:55 +00:00
dgp 8b2d677575 Revisions to the multi-thread arrangments of TclInitSubsystems().
With these changes, the caller of TclInitSubsystems() can be sure that
at some point after the TIS() call is begun and before it returned, the Tcl
library became fully initialized.  Without these revisions, that cannot be
guaranteed, TIS() can return with Tcl library initialization having never
been finished.

Even with these revisions, the caller cannot be sure the library remains
initialized.  Any thread might call Tcl_Finalize() at any time.  That's a
deeper problem which this patch does not attempt to address.
2013-04-26 19:02:31 +00:00
+76 -14
View File
@@ -81,8 +81,6 @@ TCL_DECLARE_MUTEX(exitMutex)
*/
static int inFinalize = 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
@@ -934,23 +932,38 @@ Tcl_Exit(
*-------------------------------------------------------------------------
*/
#define INITDEBUG 1
typedef enum {
DOWN, RISING, FALLING, UP
} State;
static State initState = DOWN;
static Tcl_ThreadId changer = NULL;
void
TclInitSubsystems(void)
{
if (inFinalize != 0) {
Tcl_Panic("TclInitSubsystems called while finalizing");
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) {
/*
/* Did we win? */
if (initState == DOWN) {
/* Yes! Record that we are bring the state up */
initState = RISING;
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.
@@ -974,10 +987,39 @@ TclInitSubsystems(void)
TclInitEncodingSubsystem(); /* Process wide encoding init. */
TclpSetInterfaces();
TclInitNamespaceSubsystem();/* Register ns obj type (mutexed). */
subsystemsInitialized = 1;
/* 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();
}
@@ -1026,11 +1068,17 @@ Tcl_Finalize(void)
firstExitPtr = NULL;
Tcl_MutexUnlock(&exitMutex);
TclpInitLock();
if (subsystemsInitialized == 0) {
start:
if (initState == DOWN) {
goto alreadyFinalized;
}
subsystemsInitialized = 0;
if (initState == UP) {
TclpInitLock();
if (initState == UP) {
initState = FALLING;
changer = Tcl_GetCurrentThread();
/*
* Ensure the thread-specific data is initialised as it is used in
@@ -1179,7 +1227,21 @@ Tcl_Finalize(void)
*/
TclFinalizeMemorySubsystem();
inFinalize = 0;
initState = DOWN;
changer = NULL;
}
} else {
if (changer == Tcl_GetCurrentThread()) {
#if INITDEBUG
Tcl_Panic("recursion in Tcl_Finalize()");
#endif
return;
}
TclpInitLock();
TclpInitUnlock();
goto start;
}
alreadyFinalized:
TclFinalizeLock();