<div dir="ltr">Hi Ben, Hi Denis, Hi Clément,<div class="gmail_extra"><br><div class="gmail_quote">On Fri, May 20, 2016 at 7:52 PM, Ben Coman <span dir="ltr"><<a href="mailto:btc@openinworld.com" target="_blank">btc@openinworld.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><br>
On Sat, May 21, 2016 at 4:36 AM, Clément Bera <<a href="mailto:bera.clement@gmail.com">bera.clement@gmail.com</a>> wrote:<br>
<span class="">> On Fri, May 20, 2016 at 7:51 PM, Ben Coman <<a href="mailto:btc@openinworld.com">btc@openinworld.com</a>> wrote:<br>
>> On Fri, May 20, 2016 at 9:25 PM, Clément Bera <<a href="mailto:bera.clement@gmail.com">bera.clement@gmail.com</a>> wrote:<br>
>> > On Thu, May 19, 2016 at 3:42 PM, Ben Coman <<a href="mailto:btc@openinworld.com">btc@openinworld.com</a>> wrote:<br>
<br>
</span><div><div class="h5">>> >> P.S. For the curious, here is the proof of concept I tried. A single<br>
>> >> call to the primitive counts up to four by backing up the<br>
>> >> instructionPointer such that the primitive is executed again, until<br>
>> >> the exit condition of four is reached.<br>
>> >><br>
>> >> (Note the use of Semaphore and ExcessSignalsIndex is not significant,<br>
>> >> just an expedient template I was familiar with.)<br>
>> >><br>
>> >> # VM SIDE...<br>
>> >><br>
>> >> StackInterpreterPrimitives >> primitiveRetryExperiment<br>
>> >> | excessSignals stackTop |<br>
>> >> stackTop := self stackTop.<br>
>> >> excessSignals := objectMemory fetchInteger: ExcessSignalsIndex<br>
>> >> ofObject: stackTop.<br>
>> >> excessSignals := excessSignals + 1.<br>
>> >> objectMemory storeInteger: ExcessSignalsIndex<br>
>> >> ofObject: stackTop<br>
>> >> withValue: excessSignals.<br>
>> >> [ excessSignals > 3 ] ifFalse: [ instructionPointer :=<br>
>> >> instructionPointer - 1 ].<br>
>> >><br>
>> >> StackInterpreter class >> initializePrimitiveTable<br>
>> >> (234 primitiveRetryExperiment)<br>
>> >><br>
>> ><br>
>> > ... Why instructionPointer - 1 ?<br>
>><br>
>> A random experiment before moving on to -2, -3 and -4. Obviously I<br>
>> don't know enough yet to have properly judged its impact. Just poking<br>
>> it with a stick to see what pops out, and following that old adage<br>
>> that if you need something to be true, "assume it" until you learn<br>
>> otherwise. And whadayaknow..., it worked for the StackInterpreter.<br>
>> And I've since learnt its not so easy for the JIT.<br>
>><br>
>> > It works if the send is encoded in a single byte as by chance is the case in your example, else your interpretation get misaligned, which is a complete nonsense (unless you're working with a Smalltalk-78 VM ;-) ). Going backward in the instructions is not that trivial, you need a scanner to find the previous pc.<br>
>><br>
>> I thought *maybe* that since the layout of the receiver is "known" (to<br>
>> be an OwnedLock) the previous pc could be assumed at a known distance<br>
>> up the stack - but maybe that is bad practice and trouble if the<br>
>> layout changes for anyone subclassing OwnedLock.<br>
><br>
</div></div>> I don' really understand that known layout of the receiver thing.<br>
<br>
Just my limited understanding anf naive assumption. The course I took<br>
on compilers was 20 years ago. You counter example below helps...<br>
<br>
<br>
> The problem is with the encoding of sends in the bytecode. They are encoded in different number of bytes to lower the memory footprint.<br>
><br>
> For example, in Pharo, in this method:<br>
> foo<br>
> PrimExp new primRetryExperiment excessSignals<br>
><br>
> => 27 <D1> send: primRetryExperiment<br>
><br>
> the send to primRetryExperiment is encoded in a single byte.<br>
><br>
> But in this method:<br>
> foo<br>
> self foo1 foo2 foo3 foo4 foo5 foo6 foo7 foo8<br>
> self foo9 foo10 foo11 foo12 foo13 foo14 foo15 foo16.<br>
> PrimExp new primRetryExperiment excessSignals<br>
><br>
> => 109 <83 11> send: primRetryExperiment<br>
><br>
> The same send is encoded in 2 bytes.<br>
<br>
> So, after the send, in the primitive code, you need the instruction pointer to go back by 1 or 2 bytes to repeat the send. But you don't know by how many bytes. There is no easy way to guess if it's 1 or 2 (or something else, there are other cases I omitted).<br>
<br>
<br>
Okay. That helps me understand of my task better.<br>
<span class=""><br>
>> > See #skipBackBeforeJump for example, which goes backward 1 instruction in the image side. You need to implement something similar in the interpreter if you want to do that... but you don't.<br>
>><br>
>> I'll take a look just for interest, but point taken.<br>
>><br>
>> > Although going back one instruction is very funny, it won't work with the JIT, unless you do something completely evil, crazy and hackish.<br>
>><br>
>> That was the advice I was looking for. I don't want to be hackish ;)<br>
>> At least not in the final result.<br>
>><br>
>> > Most likely you want in fact to implement your primitive with a while loop instead, so you don't need that ip modification hack. Why didn't you do a while loop in the first place ?<br>
>><br>
>> A loop in the Image would work, but I'm attempting to keep it in the<br>
>> VM. IIUC a loop won't work in the VM because the primitive sleeps and<br>
>> changes context if the lock is held by someone else.<br>
><br>
><br>
</span>> hum. If I change your example to:<br>
<span class="">><br>
> StackInterpreterPrimitives >> primitiveRetryExperiment<br>
> | excessSignals stackTop |<br>
> stackTop := self stackTop.<br>
> excessSignals := objectMemory fetchInteger: ExcessSignalsIndex ofObject: stackTop.<br>
</span>> [ excessSignals > 3 ] whileFalse: [<br>
<span class="">> excessSignals := excessSignals + 1.<br>
> objectMemory storeInteger: ExcessSignalsIndex<br>
> ofObject: stackTop<br>
</span>> withValue: excessSignals ].<br>
><br>
> It's exactly equivalent to what you wrote, isn't it ? There is no process switch - context switch - in both cases in the loop, isn't it ? Or maybe I am missing something ?<br>
<br>
Apologies for mis-leading the discussion. The missing bit is, the<br>
above isn't my actually requirement. Its only an experiment of the<br>
effect of modifying the instruction pointer. My real use case is for<br>
the OwnedLock primitives I'm introducing [1], which at the end of<br>
primitiveOwnedLockWaitAcquire.<br>
<br>
self transferTo: self wakeHighestPriority from: CSOwnedLockWaitAcquire.<br>
self forProcessPrimitiveReturnToExecutivePostContextSwitch: inInterpreter.<br>
<br>
That code works well, except for one corner case identified by Dennis<br>
where after primitiveOwnedLockRelease hands the lock to a waiting<br>
process, that process could be terminated before waking up. </blockquote><div><br></div><div>There is a better way of solving this, and that is to use a pragma to identify a method that contains such a suspension point, and have the process terminate code look for the pragma and act accordingly. For example, the pragma could have a terminate action, sent to the receiver with the context as argument, e.g.</div><div><br></div><div>Mutex>>critical: mutuallyExcludedBlock<br> <onTerminate: #ensureMutexUnlockedInCritical:></div><div> ^lock waitAcquire<br></div> ifNil: mutuallyExcludedBlock<br> ifNotNil:[ mutuallyExcludedBlock ensure: [lock release] ]</div><div class="gmail_quote"><br></div><div class="gmail_quote">(and here I'm guessing...)</div><div class="gmail_quote"><br></div><div class="gmail_quote">Mutex>> ensureMutexUnlockedInCritical: aContext</div><div class="gmail_quote"> "long-winded comment explaining the corner case, referencing tests, etc, etc and how it is solved on terminate buy this method"</div><div class="gmail_quote"> (aContext pc = aContext initialPC</div><div class="gmail_quote"> and: [self inTheCorner]) ifTrue:</div><div class="gmail_quote"> [self doTheRightThingTM]</div><div class="gmail_quote"><br></div><div class="gmail_quote">So on terminate the stack is walked (it is anyway) looking for unwinds or onTerminate: markers. Any onTerminate: markers are evaluated, and the corner case is solved. The pragma approach also allows for visibility in the code.</div><div class="gmail_quote"><br></div><div class="gmail_quote"><br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"> Doing<br>
that in primitiveOwnedLockRelease is required because when<br>
primitiveOwnedLockWaitAcquire goes to sleep, you've already past its<br>
code that acquires the lock.<br>
<br>
Mutex>>critical: mutuallyExcludedBlock<br>
^[ lock waitAcquire<br>
ifNil: mutuallyExcludedBlock<br>
ifNotNil: mutuallyExcludedBlock<br>
] ensure: [lock release].<br>
<br>
instead of the faster...<br>
Mutex>>critical: mutuallyExcludedBlock<br>
^lock waitAcquire<br>
ifNil: mutuallyExcludedBlock<br>
ifNotNil:[ mutuallyExcludedBlock ensure: [lock release] ].<br>
<br>
I'd heard about Eliot "retrying primitives" for forwarding failures<br>
and considered a solution might be for primitiveOwnedLockWaitAcquire<br>
to be re-executed when its process woke up. So the lock is only<br>
acquired after the process wakes up, rather than being handed to it<br>
while it was asleep. Conceptually the process sleeps at the top of<br>
the primitive rather than the bottom.<br>
<br>
I found what Eliot does...<br>
dispatchFunctionPointer(primitiveFunctionPointer);<br>
/* begin maybeRetryFailureDueToForwarding */<br>
if (GIV(primFailCode)<br>
&& (checkForAndFollowForwardedPrimitiveState())) {<br>
/* begin initPrimCall */<br>
GIV(primFailCode) = 0;<br>
dispatchFunctionPointer(primitiveFunctionPointer);<br>
}<br>
<br>
but I guess that won't work when switching processes. So having<br>
caught the idea for primitive retry, I went looking for another way<br>
that worked across a process switch, and learn a bit more about the VM<br>
in the process. Hence my experiment to naively decrement the<br>
instruction pointer before switching context.<br>
<br>
[1] <a href="http://forum.world.st/OwnedLock-primitives-request-for-review-td4886130.html" rel="noreferrer" target="_blank">http://forum.world.st/OwnedLock-primitives-request-for-review-td4886130.html</a><br>
<br>
cheers -ben<br>
<div class=""><div class="h5"><br>
>><br>
>> > Primitive calls are not interrupt points anyway, it would be exactly the same behavior, wouldn't it ?<br>
>> ><br>
>> >><br>
>> >><br>
>> >> # IMAGE SIDE...<br>
>> >><br>
>> >> Semaphore subclass: #PrimExp<br>
>> >> instanceVariableNames: ''<br>
>> >> classVariableNames: ''<br>
>> >> package: '0PrimitiveRetryExperiment'<br>
>> >><br>
>> >> PrimExp >> initialize<br>
>> >> excessSignals := 1.<br>
>> >><br>
>> >> PrimExp >> primRetryExperiment<br>
>> >> <primitive: 234><br>
>> >><br>
>> >> PrimExp >> excessSignals<br>
>> >> ^ excessSignals<br>
>> >><br>
>> >> # TEST CASE...<br>
>> >><br>
>> >> PrimExp new primRetryExperiment excessSignals<br>
>> >> --> 4<br>
>> >><br>
>> >> I've only done this with the Stack VM so far. I'll report further<br>
>> >> when I try it with Cog.<br>
>><br>
>> So I found the approach doesn't work with Cog.<br>
>> cheers -ben<br>
>><br>
>> ><br>
>> > It's nice to see people trying to hack the VM :-). I'll try to answer your other questions.<br>
</div></div></blockquote></div><br><br clear="all"><div><br></div>-- <br><div class="gmail_signature"><div dir="ltr"><div><span style="font-size:small;border-collapse:separate"><div>_,,,^..^,,,_<br></div><div>best, Eliot</div></span></div></div></div>
</div></div>