Tcl Source Code

View Ticket
Login
Ticket UUID: 9969cf8ca6fa749fa0952186957fd3ac110be5b3
Title: memleak in execute-10.3
Type: Bug Version: trunk
Submitter: dgp Created on: 2014-07-11 21:27:07
Subsystem: 47. Bytecode Compiler Assigned To: dgp
Priority: 5 Medium Severity: Minor
Status: Open Last Modified: 2014-07-21 14:34:59
Resolution: None Closed By: nobody
    Closed on:
Description:
==4219== 157 (96 direct, 61 indirect) bytes in 2 blocks are definitely lost in loss record 5 of 7
==4219==    at 0x4A0610C: malloc (vg_replace_malloc.c:195)
==4219==    by 0x5D14E2: TclpAlloc (tclAlloc.c:699)
==4219==    by 0x43D1AE: Tcl_Alloc (tclCkalloc.c:1059)
==4219==    by 0x571252: Tcl_NewStringObj (tclStringObj.c:340)
==4219==    by 0x516C69: TclGetSourceFromFrame (tclExecute.c:9735)
==4219==    by 0x4306B8: EvalObjvCore (tclBasic.c:4262)
==4219==    by 0x4309D8: TclNRRunCallbacks (tclBasic.c:4390)
==4219==    by 0x43027D: Tcl_EvalObjv (tclBasic.c:4121)
User Comments: dgp added on 2014-07-17 04:27:42:
Whoa.  This is either a stroke of dumb luck, or a consequence
of well-crafter Sofer design.

I found where the ->rewind bails out on coroutine deletion
and saw that teardown of trace data in frames comes right
after.  The leak appears to be a consequence of just this 
small misordering of code.  Fix committed.

The interaction of execution traces and [yield]ing commands
still holds some mysteries I think, but I'll open another ticket
for that, and keep this one open until I do.

Leak plugged.  And there was much rejoicing...

dgp added on 2014-07-17 03:51:45:
Simplest demo I can manage of how things are functioning now:

% trace add execution yield {enter leave} {apply {{cmd args} {puts $args}}}
% coroutine coro yield
enter
% coro
0 {} leave

I can convince myself that's correct.

dgp added on 2014-07-17 03:41:34:
Oh boy.

Test execute-10.3 came in to test that a crashing
scenario in Ticket 3072640 stopped crashing.

The leak here appears to be exposing that although
things are not crashing, they don't seem to be working
properly either.

The issue appears to be how to handle execution leave
traces for the [yield] command or for other commands
like [for] or [while] that can end up yielding due to
[yield] in their bodies.  When these commands yield,
in some sense they are blocking, they are still in progress,
and we may sometime later return to them and let them
complete and leave at that time.  So their leave traces
do not fire *yet*.

Execution trace implementation creates things for enter
traces and destroys them after leave traces.  That's
why we see a leak.  Those commands that never leave,
never clean up their execution trace housekeeping.

What's strange is that after the [yield], the execution
trace appears to still be in effect, now tracing a whole
other body of code.

In the test execute-8.3, a bit of debug printing will
reveal that the command

::t {trace remove execution ::generate enterstep ::t} enterstep

is invoked, even though I would not normally think of the
[trace remove] being part of [::generate].

The other thing that appears missing is that I would think
that when it becomes clear the coro will never be resumed
(with [rename coro {}], then we ought to be cleaning up
the abandoned trace housekeeping, and that's not happening
(thus the leak)).

My head hurts.