On Tue, Jun 7, 2016 at 1:55 AM, Ben Coman <[email protected]> wrote:

> On Tue, Jun 7, 2016 at 2:39 AM, Clément Bera <[email protected]>
> wrote:
> > Yeah anyway the non local return happens after the ensure: send, so
> nothing
> > to worry about.
> >
> > In your analysis, you need to see that the #returnTop bytecode means NLR
> in
> > a block but method return in a method. Even if it looks the same, in one
> > case the #returnTop belongs to a block closure bytecode, hence is a NLR,
> > whereas in the othercase, it belongs to the method bytecode, hence it's a
> > method return.
> >
> > See StackInterpreter>>commonReturn, this code:
> >
> > "If this is a method simply return to the  sender/caller."
> > (self iframeIsBlockActivation: localFP) ifFalse:
> > [^self commonCallerReturn].
> >
>
> Ahhh, got it.  Quite a subtle semantic when the source code looks much the
> same.
>
>
> > test if the return top is a method return or block NLR. I don't know why
> > Eliot decided to share the bytecode between method return and block NLR,
> but
> > there are 3 returns in Smalltalk, method return, block return and block
> NLR
> > and they're all quite different.
> >
> > About your example, the normal closure bytecode creates the closure,
> pushes
> > it on top of the stack, then skips over the closure bytecode (jump
> forward).
> > About the jump instructions generated by ifTrue:ifFalse:, the jump
> forward
> > instruction has no interrupt point, while the conditional jump have an
> > interrupt point only in the case of a must be boolean. Does this make
> sense
> > ?
>
> Just to confirm in my own words...
> the jumpFalse:
> * sent to a boolean has no interrupt point
> * sent to a non-boolean has an interrupt point
>

yes.


>
> In Squeak there's indentation in bytecode printing to ease control flow
> > understanding, that may help you. You are using a Squeak image for VM
> > simulation and Pharo image to implement the primitives and experiment
> with
> > the primitives, aren't you ?
>
> The purpose of the primitives is for Pharo, but right this moment I'm
> using Squeak for both the simulator and the image being simulated (my
> changeset only needed a few changes to split Context back to its
> earlier components).  While learning the simulator it was the path of
> least resistance using the supplied scripts to build the reader image,
> and I hadn't swapped to simulating a Pharo image yet.


