void Tcl_Finalize() { ExitHandler *exitPtr; ThreadSpecificData *tsdPtr; TclpInitLock(); if (subsystemsInitialized != 0) { subsystemsInitialized = 0; tsdPtr = TCL_TSD_INIT(&dataKey); /* * Invoke exit handlers first. */ Tcl_MutexLock(&exitMutex); inFinalize = 1; for (exitPtr = firstExitPtr; exitPtr != NULL; exitPtr = firstExitPtr) { /* * Be careful to remove the handler from the list before * invoking its callback. This protects us against * double-freeing if the callback should call * Tcl_DeleteExitHandler on itself. */ firstExitPtr = exitPtr->nextPtr; Tcl_MutexUnlock(&exitMutex); (*exitPtr->proc)(exitPtr->clientData); ckfree((char *) exitPtr); Tcl_MutexLock(&exitMutex); } firstExitPtr = NULL; Tcl_MutexUnlock(&exitMutex); /* * Clean up after the current thread now, after exit handlers. * In particular, the testexithandler command sets up something * that writes to standard output, which gets closed. * Note that there is no thread-local storage after this call. */ Tcl_FinalizeThread(); /* * Now finalize the Tcl execution environment. Note that this * must be done after the exit handlers, because there are * order dependencies. */ TclFinalizeCompExecEnv(); TclFinalizeEnvironment(); TclFinalizeEncodingSubsystem(); if (tclExecutableName != NULL) { ckfree(tclExecutableName); tclExecutableName = NULL; } if (tclNativeExecutableName != NULL) { ckfree(tclNativeExecutableName); tclNativeExecutableName = NULL; } if (tclDefaultEncodingDir != NULL) { ckfree(tclDefaultEncodingDir); tclDefaultEncodingDir = NULL; } Tcl_SetPanicProc(NULL); // Added to avoid memeory leak TclFinalizeThreadData(); /* * Free synchronization objects. There really should only be one * thread alive at this moment. */ TclFinalizeSynchronization(); /* * We defer unloading of packages until very late * to avoid memory access issues. Both exit callbacks and * synchronization variables may be stored in packages. */ TclFinalizeLoad(); /* * There shouldn't be any malloc'ed memory after this. */ TclFinalizeMemorySubsystem(); inFinalize = 0; } TclpInitUnlock(); }