Ticket UUID: | 858493 | |||
Title: | Win32: DllMain needs SEH around Tcl_Finalize() | |||
Type: | Patch | Version: | None | |
Submitter: | davygrvy | Created on: | 2003-12-11 20:03:31 | |
Subsystem: | 50. Embedding Support | Assigned To: | mdejong | |
Priority: | 5 Medium | Severity: | ||
Status: | Closed | Last Modified: | 2004-01-03 16:24:50 | |
Resolution: | Fixed | Closed By: | davygrvy | |
Closed on: | 2004-01-03 09:24:50 | |||
Description: |
If Tcl is being unloaded by the OS from Tcl_Panic's ExitProcess(), and Tcl_Panic was called from an __except block due to a crash, calling Tcl_Finalize is unsafe. IOW, DllMain should protect the call to Tcl_Finalize to ensure it won't throw an unhandled exception as we can't guarentee Tcl is always being unloaded from a stable condition. I found this experimenting with running Tcl from a __try block and calling Tcl_Panic in the __except handler: // enter Tcl's notifier and don't fall-out until the thread is // told to quit. // If a fatal exception happens, catch it and by setting the // async token to NULL, render QueueJob() non-functional. // __try { __try { while (!done) { // *ALL* of Tcl runs from this loop. Tcl_DoOneEvent (TCL_ALL_EVENTS); } Tcl_AsyncDelete(*token); Tcl_Finalize(); FreeLibrary(hTclMod); } __finally { *token = 0L; } } __except (EXCEPTION_EXECUTE_HANDLER) { .... Tcl_Panic("big crash"); } I couldn't place FreeLibrary in the __finally as it was causing exceptions itself from Tcl_Finalize in the DllMain. Yet, when my panicProc calls ExitProcess, the same thing happens. I added a change just yesterday to WishPanic() on the 84branch: __try { ExitProcess(EXIT_FAILURE); } __except (EXCEPTION_EXECUTE_HANDLER) { TerminateProcess(GetCurrentProcess(), EXIT_FAILURE); } but I think I need to back it out and address the problem here in Tcl. | |||
User Comments: |
davygrvy added on 2004-01-03 16:24:50:
Logged In: YES user_id=7549 done, thank you. mdejong added on 2003-12-26 11:17:26: Logged In: YES user_id=90858 Ho Ho Ho, the SEH ASM code changes have been committed to both Tcl and Tk on the HEAD. davygrvy added on 2003-12-22 07:09:27: Logged In: YES user_id=7549 TODO: get MinGW custom SEH assembly into both Tcl and Tk's DllMain() Merry XMas. davygrvy added on 2003-12-22 06:50:23: Logged In: YES user_id=7549 Committed Tk patch without MinGW assembly code. Problem for me is now solved and I can succesfully unload both tcl and tk from within ExitProcess() all within an exception handler. Life is good. No mods where made to WishPanic. davygrvy added on 2003-12-22 06:11:04: File Added - 71220: patch.txt davygrvy added on 2003-12-22 06:10:17: File Deleted - 70394: davygrvy added on 2003-12-22 05:06:18: Logged In: YES user_id=7549 took out the HAVE_NO_SEH stuff and committed to the HEAD so I can move this forward. davygrvy added on 2003-12-22 04:28:37: Logged In: YES user_id=7549 Mo, any comments on seh_dllmain_patch.txt? I need to commit it to the HEAD. I can take out the gcc asm stuff if it makes things easier for a later patch on top of this. davygrvy added on 2003-12-13 07:57:22: File Added - 70394: patch.txt davygrvy added on 2003-12-13 07:56:28: File Deleted - 70389: Logged In: YES user_id=7549 new Tk patch. more better. Where should the three new funcs go? I dropped them in generic/tkUtil.c, but generic/tkEvent.c would mirror Tcl a bit better. davygrvy added on 2003-12-13 06:47:06: File Added - 70389: patch.txt Logged In: YES user_id=7549 Attached a patch for Tk. I'm still having problem in DeleteWindowsExitProc(), but as it is now being called from within an SEH block no damage occurs, yet am I just masking it and being lazy? I don't know yet. Added three new functions: TkCreateExitHandler TkDeleteExitHandler TkFinalize They're in the private Stubs table, which was a mistake, they should be unstubbed, private, internal instead. They just apply a simple level of indirection so I can call TkFinalize from the Tk's DllMain() for DLL_PROCESS_DETACH and avoid the dangling pointer thing. davygrvy added on 2003-12-13 04:11:53: Logged In: YES user_id=7549 Note to Self: Exit handlers can not be guarenteed to get called from the same thread that created them. Any Tcl_ExitProc that accesses thread specific data are already broken and should be using the ClientData to deref tsdPtr instead. davygrvy added on 2003-12-12 17:21:13: Logged In: YES user_id=7549 Looks like Tk needs some attention, too. Its exit handler, DeleteWindowsExitProc, is called *AFTER* Tk has been unloaded by the OS under exception condition, or at least from the ExitProcess condition. No wonder is goes bang. This isn't surprising as DLLs are unloaded in reverse order they were loaded. If Tcl is calling exit handlers in other extensions, no wonder they crash, they've all been unloaded already.. They're all big dangling pointers.. Now things are getting interesting :) Should Tk have it's own Tk_CreateExitHandler so it can clean itself up from its DllMain given the OS is taking it down before Tcl? I think so. I'll scan the Tk source for more Tcl_CreateExitHandler calls to make sure this is the only one, and if so, just calling DeleteWindowsExitProc from DLL_PROCESS_DETACH should succeed along with a Tcl_DeleteExitHandler to remove it. That's the easy fix. Will study this some more.. A very interesting problem. davygrvy added on 2003-12-12 08:47:06: Logged In: YES user_id=7549 PS. ya know... It's becoming a common occurance for me to have problems in Tcl_Finalize. If we do wrap DllMain's call to Tcl_Finalize in SEH protection, I sure hope this won't mask programming errors other developers might create in their journeys of embedding. My intent is not to mask the normal route to Tcl_Finalize, but just DllMain's entry to it. Just thought I should mention this. davygrvy added on 2003-12-12 08:28:29: Logged In: YES user_id=7549 commited the win/tclWinSock.c change to the HEAD from below and reverted the WishPanic() change in win/winMain.c on the core-8-4-branch of Tk. Tcl_Finalize, although now not blocking in SocketThreadExitHandler, sometimes blows chunks in a Tk exit handler trying to destroy the tkCon window. I'll study it some more... davygrvy added on 2003-12-12 07:17:02: Logged In: YES user_id=7549 Agreed, but 8.5 has the Tcl_Namespace problem in the Stubs table and I find it too difficult to work with. Even if I did place the changes into the HEAD, I would be testing and running the code in the 8.4 branch only. I'm finding some deeper problem in some of the exit handlers. It looks like threads are halted and in SocketThreadExitHandler for example is blocking on: WaitForSingleObject(tsdPtr->socketThread, INFINITE); Although the thread appears to be running, it isn't. This solves it: dwWait = WaitForSingleObject(tsdPtr->socketThread, 100); if (dwWait == WAIT_TIMEOUT) { /* * Avoids a lock-up, just in case it is needed from an * unclean exit condition when the thread appears * running, but isn't. */ TerminateThread(tsdPtr->socketThread, EXIT_FAILURE); } dgp added on 2003-12-12 05:42:29: Logged In: YES user_id=80530 Just a general comment. I think that experiments like this are worthwhile, but best done on the CVS HEAD, leaving the core-8-4-branch stable with more tested code. davygrvy added on 2003-12-12 03:11:16: File Added - 70281: seh_dllmain_patch.txt Logged In: YES user_id=7549 Mo, could have a look at the patch for the HAVE_NO_SEH part. I copied from the others, but I don't if it works under cygwin, and probably could do much if it wasn't working correctly anyways. Then again, I'm not sure if this is the correct direction to stablize a Tcl_PanicProc. It seems more correct not to modify a Tcl_PanicProc (WishPanic) but to stabize the DllMain code so it can exit given an unstable exit. davygrvy added on 2003-12-12 03:03:32: File Added - 70280: bang.c |