> cheers -ben
>
> >
> > Cheers
> >
> > On Mon, Jun 6, 2016 at 6:34 PM, Ben Coman <[email protected]> wrote:
> >>
> >> On Mon, Jun 6, 2016 at 9:34 PM, Clément Bera <[email protected]>
> >> wrote:
> >> > Hi Ben,
> >> >
> >> > Firstly be careful because atomic usually means that the thread cannot
> >> > be
> >> > interrupted by a concurrent native thread, so an atomic operation has
> to
> >> > be
> >> > guaranteed at processor level.
> >> >
> >> > Assuming that by atomic you mean that the operation's process cannot
> be
> >> > preempted by another green thread, yes there is big problems with non
> >> > local
> >> > returns.
> >>
> >> Yes, I meant at the image level, run by the single-thread VM.
> >>
> >> >
> >> > Two main issues:
> >> > - NLR can trigger unwind blocks, which can switch to another process.
> >> > - NLR can fail, triggering the cannotReturn: call-back, which can
> switch
> >> > to
> >> > another process.
> >>
> >> These would presumably could only occur after the #ensure: had been
> >> invoked?  .
> >>
> >> >
> >> > I understand generally that an inlined  #ifTrue  is atomic.  For
> >> > example, here after the primitive returns, no interruption can occur
> >> > before the #ensure is invoked...
> >> >     self primitiveWaitAcquire ifTrue:
> >> >            [mutuallyExclusiveBlock ensure: [self release]].
> >> >
> >> > The exact answer is that #ifTrue: can be interrupted if self
> >> > primitiveWaitAcquire is not a boolean, and can't be interrupted if
> it's
> >> > a
> >> > boolean. It's nice to mark in a comment if part of the method's code
> has
> >> > not
> >> > to be interrupted to avoid issues years later.
> >>
> >> Good tip.
> >>
> >> >
> >> > But I want to know whether a non-local return could have an impact on
> >> > that atomicity. For example ...
> >> >     self primitiveWaitAcquire ifTrue:
> >> >            [ ^ mutuallyExclusiveBlock ensure: [self release]]
> >> >
> >> > In your code there is no non local return because ifTrue: is inlined
> by
> >> > the
> >> > Bytecode compiler.
> >>
> >> I'm not clear on your meaning.  If there were additional code
> >> following those two lines, then they would not be executed, so it
> >> would still be a NLR ?  I took my understanding from [1].  And in the
> >> bytecode below it seems #returnTop is associated with the NLRs - so
> >> the NLR is still there(?), but having looked at the bytecode I can now
> >> see it has no impact, I think either with or without inlining makes no
> >> difference wrt NLR.
> >>
> >>
> >> [1]
> >>
> https://silversmalltalk.wordpress.com/2011/02/02/implementing-smalltalks-non-local-returns-in-javascript/
> >>
> >> > Try to compile it and look at the bytecode you will see.
> >>
> >> Good idea. So I share a little analysis...
> >>
> >>
> >> The bytecode for these two lines is identical,
> >>     a blah: [ [ self foo ] ensure: [  self bar  ] ]
> >>     a blah: [ ^ [ self foo ] ensure: [ self bar ] ]
> >> except for bytecode 49, respectively...
> >>      "49 <7D> blockReturn"
> >>      "49 <7C> returnTop"
> >>
> >> "29 <10> pushTemp: 0"
> >> "30 <8F 00 00 10> closureNumCopied: 0 numArgs: 0 bytes 34 to 49"
> >> "34 <8F 00 00 03> closureNumCopied: 0 numArgs: 0 bytes 38 to 40"
> >> "38 <70> self"
> >> "39 <D0> send: foo"
> >> "40 <7D> blockReturn"
> >> "41 <8F 00 00 03> closureNumCopied: 0 numArgs: 0 bytes 45 to 47"
> >> "45 <70> self"
> >> "46 <D1> send: bar"
> >> "47 <7D> blockReturn"
> >> "48 <E2> send: ensure:"
> >> "49 <7C> returnTop"
> >> "50 <E3> send: blah:"
> >> "51 <87> pop"
> >> "52 <78> returnSelf"
> >>
> >>
> >> Changing #blah: to #ifTrue:  these two lines
> >>      a ifTrue: [ [ self foo ] ensure: [ self bar ] ]
> >>      a ifTrue: [ ^ [ self foo ] ensure: [ self bar ] ]
> >> are the same except for bytecode 49, respectively...
> >>      "47 <87>  pop"
> >>      "47 <7C> returnTop"
> >>
> >> "29 <10> pushTemp: 0"
> >> "30 <AC 10> jumpFalse: 48"
> >> "32 <8F 00 00 03> closureNumCopied: 0 numArgs: 0 bytes 36 to 38"
> >> "36 <70> self"
> >> "37 <D0> send: foo"
> >> "38 <7D> blockReturn"
> >> "39 <8F 00 00 03> closureNumCopied: 0 numArgs: 0 bytes 43 to 45"
> >> "43 <70> self"
> >> "44 <D1> send: bar"
> >> "45 <7D> blockReturn"
> >> "46 <E2> send: ensure:"
> >> "47 <87> pop"
> >> "48 <78> returnSelf"
> >>
> >> And between #blah: and #ifTrue: the difference is
> >> removal of...
> >>     "30 <8F 00 00 10> closureNumCopied: 0 numArgs: 0 bytes 34 to 49"
> >>     "49 <7D> blockReturn"
> >>     "50 <E3> send: blah:"
> >> and replaced them with...
> >>     "30 <AC 10> jumpFalse: 48"
> >>
> >> It took me a while to guess how to follow the execution path.   I had
> >> assumed it would just go top to bottom, but that didn't make sense for
> >> foo to be executed before blah.  Can you confirm.. I guess the trick
> >> (for #blah) is...
> >>
> >> 1. At 29, push the temp
> >> 2. skip the closureNumCopied bytes 34 to 49,
> >> 3. At 50, send #blah.
> >> 4. next is bytecode 34 skips bytecodes 38 to 40,
> >> 5. and bytecode 41 skips bytecodes 45 to 47
> >> 6. At 48 send #ensure:.
> >>
> >> versus   #ifTrue:  bytecocde...
> >>
> >> 1. At 29, push the temp
> >> 2. skip closureNumCopied bytes  36 to 38"
> >> 3. skip closureNumCopied: bytes 43 to 45"
> >> 4. At 46, send #ensure:
> >>
> >> So its clear (IIUC) that with  #IfTrue:  the #ensure is the first send:
> >> after the push temp, but the non-inlined  #blah:  is an extra send
> >> between which could be interrupted and prevent the #ensure from being
> >> executed.
> >>
> >> >
> >> > Else as I said at the beginning of the mail, a non local return can
> >> > imply a
> >> > process switch if that was your question.
> >>
> >> Yes, but a process switch is okay after the send of #ensure:
> >> just not before.
> >>
> >> thx. cheers -ben
> >>
> >> >
> >> >
> >> >
> >> >
> >> >
> >> >
> >> >
> >> > On Mon, Jun 6, 2016 at 2:46 PM, Ben Coman <[email protected]>
> wrote:
> >> >>
> >> >> I understand generally that an inlined  #ifTrue  is atomic.  For
> >> >> example, here after the primitive returns, no interruption can occur
> >> >> before the #ensure is invoked...
> >> >>     self primitiveWaitAcquire ifTrue:
> >> >>            [mutuallyExclusiveBlock ensure: [self release]].
> >> >>
> >> >> But I want to know whether a non-local return could have an impact on
> >> >> that atomicity. For example ...
> >> >>     self primitiveWaitAcquire ifTrue:
> >> >>            [ ^ mutuallyExclusiveBlock ensure: [self release]]
> >> >>
> >> >> cheers -ben
> >> >>
> >> >
> >>
> >
>
>

Reply via email to