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