I've run into an interaction with threads and the XOTcl exit handler that I don't understand.
I create a thread from within an object, and release that thread in the object's destructor. If I destroy the object directly, everything's fine, but if I destroy the object through "Object setExitHandler", I get an error. Am I treading into undefined behavior? Is there well-defined way to shut down multiple threads?
Scott
Here's some sample code:
package require XOTcl package require Thread
namespace import {xotcl::*} Class A A instproc init {} { my set tid [thread::create [subst -nocommands { set ::auto_path [list $::auto_path] package require XOTcl namespace import {xotcl::*} Object b thread::wait }]] }
A instproc destroy {args} { thread::send [my set tid] {b destroy} thread::release [my set tid] next }
A a if {0} { # This gives an error xotcl::Object setExitHandler {a destroy} } else { # this works a destroy }
exit
Scott Gargash schrieb:
I've run into an interaction with threads and the XOTcl exit handler that I don't understand.
I create a thread from within an object, and release that thread in the object's destructor. If I destroy the object directly, everything's fine, but if I destroy the object through "Object setExitHandler", I get an error. Am I treading into undefined behavior? Is there well-defined way to shut down multiple threads?
the thread and exit code is a tricky and sensitive area, especially if it is supposed to work with tcl and the aolserver. below is the somewhat simplified code (btw., your example works on mac os x without a problem). In general. there is no need to destroy the objects by hand, since these are destroyed automatically and the appropriate destroy methods are executed. also, the destroy order is not completely trivial in order to be able to execute the destroy methods correctly. i would not do this by hand.
isn't the default beavior sufficient? it prints:
::a created thread waits exithandler ::a destroyed
-gustaf
package require XOTcl package require Thread
namespace import {xotcl::*} Class A A instproc init {} { puts stderr "[self] created" my set tid [thread::create [subst -nocommands { set ::auto_path [list $::auto_path] package require XOTcl namespace import {xotcl::*} Object b puts stderr "thread waits" thread::wait }]] }
A instproc destroy {args} { puts stderr "[self] destroyed" thread::send [my set tid] {b destroy} thread::release [my set tid] next }
A a xotcl::Object setExitHandler {puts stderr exithandler}
Scott Gargash schrieb:
I've run into an interaction with threads and the XOTcl exit handler that I don't understand.
I create a thread from within an object, and release that thread in the object's destructor. If I destroy the object directly, everything's fine, but if I destroy the object through "Object setExitHandler", I get an error. Am I treading into undefined behavior? Is there well-defined way to shut down multiple threads?
the thread and exit code is a tricky and sensitive area, especially if it is supposed to work with tcl and the aolserver. below is the somewhat simplified code (btw., your example works on mac os x without a problem). In general. there is no need to destroy the objects by hand, since these are destroyed automatically and the appropriate destroy methods are executed. also, the destroy order is not completely trivial in order to be able to execute the destroy methods correctly. i would not do this by hand.
isn't the default beavior sufficient?
Unfortunately, the default behavior is not sufficient. My example was simplified from my real problem. In the real problem, I have essentially a directed graph of objects that I serialize out upon destruction. Since the default behavior doesn't destroy objects in a defined order, when the root object goes to serialize out, some nodes that it needs to serialize may already be destroyed. This is undesireable.
So I inserted my own exitHandler and that works in a single thread, but when I have multiple threads, things get confused.
BTW, the failure was on WinXP. Given that the behavior is different on different platforms, it does seem like I've treaded into undefined behavior. Is there any way to wrangle it back into something defined?
Scott
Scott Gargash schrieb:
Unfortunately, the default behavior is not sufficient. My example was simplified from my real problem. In the real problem, I have essentially a directed graph of objects that I serialize out upon destruction. Since the default behavior doesn't destroy objects in a defined order, when the root object goes to serialize out, some nodes that it needs to serialize may already be destroyed. This is undesireable.
one has to differantiate here more in detail. There is a defined order, otherwise xotcl would horribly crash (referencing freed memory). The shutdown happens in two rounds.
a) The first round is the "soft" destroy, were all destroy methods are called. In this state calling destroy does not make a physical destruction, no memory is freed. First destroy is called for objects (making sure it is not called twice, then for all classes. The more detailed order depends on the hash tables and is therefore undefined. b) after this, gobal xotcl-allocated variables and structures are freed. the second round is the phyiscal destroy, where memory is actually freed. first all objects not being classes and not having children are deleted until none of these exist. Then classes except ::xotcl::Object and ::xotcl::Class are freed, that have no subclasses, no instances (for metaclasses), and no children are deleted (in multiple rounds). finally ::xotcl::Object and ::xotcl::Class are freed. Finally the global namespaces are freed and the destruction in is finished.
Before (a) and (b) the user exit handler is called. there a user is free to define his deletion logic.
So I inserted my own exitHandler and that works in a single thread, but when I have multiple threads, things get confused.
You have not told us, what "confused" means (other than: "i get an error") since your example works for me.
The shutown is differnt in the threaded case, since also tcl has and needs different mechanisms here. in the single threaded case is only one exit handler, in the multi threaded case is as well an thread exit handler (in xotcl, all threads can have their own exit handler, defined by setExitHandler).
If you have multiple threads, and you want to control the order, in which the threads exit (and in which their exit handlers are called), you have to do this on your own as well (not using "exit", but your own shutdown command). Be aware of, that tcl tries to shutdown your threads on exit as well.
BTW, the failure was on WinXP. Given that the behavior is different on different platforms, it does seem like I've treaded into undefined behavior. Is there any way to wrangle it back into something defined?
i am not using windows. it is certainly possible that there is a different behavior on thread exit, but first one has to understand, what the problem in your case is....
best regards -gustaf neumann
Scott
Gustaf Neumann neumann@wu-wien.ac.at wrote on 08/16/2006 03:06:18 AM:
Scott Gargash schrieb:
So I inserted my own exitHandler and that works in a single thread, but when I have multiple threads, things get confused.
You have not told us, what "confused" means (other than: "i get an error") since your example works for me.
Sorry, I omitted that. My example gives the following error:
Error from thread tid00000C20 invalid command name "tclPkgUnknown" while executing "tclPkgUnknown XOTcl {}" ("package unknown" script) invoked from within "package require XOTcl" User defined exit handler contains errors! Error in line 2: target thread died Execution interrupted.
My actual code doesn't give any consistent behavior. Sometimes it sort of works, sometimes it generates corrupt data, and sometimes it dumps core.
If you have multiple threads, and you want to control the order, in which the threads exit (and in which their exit handlers are called), you have to do this on your own as well (not using "exit", but your own shutdown command). Be aware of, that tcl tries to shutdown your threads on exit as well.
A ha, you've describe my problem (and also the solution I found). I have exit handlers in all the threads, and I get inconsistent behavior. I wound up overriding exit to get things to exit in a defined manner.
It was in the process of trying to reduce it down to a simple test case that I found the problem (described above) on WinXP with just a single exit handler. Now, perhaps that's an real problem of some sort also, but it sounds like I need to continue overriding "exit" to get the desired behavior in the real codebase.
Scott