From: SourceForge.net <no...@so...> - 2010-07-06 22:42:01
|
Bugs item #3026061, was opened at 2010-07-07 02:42 Message generated for change (Tracker Item Submitted) made by a_kovalenko You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=110894&aid=3026061&group_id=10894 Please note that this message will contain a full copy of the comment thread, including the initial issue submission, for this request, not just the latest update. Category: 80. Thread Package Group: None Status: Open Resolution: None Priority: 5 Private: No Submitted By: Anton Kovalenko (a_kovalenko) Assigned to: Zoran Vasiljevic (vasiljevic) Summary: threadCmd.c:Init() references an interp but not preserves it Initial Comment: This bug is a member of "rare segfaults on exit" family. However I've found a way to reproduce it reliably: #!/usr/bin/wish8.6 # The _wish_ interpreter is mandatory for reproducing the bug using this script: # the example below depends on Tk_MainEx() being called. package req Thread package req Tk # Create thread pool set tph [tpool::create] # Post a long-running job tpool::post -nowait -detached $tph "after 5000; [list thread::send -async [thread::id] {puts "foo bar, yep?"}];" # Make Tk/wish exit earlier than the job finishes after 3000 {destroy .} # And then it segfaults. What's going on? 1. On initial `package require Thread', threadCmd.c:Init() is called for the main interpreter. Interps for other threads are created with threadCmd.c:NewThread(), only the main one is not _created_ but _registered_ with Init(). NewThread() takes care to Tcl_Preserve/Tcl_Release the interp when appropriate, but Init() doesn't. Pointer to the main interp is now stored in tsdPtr, but there is no Tcl_Preserve call to prevent its destruction, nor Tcl_CallWhenDeleted handler to wipe the pointer when the interp is destroyed. BAD. 2. Thread pool is created and the job is posted, so a worker thread is created and NewThread is started in it. No problem here. 3. Before the job finishes, main window is destroyed, and Tk_MainEx is going to exit. 4. Tk_MainEx marks the main interp for deletion, then calls Tcl_Release on it, causing its actual deletion. From looking at Tk_MainEx I decided that it has the right to do what it does - that's why this bug is submitted against Thread package, not Tk. 4.1. At this point, tsdPtr of thread package has a dangling reference to a corpse of main interp. BAD. 5. Then Tk_MainEx calls Tcl_Exit(0), which in turn invokes application exit handlers. 6. ThreadPoolCmd.c:AppExitHandler() tries to destroy the thread pool gracefully. It causes a worker thread to exit and then waits for it, calling Tcl_DoOneEvent() repeatedly until there is no workers. 7. But, _before_ the worker thread exits, it posts an event to the main thread, asking it to eval "puts..." within its main interp (that doesn't exist). 8. threadCmd.c:ThreadEventProc() retrieves a dangling interp pointer from main thread's tsd. 9. When it comes to Tcl_ResetResult(dead interp), wish segfaults. A possible solution, not nessesary the best one, is to add a couple of lines into threadCmd.c:Init(): Tcl_Preserve(interp); Tcl_CreateThreadExitHandler(Tcl_Release, interp); Another possible solution is to assign NULL to tsdPtr->interp when interp is deleted (Tcl_CallWhenDeleted). ---------------------------------------------------------------------- You can respond by visiting: https://sourceforge.net/tracker/?func=detail&atid=110894&aid=3026061&group_id=10894 |