sorry for delay.

> The main problem is that the Tcl 'trace' command don't store the
> callback object that is passed in.  Instead it just copies the
> stringified version and then drop the object.  This is a problem when
> we pass in a "perlsub" object wrapping an anynumous function.  We will
> end up garbage collecting the function before it is called by trace.

it is delicate matter indeed: not creating same sub second time, and, even
more importantly, do not allow reference to be freed before its time.

with %anon_refs it is all obvious: it is never freed because it is within
the hash;

> 
> This problem show for Tcl::Tk because it tries to detect when the
> mainwindow is deleted with this code:
> 
>     $i->call('trace', 'add', 'command', '.', 'delete',
>          sub { for (keys %W) {$W{$_}->{$mwid} = undef; }});
> 
> Since this does not work, the MainLoop will not terminate when the
> main window is deleted.  The result is that all the tests hang.  A
> workaround to make the tests run is to assign this callback to some
> global variable that keeps it alive even if Tcl will not.
> 
> With this workaround all tests pass, but we get a problem with the
> "Tcl::Var" destructor in one of the tests during perl's global
> destruction.  What seems to be happening is that the interpreter is
> freed before all the variables resulting in a core dump where Tcl
> prints "called Tcl_FindHashEntry on deleted table" and dies with this
> stack trace:

destruction order.

It comes to mind for each interpreter to keep track for perl references
created for it.

Okay, storing references within %anon_refs isn't good, as it interferes with
refcounting.
But we may store there Tcl-side names, (strings but not refs to variable
itself).

This way, each time a reference goes out, it will delete its name from that
hash at sub DESTROY time, and interpreter destruction should check whether
that hash is empty, and somehow manage for all variables to be deleted
before him.
In this case we could think of race condition: what if interpreter goes out
of scope, but its dependant variables Tcl::Var are not (have many
refcounts)? But, as long as Tcl::Var are anyways useless without
interpreter, we can "untie" all them 

> 
>     #0  0xb7d7c941 in kill () from /lib/libc.so.6
>     #1  0xb7e6dc6d in pthread_kill () from /lib/libpthread.so.0
>     #2  0xb7e6dfc1 in raise () from /lib/libpthread.so.0
>     #3  0xb7d7c6f4 in raise () from /lib/libc.so.6
>     #4  0xb7d7da66 in abort () from /lib/libc.so.6
>     #5  0xb7cf5731 in Tcl_PanicVA () from 
> /opt/ActiveTcl/8.4.9.1.139183/lib/libtcl8.4.so
>     #6  0xb7cf5763 in Tcl_Panic () from 
> /opt/ActiveTcl/8.4.9.1.139183/lib/libtcl8.4.so
>     #7  0xb7cdb092 in BogusFind () from 
> /opt/ActiveTcl/8.4.9.1.139183/lib/libtcl8.4.so
>     #8  0xb7cf10ce in TclGetNamespaceForQualName () from 
> /opt/ActiveTcl/8.4.9.1.139183/lib/libtcl8.4.so
>     #9  0xb7cf15be in Tcl_FindNamespaceVar () from 
> /opt/ActiveTcl/8.4.9.1.139183/lib/libtcl8.4.so
>     #10 0xb7d08929 in TclLookupSimpleVar () from 
> /opt/ActiveTcl/8.4.9.1.139183/lib/libtcl8.4.so
>     #11 0xb7d08674 in TclObjLookupVar () from 
> /opt/ActiveTcl/8.4.9.1.139183/lib/libtcl8.4.so
>     #12 0xb7d098d8 in TclObjUnsetVar2 () from 
> /opt/ActiveTcl/8.4.9.1.139183/lib/libtcl8.4.so
>     #13 0xb7d09829 in Tcl_UnsetVar2 () from 
> /opt/ActiveTcl/8.4.9.1.139183/lib/libtcl8.4.so
>     #14 0xb7f33cd2 in XS_Tcl_UnsetVar () from 
> /opt/perl/ap813/lib/site_perl/5.8.7/i686-linux-thread-multi/au
> to/Tcl/Tcl.so
>     #15 0x080b533c in Perl_pp_entersub ()
>     #16 0x080aeba1 in Perl_runops_standard ()
>     #17 0x08063ff1 in S_call_body ()
>     #18 0x08063d97 in Perl_call_sv ()
>     #19 0x080bc2b2 in Perl_sv_clear ()
>     #20 0x080bc91e in Perl_sv_free ()
>     #21 0x080bc630 in Perl_sv_clear ()
>     #22 0x080bc91e in Perl_sv_free ()
>     #23 0x080a4318 in Perl_mg_free ()
>     #24 0x080bc420 in Perl_sv_clear ()
> 
>     [...]
> 
>     #45 0x080bc542 in Perl_sv_clear ()
>     #46 0x080bc91e in Perl_sv_free ()
>     #47 0x080b615e in do_clean_objs ()
>     #48 0x080b60d2 in S_visit ()
>     #49 0x080b6202 in Perl_sv_clean_objs ()
>     #50 0x08061053 in perl_destruct ()
>     #51 0x0805ff3a in main ()
> 
> We might be able to avoid this problem with maintaining various flags
> that make us skip variable deletion during this phase, but I don't
> know any good way (besides patching Tcl) to deal with the 'trace'
> problem.  There might also be other places where Tcl commands taking
> callback arguments only keep a copy of the stringified version.

I think 'trace' problem isn't that essential, if it only touches few lines
in Tcl::Tk (we'll fix them there); do you have an idea when user can step
into this problem?

I am sorry to provide only vague information right now, but it looks like it
is not a reference count problem that is happening here. Or - reference
counting scheme is now okay, but another kind of problem is introduced.

I tried application with screenshot at
http://vkonovalov.ru/cgi-bin/perl-tcltk-wiki.cgi/42
It appears that it starts okay, but misbehaves when I press button "Go": I
get two error messages simultaneously: message about unreferenced scalar and
"not a CODE reference" for my callback. This does not go away even when I
quickly re-introduce %anon_refs hash... Looks like PerlSubObj is failing
somewhere somehow.

I'll try reducing a problem to a few lines, within this week.

Anyway, thank you again for your code, it looks like it is moving in a very
right direcion.

PS. some char * var_trace(...) function in Tcl.xs survived many CPAN
releases... of course its true place is right in the middle of nowhere and
not in Tcl.xs :)

Reply via email to