Tcl Source Code

View Ticket
Login
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

Attachments: