I've written a patch for Itcl that makes it possible to use Itcl
objects in multiple threads. It is possible to create an object e.g. in
the main thread (using the -ts/-threadshared option) and call methods
on it in a different thread.
The patch for Itcl and also one for Itk can be downloaded at
http://e-lehmann.de/?page_id=31, I wrapped up the sources and created
binaries for Linux and windows as well, and created a Wiki entry at
http://wiki.tcl.tk/16140.
Please report bugs, if any, directly to me. If it is interresting to
have the patch included in the official release, I can submit it to the
official patch submitting page at sourceforge.
Eckhard
> Hi all,
>
> I've written a patch for Itcl that makes it possible to use Itcl
> objects in multiple threads. It is possible to create an object e.g. in
> the main thread (using the -ts/-threadshared option) and call methods
> on it in a different thread.
Sounds really interesting! Especially when using things like AOLserver or
writing other multithreaded servers.
> The patch for Itcl and also one for Itk can be downloaded at
> http://e-lehmann.de/?page_id=31, I wrapped up the sources and created
> binaries for Linux and windows as well, and created a Wiki entry at
> http://wiki.tcl.tk/16140.
I'm wondering about one thing, though. The main drawback should be that
you need to either deepcopy the Tcl_Obj or convert to/from strings. Both
seem very time consuming to me.
From what I remember with ns_cache from AOLserver, Tcl_Obj cannot be
shared between threads. So if this is true, your code will probably fail
when exiting Tcl or deleting the object, probably in a more complex
scenario.
Also, how does it handle the following code:
namespace eval ::myns {}
A -threadshared ::myns::obj0
I would assume you may need to create the parent namespace in each other
thread.
--
WK
> Sounds really interesting! Especially when using things like AOLserver or
> writing other multithreaded servers.
I am glad to hear this ;)
> I'm wondering about one thing, though. The main drawback should be that
> you need to either deepcopy the Tcl_Obj or convert to/from strings. Both
> seem very time consuming to me.
No, I didn't do it that way, exactly because deep copies are not easily
possible. (think of objects that contain objects as attributes etc...).
There is an ItclObject* structure representing each Itcl object - and
this one is shared between threads. When the '-threadshared' flag is
set, there is an access command for the object created in *each*
thread's interpreter - that's all. These access commands refer to the
same ItclObject* structure (so syncrhronization is really necessary
here, see http://wiki.tcl.tk/16140 near the bottom).
> From what I remember with ns_cache from AOLserver, Tcl_Obj cannot be
> shared between threads. So if this is true, your code will probably fail
> when exiting Tcl or deleting the object, probably in a more complex
> scenario.
Object deletion and thread exiting was indeed a tricky point and I
sometimes really racked my brain over debugging various scenarios
involving this. But finally I made it and hopefully it works forever
now ;-). Let me know if not...
Tcl_Obj*'s are not shared as such. The ItclObject* structure is shared,
which has nothing to do with Tcl_Obj*. The only place where Tcl_Obj*
comes into play is, when the access commands are transferred via
tsv::get/tsv::set - and then they are just strings.
I started some kind of Tcl_Obj* representation for Itcl objects, but it
is rather useless at the moment. The code for this (in
generic/itcl_objtype.c) could be removed together with it's
dependencies and nothing would happen.
> Also, how does it handle the following code:
>
> namespace eval ::myns {}
> A -threadshared ::myns::obj0
>
> I would assume you may need to create the parent namespace in each other
> thread.
As I've seen from Tcl_Create[Obj]Command(), the namespace is
automatically created if it does not exist.
The current patch has no regression tests for this case, but I just
extended the test suite with two tests - it seems to work correctly.
Eckhard
Actually, they are possible.
Please read: http://wiki.tcl.tk/3881
For showing the error with your Threaded Itcl: http://wiki.tcl.tk/16156
> There is an ItclObject* structure representing each Itcl object - and
> this one is shared between threads. When the '-threadshared' flag is
> set, there is an access command for the object created in *each*
> thread's interpreter - that's all. These access commands refer to the
> same ItclObject* structure (so syncrhronization is really necessary
> here, see http://wiki.tcl.tk/16140 near the bottom).
I meant object's variables.
The issue is that Tcl_Obj gets shared across threads. If thread A
creates a list and it gets modified by thread B (not in my example, but
it would also hurt, probably), weird things might happen.
This is because Tcl uses thread local variables to store things.
> > From what I remember with ns_cache from AOLserver, Tcl_Obj cannot be
> > shared between threads. So if this is true, your code will probably
> fail
> > when exiting Tcl or deleting the object, probably in a more complex
> > scenario.
> Object deletion and thread exiting was indeed a tricky point and I
> sometimes really racked my brain over debugging various scenarios
> involving this. But finally I made it and hopefully it works forever
> now ;-). Let me know if not...
It does not.
> Tcl_Obj*'s are not shared as such. The ItclObject* structure is shared,
> which has nothing to do with Tcl_Obj*. The only place where Tcl_Obj*
> comes into play is, when the access commands are transferred via
> tsv::get/tsv::set - and then they are just strings.
> I started some kind of Tcl_Obj* representation for Itcl objects, but it
> is rather useless at the moment. The code for this (in
> generic/itcl_objtype.c) could be removed together with it's
> dependencies and nothing would happen.
They are. Unless it can only work with using tsv::* instead of object
variables.
> > Also, how does it handle the following code:
> >
> > namespace eval ::myns {}
> > A -threadshared ::myns::obj0
> >
> > I would assume you may need to create the parent namespace in each
> other
> > thread.
> As I've seen from Tcl_Create[Obj]Command(), the namespace is
> automatically created if it does not exist.
> The current patch has no regression tests for this case, but I just
> extended the test suite with two tests - it seems to work correctly.
I believe it doesn't. That's why many extensions do Tcl_Eval(...,
"namespace eval ::namespace {}") before calling Tcl_Create*Command(). I
didn't check it, though.
--
WK
But that would mean that I need to make a deep copy of every object
variable...
>
> For showing the error with your Threaded Itcl: http://wiki.tcl.tk/16156
I updated the site. If you provide the -wait flag to thread::release,
there are no segfaults. Anyway I will have a gdb session again... If
the Tcl_Obj* variables are indeed the problem, IncrRefCount might help?
> I meant object's variables.
>
> The issue is that Tcl_Obj gets shared across threads. If thread A
> creates a list and it gets modified by thread B (not in my example, but
> it would also hurt, probably), weird things might happen.
>
> This is because Tcl uses thread local variables to store things.
Actually, the Thread extension makes deep copies of Tcl_Obj*. I thought
it is just because the tsv::* shared variables need to be available in
other thread's interp and there is no other reason. Would be nice if
someone could enlighten me...
> I believe it doesn't. That's why many extensions do Tcl_Eval(...,
> "namespace eval ::namespace {}") before calling Tcl_Create*Command(). I
> didn't check it, though.
I know it does - for the C API function (CREATE_NS_IF_UNKNOWN flag to
TclGetNamespaceForQualName in generic/tclBasic.c:1621). It might not
for [proc].
Eckhard
Unfortunately yes, this is what you would need to do - and this is why it
makes more sense to make a worker thread or worker threads pool.
>> For showing the error with your Threaded Itcl: http://wiki.tcl.tk/16156
> I updated the site. If you provide the -wait flag to thread::release,
> there are no segfaults. Anyway I will have a gdb session again... If
> the Tcl_Obj* variables are indeed the problem, IncrRefCount might help?
Interesting. I know there were issues with list objects and some data
bound to thread local storages. But that was in 8.3. There can also be
bytecode objects. Some Tk specific objects might also not work across
threads.
I don't want to discourage you in any way, but I'm afraid Tcl_Obj and
threads may not mix well. But I'd be glad to see it work and I'd love to
be wrong!
> Actually, the Thread extension makes deep copies of Tcl_Obj*. I thought
> it is just because the tsv::* shared variables need to be available in
> other thread's interp and there is no other reason. Would be nice if
> someone could enlighten me...
I'm afraid the reason is Tcl_Obj can't be shared across threads.
--
WK
This is not a practicable solution at all, in my opinion. It would mean
to make these deep copies *everytime* when the object changes state, to
reflect the new state in every thread. This is an horrendous big
overhead... I'd rather think about making Tcl_Obj* thread save. In the
end, every multithreaded application has its shared memory with shared
variables - that is the sense of multi threading: light weight
processes with shared memory.
> > I updated the site. If you provide the -wait flag to thread::release,
> > there are no segfaults. Anyway I will have a gdb session again... If
> > the Tcl_Obj* variables are indeed the problem, IncrRefCount might help?
>
> Interesting. I know there were issues with list objects and some data
> bound to thread local storages. But that was in 8.3. There can also be
> bytecode objects. Some Tk specific objects might also not work across
> threads.
I left Tk completely outside. Most people advise to have all Tk related
stuff only in one thread anyway... so I don't consider it as an issue
yet.
> I don't want to discourage you in any way, but I'm afraid Tcl_Obj and
> threads may not mix well. But I'd be glad to see it work and I'd love to
> be wrong!
It would be good to have more in depth information on this from TCT
members who know the Tcl code better than I.
In the end I think, the most efficient way is to make Tcl_Obj* thread
save if they are not, rather than building workaround solutions (deep
copies in this case). Other programs and extensions would benefit from
this as well...
Eckhard
With Tcl's "copy-on-write" rule for Tcl_Obj sharing, the only time
a Tcl_Obj can "change state" is when it is unshared. An unshared
Tcl_Obj would have only one reference which could only live in one
thread. Does that help?
--
| Don Porter Mathematical and Computational Sciences Division |
| donald...@nist.gov Information Technology Laboratory |
| http://math.nist.gov/~DPorter/ NIST |
|______________________________________________________________________|
> With Tcl's "copy-on-write" rule for Tcl_Obj sharing, the only time
> a Tcl_Obj can "change state" is when it is unshared. An unshared
> Tcl_Obj would have only one reference which could only live in one
> thread. Does that help?
Do you mean "shared" in terms of thread shared or in terms of ref
counts? Is it possible to have Tcl_Obj shared between threads without
deep-copying them to each thread's interpreter, I mean just by
interchanging references?
Eckhard
What I'm trying to get across is that concern that a Tcl_Obj
might "change state", and how to react when that happens, is
misplaced.
Tcl_Obj's are values, not data containers. They do not
"change state" any more than "2" changes state.
Ok, I didn't mean Tcl_Obj with my "change state" statement - I meant
Itcl objects which are data containers that contain Tcl_Obj as
attribute variables.
Eckhard