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 >> >