On Mon, Jun 6, 2016 at 9:34 PM, Clément Bera <bera.clem...@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 <b...@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
>>
>

Reply via email to