On Tue, Jun 7, 2016 at 2:39 AM, Cl��ment Bera <bera.clement@gmail.com> 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
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 <btc@openinworld.com> wrote:
>>
>> On Mon, Jun 6, 2016 at 9:34 PM, Cl��ment Bera <bera.clement@gmail.com>
>> 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 <btc@openinworld.com> 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
>> >>
>> >
>>
>