[squeak-dev] BUG/REGRESSION while debugging Generator >> #nextPut:

Eliot Miranda eliot.miranda at gmail.com
Tue Dec 24 02:57:12 UTC 2019


Hi Christophe,

On Fri, Dec 13, 2019 at 10:48 AM Thiede, Christoph <
Christoph.Thiede at student.hpi.uni-potsdam.de> wrote:

> Hi all,
>
>
> I discovered a bug when stepping over a call to Generator >> #nextPut:.
>

Apologies.  I can now reproduce the bug.  I was stupidly stepping over the
block evaluation, not the send of nextPut:.


> !! Save your image before trying the following !!
>
>
> Generator
> on: [:stream | stream nextPut: #foo]
>
>
>
>
>    1. Due to multiprocessing implementation of the Generator, pressing
>    over will hang up the current process forever, because the generator is
>    never queried.
>    2. If I press cmd-dot, I get an infinite number of debugger windows
>    that show Context >> #cannotReturn:. This will probably damage your
>    image.
>
>
> The first is annoying for newcomers, but I see this behavior is reasonable
> and without the effect of the second, it wouldn't be a big problem.
> The second, however, looks like a bigger issue to me. Actually, I
> think the most serious aspect is that in Squeak 5.1, the same procedure
> does not crash your image, but you are forwarded to the emergency debugger
> and can terminate the process:
>
>
>
> Also, this is not the first scenario in the last time where I got
> an infinite debugger chain. See [BUG(s)] in Context control (#jump,
> #runUntilErrorOrReturnFrom:)
> <http://forum.world.st/BUG-s-in-Context-control-jump-runUntilErrorOrReturnFrom-td5107263.html> for
> a similar issue. And there were even more situations which I could not
> yet reproduce exactly.
> I'm afraid that the recent changes to the debuggers might have weakened
> its ability to detect recursive errors. Can someone else tell about these
> problems?
>
> I suppose we ignore a large number of recursive errors as in
> MorphicDebugger >> #openOn:context:label:contents:fullView:, the uiBlock
> is often triggered as a separate UI message which will be executed after
> the recursion flag has been cleared by the base class.
>
> Would be great if someone could have a look at it or share more
> information :)
>

So the issue seems to me to be the interaction between the block
established in runUntilErrorOrReturnFrom: to catch unhandled errors (this
one:
Context contextOn: UnhandledError do: [:ex |
error ifNil: [
error := ex exception.
topContext := thisContext.
ex resumeUnchecked: here jump]
ifNotNil: [ex pass]
]
)
and the swapSender: in Generator>>nextPut:.  So after the swapSender: the
Generator's continue variable refers to this BlockClosure>>on:do:.
BlockClosure>>ensure: pair introduced by runUntilErrorOrReturnFrom:.  What
I don't understand was how the evaluateOnBehalfOf: changes interact with
that.  But what I can say is that the Debugger is leaving the Generator (or
any code that uses swapSender:) in an invalid state because
a) once the swapSender: occurs the protect blocks (the on:do:,ensure: pair)
are no longer on the stack.  What the swapSender should have been morphed
into doing was preserving the protect blocks under the current context, or
rather that swapSender should have been persuaded to swap the senders of
the protect blocks.  Now when the debugger continues stepping it never gets
to the protect blocks because they have been swapped out of the way, and
execution proceeds all the way down to the fork in Generator>>reset.

You can observe this by
- stepping through "Generator on: [:stream | stream nextPut: #foo]" until
at the send of #nextPut:
- inspecting the Debugger (via the Morphic tool handle menu "inspect model"
- debugging "self doStep" to step the Debugger through the send
- when you get to runUntilErrorOrReturnFrom: *ONLY STEP UP TO THE jump* *DO
NOT STEP OVER THE jump*
- at the jump, step into, then do step into, and you'll find the debugger
now in Generator>>nextPut:, with the stack still including the protect
pair.  But when you step over the swapSender: then, of course, they've gone
missing, and the stack points back to the fork in reset with nothing on the
stack to stop execution (because there's no ensure: block which is
what runUntilErrorOrReturnFrom: inserted to try and catch any attempt to
return).

So to get to the point of the swapSender: you should see this:

Context>>jump
Context>>runUntilErrorOrReturnFrom:
[] in Process>>complete:
BlockClosure>>ensure:
Process>>evaluate:onBehalfOf:
Process>>complete:
Process>>completeStep:
[] in MorphicDebugger(Debugger)>>doStep
BlockClosure>>on:do:
MorphicDebugger(Debugger)>>handleLabelUpdatesIn:whenExecuting:
MorphicDebugger(Debugger)>>doStep

and the stack from the Context about to jump is

Generator>>nextPut:
BlockClosure>>on:do:
BlockClosure>>ensure:
[] in UndefinedObject>>DoIt
Generator>>fork
[] in Generator>>reset
Generator>>reset
Generator>>initializeOn:
Generator class>>on:
UndefinedObject>>DoIt ...

and the Generator's "continue" stack immediately before the swapSender: in
Generator>>nextPut: is

Generator>>reset
Generator>>initializeOn:
Generator class>>on:
UndefinedObject>>DoIt

What we'd like to see after the swapSender: is the protect blocks moved to
the continue stack:
Generator>>nextPut:
BlockClosure>>on:do:
BlockClosure>>ensure:
Generator>>reset
Generator>>initializeOn:
Generator class>>on:
UndefinedObject>>DoIt

and the new "continue" stack looking like this:
Generator>>fork
[] in Generator>>reset
Generator>>reset
Generator>>initializeOn:
Generator class>>on:
UndefinedObject>>DoIt ...


What I don't know is how to fix this in the general case.  This seems
hard.  The debugger is executing Smalltalk (i.e. *not* stepping bytecodes)
in the context of runUntilErrorOrReturnFrom: and a swapSender: could happen
at an arbitrarily deep point in that execution and we want the protect
blocks in runUntilErrorOrReturnFrom: to protect execution after a
swapSender:.  I have no idea how to do that.

Now what's more likely is that I'm not seeing what's obviously wrong about
the introduction of evaluate:onBehalfOf:.  Why does this make any
difference?  It seems to me that the issue is with
runUntilErrorOrReturnFrom: and swapSender:, not with evaluate:onBehalfOf:.
What am I missing?

> Best,
> Christoph

_,,,^..^,,,_
best, Eliot
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.squeakfoundation.org/pipermail/squeak-dev/attachments/20191223/9cf6607d/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: pastedImage.png
Type: image/png
Size: 43800 bytes
Desc: not available
URL: <http://lists.squeakfoundation.org/pipermail/squeak-dev/attachments/20191223/9cf6607d/attachment-0003.png>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: pastedImage.png
Type: image/png
Size: 185254 bytes
Desc: not available
URL: <http://lists.squeakfoundation.org/pipermail/squeak-dev/attachments/20191223/9cf6607d/attachment-0004.png>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: pastedImage.png
Type: image/png
Size: 171961 bytes
Desc: not available
URL: <http://lists.squeakfoundation.org/pipermail/squeak-dev/attachments/20191223/9cf6607d/attachment-0005.png>


More information about the Squeak-dev mailing list