<div dir="ltr">Hi Tobias,<div><br></div><div> first let me sympathize. This is such horrible code :-(. Ovr the years, getting this code to work with Cog's context-to-stack-mapping machinery has given me headaches :-).</div><div><br></div><div class="gmail_extra"><br><div class="gmail_quote">On Tue, Mar 31, 2015 at 9:01 AM, Tobias Pape <span dir="ltr"><<a href="mailto:Das.Linux@gmx.de" target="_blank">Das.Linux@gmx.de</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">Hey fellow Squeakers<br>
<br>
[Attention: low level lengthy stuff]<br>
<br>
I today encountered a strange behavior.<br>
When running the Startup code, somewhen ContextPart>>#complete is called,<br>
which, in the process issues #contextOn:do:, which subsequently somewhere interanlly<br>
does a jump:<br>
<br>
contextOn: exceptionClass do: block<br>
"Create an #on:do: context that is ready to return from executing its receiver"<br>
<br>
| ctxt chain |<br>
ctxt := thisContext.<br>
[chain := thisContext sender cut: ctxt. ctxt jump] on: exceptionClass do: block.<br>
"jump above will resume here without unwinding chain"<br>
^ chain<br>
<br>
The idea is that we end up right before '^ chain' as the comment indicates.<br>
so what happens is that ctxt is the context of #contextOn:do: itself and it gets<br>
send #jump in the closure.<br>
<br>
Jump now does the following:<br>
<br>
jump<br>
"Abandon thisContext and resume self instead (using the same current process). You may want to save thisContext's sender before calling this so you can jump back to it.<br>
Self MUST BE a top context (ie. a suspended context or a abandoned context that was jumped out of). A top context already has its return value on its stack (see Interpreter>>primitiveSuspend and other suspending primitives).<br>
thisContext's sender is converted to a top context (by pushing a nil return value on its stack) so it can be jump back to."<br>
<br>
| top |<br>
"Make abandoned context a top context (has return value (nil)) so it can be jumped back to"<br>
thisContext sender push: nil.<br>
<br>
"Pop self return value then return it to self (since we jump to self by returning to it)"<br>
stackp = 0 ifTrue: [self stepToSendOrReturn].<br>
stackp = 0 ifTrue: [self push: nil]. "must be quick return self/constant"<br>
top := self pop.<br>
thisContext privSender: self.<br>
^ top<br>
<br>
So. bytecode for #contextOn:do: is:<br>
<br>
29 <8A 01> push: (Array new: 1)<br>
31 <6B> popIntoTemp: 3<br>
32 <89> pushThisContext:<br>
33 <6A> popIntoTemp: 2<br>
34 <12> pushTemp: 2<br>
35 <13> pushTemp: 3<br>
36 <8F 20 00 0A> closureNumCopied: 2 numArgs: 0 bytes 40 to 49<br>
40 <89> pushThisContext:<br>
41 <D2> send: sender<br>
42 <10> pushTemp: 0<br>
43 <E1> send: cut:<br>
44 <8E 00 01> popIntoTemp: 0 inVectorAt: 1<br>
47 <10> pushTemp: 0<br>
48 <D3> send: jump<br>
49 <7D> blockReturn<br>
50 <10> pushTemp: 0<br>
51 <11> pushTemp: 1<br>
52 <F0> send: on:do:<br>
53 <87> pop<br>
54 <8C 00 03> pushTemp: 0 inVectorAt: 3<br>
57 <7C> returnTop<br>
<br>
<br>
The jump lands right at 53 and does a pop.<br>
HOWEVER, at this point the stack of this context is empty and the pop actually pops the 3rd temp<br>
from the temps that 'just happens' to be right under the stack. This should be fatal.<br>
HOWEVER again, Squeak actually does not pop but only decrement the SP so the temp access still<br>
works(this _could_ be fine but some implementations (Eg, RSqueak) tried to separate temps and<br>
stack; which is not possible currently).<br>
<br>
What could be the problem here?<br>
- are the 'stackp = 0'-checks in #jump wrong and they actually should check for the actual stack depth _after_ temps?<br></blockquote><div><br></div><div>It does look like it. I would have expected this to be more correct:</div><div><br></div><div>jump</div><div><span class="" style="white-space:pre">        </span>"Abandon thisContext and resume self instead (using the same current process).</div><div><span style="white-space:pre">        </span> You may want to save thisContext's sender before calling this so you can jump back to it.</div><div><span class="" style="white-space:pre">        </span>Self MUST BE a top context (ie. a suspended context or a abandoned context that was jumped</div><div><span style="white-space:pre">        </span> out of). A top context already has its return value on its stack (see Interpreter>>primitiveSuspend</div><div><span style="white-space:pre">        </span> and other suspending primitives). thisContext's sender is converted to a top context (by pushing a</div><div><span style="white-space:pre">        </span> nil return value on its stack) so it can be jump back to."</div><div><br></div><div><span class="" style="white-space:pre">        </span>| top |</div><div><span class="" style="white-space:pre">        </span>"Make abandoned context a top context (has return value (nil)) so it can be jumped back to"</div><div><span class="" style="white-space:pre">        </span>thisContext sender push: nil.</div><div><br></div><div><span class="" style="white-space:pre">        </span>"Pop self return value then return it to self (since we jump to self by returning to it)"</div><div><span class="" style="white-space:pre">        </span>stackp <= self numTemps ifTrue: [self stepToSendOrReturn].</div><div><span class="" style="white-space:pre">        </span>(stackp <= self numTemps</div><div><span class="" style="white-space:pre">        </span> and: [self willJustPop]) ifTrue: [self push: nil]. "must be quick return self/constant"</div><div><br></div><div><span class="" style="white-space:pre">        </span>top := self pop.</div><div><span class="" style="white-space:pre">        </span>thisContext privSender: self.</div><div><span class="" style="white-space:pre">        </span>^top</div><div> </div><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">
- should we put in a "sacrificial anode" in #contextOn:do: so that the pop does not pop the empty stack? (like this:<br>
<br>
contextOn: exceptionClass do: block<br>
"Create an #on:do: context that is ready to return from executing its receiver"<br>
<br>
| ctxt chain |<br>
ctxt := thisContext.<br>
[chain := thisContext sender cut: ctxt.<br>
ctxt push: nil. "sacrifical anode"<br>
ctxt jump<br>
] on: exceptionClass do: block.<br>
"jump above will resume here without unwinding chain"<br>
^ chain<br></blockquote><div><br></div><div>That looks right. Once the on:do: is sent the thisContext of contextOn:do:'s stack contains only exceptionClass, block, context and the indirection vector containing chain. So it is in the stack = self numTemps case.</div><div> </div><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>
- Or is there an even better way?<br></blockquote><div><br></div><div>I'm not sure the other ways are any better. The way to transfer to a context without disturbing its stack is to do a process switch. So you do something equivalent to</div><div><br></div><div><div>jump</div><div><span class="" style="white-space:pre">        </span>"Abandon thisContext and resume self instead (using the same current process)."</div><div><br></div><div><span class="" style="white-space:pre">        </span>| process semaphore |</div><div><span class="" style="white-space:pre">        </span>process := Processor activeProcess.</div><div><span class="" style="white-space:pre">        </span>semaphore := Semaphore new.</div><div><br></div><div><span class="" style="white-space:pre">        </span>[process suspendedContext unwindTo: self.</div><div><span class="" style="white-space:pre">        </span> process suspendedContext: self.</div><div><span class="" style="white-space:pre">        </span> semaphore signal] fork.</div><div><br></div><div><span class="" style="white-space:pre">        </span>semaphore wait</div></div><div><br></div><div>This way no bizarre stack manipulations are going on, and no return value is pushed, because there isn't a return. One may get away with:</div><div><br></div><div><div>jump</div><div><span class="" style="white-space:pre">        </span>"Abandon thisContext and resume self instead (using the same current process)."</div><div><br></div><div><span class="" style="white-space:pre">        </span>[| process |</div><div><span class="" style="white-space:pre">        </span> process := Processor activeProcess.</div><div><span class="" style="white-space:pre">        </span> process suspendedContext unwindTo: self.</div><div><span class="" style="white-space:pre">        </span> process suspendedContext: self] fork.</div><div><br></div><div><span class="" style="white-space:pre">        </span>Processor yield</div></div><div><br></div><div>I'd be interested in your experience using either of these. One of the advantages the process switch versions have is in not updating the receiving context sp there's a chance the context-to-stack mapping machinery won't flush the context to the heap. In the end it might actually be faster.</div></div>-- <br><div class="gmail_signature">best,<div>Eliot</div></div>
</div></div>