Posted to tcl by dgp at Fri Apr 26 14:04:00 GMT 2013view raw

  1. Index: generic/tclEvent.c
  2. ==================================================================
  3. --- generic/tclEvent.c
  4. +++ generic/tclEvent.c
  5. @@ -79,12 +79,10 @@
  6. * in closing of files and pipes.
  7. */
  8.  
  9. static int inExit = 0;
  10.  
  11. -static int subsystemsInitialized = 0;
  12. -
  13. /*
  14. * This variable contains the application wide exit handler. It will be called
  15. * by Tcl_Exit instead of the C-runtime exit if this variable is set to a
  16. * non-NULL value.
  17. */
  18. @@ -1013,31 +1011,40 @@
  19. * Varied, see the respective initialization routines.
  20. *
  21. *-------------------------------------------------------------------------
  22. */
  23.  
  24. +#define INITDEBUG 1
  25. +typedef enum {
  26. + DOWN, CHANGING, UP
  27. +} State;
  28. +
  29. +static State initState = DOWN;
  30. +static Tcl_ThreadId changer = NULL;
  31. +
  32. void
  33. TclInitSubsystems(void)
  34. {
  35. - if (inExit != 0) {
  36. - Tcl_Panic("TclInitSubsystems called while exiting");
  37. + start:
  38. + if (initState == UP) {
  39. + goto done;
  40. }
  41.  
  42. - if (subsystemsInitialized == 0) {
  43. + if (initState == DOWN) {
  44. /*
  45. - * Double check inside the mutex. There are definitly calls back into
  46. - * this routine from some of the functions below.
  47. + * Enter the race to become the thread to change initState from
  48. + * DOWN to UP.
  49. */
  50.  
  51. TclpInitLock();
  52. - if (subsystemsInitialized == 0) {
  53. - /*
  54. - * Have to set this bit here to avoid deadlock with the routines
  55. - * below us that call into TclInitSubsystems.
  56. - */
  57.  
  58. - subsystemsInitialized = 1;
  59. + /* Did we win? */
  60. + if (initState == DOWN) {
  61. +
  62. + /* Yes! Record that we are CHANGING the state */
  63. + initState = CHANGING;
  64. + changer = Tcl_GetCurrentThread();
  65.  
  66. /*
  67. * Initialize locks used by the memory allocators before anything
  68. * interesting happens so we can use the allocators in the
  69. * implementation of self-initializing locks.
  70. @@ -1059,13 +1066,43 @@
  71. * mutexes. */
  72. TclInitIOSubsystem(); /* Inits a tsd key (noop). */
  73. TclInitEncodingSubsystem(); /* Process wide encoding init. */
  74. TclpSetInterfaces();
  75. TclInitNamespaceSubsystem();/* Register ns obj type (mutexed). */
  76. +
  77. + /* Record that the state is now UP */
  78. + initState = UP;
  79. + changer = NULL;
  80. + }
  81. + TclpInitUnlock();
  82. + } else {
  83. + /* initState != DOWN */
  84. + if (changer == Tcl_GetCurrentThread()) {
  85. + /*
  86. + * We're the thread changing state and must have reached here via
  87. + * a recursive call. Return as a no-op to avoid deadlock.
  88. + * NOTE: This really should not be happening, and the caller
  89. + * responsible for this recursion is in danger of seeing a
  90. + * call to TclInitSubsystems() return even though initialization
  91. + * is not complete. Consider adding a panic here, at least in
  92. + * some build configurations to hunt down and eliminate such
  93. + * things.
  94. + */
  95. +#if INITDEBUG
  96. + Tcl_Panic("recursion in TclInitSubsystems");
  97. +#endif
  98. + return;
  99. }
  100. + /*
  101. + * A change of state is in progress in another thread. Make
  102. + * sure we wait for it to finish before trying again.
  103. + */
  104. + TclpInitLock();
  105. TclpInitUnlock();
  106. + goto start;
  107. }
  108. + done:
  109. TclInitNotifier();
  110. }
  111. ^L
  112. /*
  113. *----------------------------------------------------------------------
  114. @@ -1094,15 +1131,21 @@
  115. * Invoke exit handlers first.
  116. */
  117.  
  118. InvokeExitHandlers();
  119.  
  120. - TclpInitLock();
  121. - if (subsystemsInitialized == 0) {
  122. + start:
  123. + if (initState == DOWN) {
  124. goto alreadyFinalized;
  125. }
  126. - subsystemsInitialized = 0;
  127. +
  128. + if (initState == UP) {
  129. +
  130. + TclpInitLock();
  131. + if (initState == UP) {
  132. + initState = CHANGING;
  133. + changer = Tcl_GetCurrentThread();
  134.  
  135. /*
  136. * Ensure the thread-specific data is initialised as it is used in
  137. * Tcl_FinalizeThread()
  138. */
  139. @@ -1249,10 +1292,26 @@
  140. /*
  141. * At this point, there should no longer be any ckalloc'ed memory.
  142. */
  143.  
  144. TclFinalizeMemorySubsystem();
  145. +
  146. + initState = DOWN;
  147. + changer = NULL;
  148. + }
  149. + TclpInitUnlock();
  150. + } else {
  151. + if (changer == Tcl_GetCurrentThread()) {
  152. +#if INITDEBUG
  153. + Tcl_Panic("recursion in Tcl_Finalize()");
  154. +#endif
  155. + return;
  156. + }
  157. + TclpInitLock();
  158. + TclpInitUnlock();
  159. + goto start;
  160. + }
  161.  
  162. alreadyFinalized:
  163. TclFinalizeLock();
  164. }
  165. ^L
  166.  
  167.