[Vm-dev] Re: primitive retry across suspension for OwnedLock waitAcquire (was: Interpreter versus StackInterpreter hierarchy)

Ben Coman btc at openinworld.com
Sat Jun 4 02:10:10 UTC 2016


On Fri, Jun 3, 2016 at 8:30 PM, Ben Coman <btc at openinworld.com> wrote:
> On Fri, Jun 3, 2016 at 6:33 PM, Ben Coman <btc at openinworld.com> wrote:
>
>> where  OwnedLock>>experiment1   is...
>>     | result |
>>     result := OrderedCollection new: 20.
>>     result myAdd: 0.
>
> btw, OrderedCollection>>myAdd:
> is direct copy of OrderderedCollection>>add:
> just so [set break selector...] can distinguish these from every send
> of add: in the image.
>
>
>> The current VM produces a result of...
>>      OrderedCollection(0 11 12 21 22 13 14 24 8 9)
>>
>> but I'm hoping to get something like...
>>      OrderedCollection(0 11 12 21 22 14 23 24 8 9)
>
>
> After loading my 'ownedlock-reader.image' with an unchanged VM,
> then adding these two lines to transferTo:
>
>>     objectMemory
>>         storeInteger: SuspendedPrimitiveFailCodeIndex
>>         ofObject: oldProc
>>         withValue: primFailCode.
>>     primFailCode := objectMemory
>>        fetchInteger: SuspendedPrimitiveFailCodeIndex
>>        ofObject: newProc.
>
> then executing...    OwnedLock new experiment1 !
> I get an AssertionFailure in...
>
> Spur32BitMMLESimulator>>fetchPointer: fieldIndex ofObject: objOop
>     self assert: (self isForwarded: objOop) not.
>     self assert: (fieldIndex >= 0 and: [fieldIndex < (self
> numSlotsOfAny: objOop)
>            or: [fieldIndex = 0 "forwarders and free objs"]]).
>     ^super fetchPointer: fieldIndex ofObject: objOop
>
> where...
>   objOop  ==>
>       16r2D2490: a(n) OwnedLock
>            16r41A280 nil   16r41A280 nil   16r41A280 nil
>
>   fieldIndex  ==>  3
>
>   self numSlotsOfAny: objOop  ==>  3
>
>
> One thing I'm curious about is why the " < " and not a " <= " ?
>     super fetchPointer: fieldIndex ofObject: objOop
> successfully returns...   16r41A280: a(n) UndefinedObject
>
>
> btw, here is the call stack...
>    -16r106C [] in OwnedLock>experiment1 16r2D2490: a(n) OwnedLock
>    -16r104C BlockClosure>on:do: 16r2D29C8: a(n) BlockClosure
>    -16r1024 [] in OwnedLock>experiment1 16r2D2490: a(n) OwnedLock
>    -16r1008 [] in BlockClosure>newProcess 16r2D27E8: a(n) BlockClosure

btw2, I am guessing that the lack of a Compiler in the call stack
means we are within one of the forked blocks.

>
> and the ext head frame...
>    -16r106C [] in OwnedLock>experiment1 16r2D2490: a(n) OwnedLock
>    -16r1064/3559:   rcvr/clsr:   16r2D29C8 a BlockClosure
>    -16r1068/3558:cllr ip/ctxt:   16rA3F922=10746146
>    -16r106C/3557:    saved fp:    -16r104C=-4172
>    -16r1070/3556:      method:   16r81EF10 a CompiledMethod
>    -16r1074/3555:       flags:  16r1010001=16842753  numArgs: 0
> hasContext: true  isBlock: true
>    -16r1078/3554:     context:   16r2D29F8=2959864
>    -16r107C/3553:    receiver:   16r2D2490 an OwnedLock
>    -16r1080/3552:   temp/stck:   16r2D24A8 an OrderedCollection
>    -16r1084/3551:   temp/stck:   16r2D2490 an OwnedLock
>
> and the int head frame...
>    -16r108C OwnedLock(Process)>suspend 16r2D2490: a(n) OwnedLock
>    -16r1084/3551:   rcvr/clsr:   16r2D2490 an OwnedLock
>    -16r1088/3550:cllr ip/ctxt:   16r81EFBB=8515515
>    -16r108C/3549:    saved fp:    -16r106C=-4204
>    -16r1090/3548:      method:  16r10DF5B0 a CompiledMethod
>    -16r1094/3547:       flags:        16r1=1  numArgs: 0  hasContext:
> false  isBlock: false
>    -16r1098/3546:     context:   16r41A280=4301440
>    -16r109C/3545:    receiver:   16r2D2490 an OwnedLock
>    -16r10A0/3544:   temp/stck:   16r41A280 nil
>
>
> What is the difference between ext and int  head frames?
>
> One thing I find curious here is "OwnedLock(Process)>suspend".  Does
> this indicate that #suspend was sent to an OwnedLock and looked up the
> hierarchy to find the method in Process?
> But OwnedLock doesn't inherit from Process, it inherits from LinkedList.
>
> cheers -ben

TLDR;   Reporting all the following helped me focus on the details,
but most turned out irrelevant. I've left it for background info, but
you may as well skip down to my Ahahhh.. moment.
And the question I'm stuck on is right at the bottom an fairly
isolated from everything else.

At the assertion failure, if I [print oop...] the Array inside the
Ordered Collection it produces...

  16r285008: a(n) Array
        16r1 =0 (16r0)       16r17 =11 (16rB)       16r19 =12 (16rC)
    16r2B =21 (16r15)
       16r2D =22 (16r16)       16r1D =14 (16rE)   16r41A280 nil   16r41A280 nil
   16r41A280 nil   16r41A280 nil   16r41A280 nil   16r41A280 nil
   16r41A280 nil   16r41A280 nil   16r41A280 nil   16r41A280 nil
   16r41A280 nil   16r41A280 nil   16r41A280 nil   16r41A280 nil

which is somewhat positive, showing that now experimentSuccessAndSleep
did not error(13)
i.e. OrderedCollection(0 11 12 21 22 14)

Also, it seems it got to the end of the first forked block, and
considering the later bytecode trace, maybe the problem is with
terminating that forked block. Is line "145 7D returnTopFromBlock
(2506913)" below where this starts to happen?


With  [break on selector...] > myAdd:
I can <proceed> successfully five times. Turning on [print sends] and
[print bytecode each bytecode] then proceding the sixth time
produces...

2506895 myAdd:
21 70 pushReceiverBytecode (2506897)
22 10 pushTemporaryVariableBytecode (2506898)
23 E0 sendLiteralSelector1ArgBytecode (2506899)

OrderedCollection>addLast:
2506898 addLast:
25 00 pushReceiverVariableBytecode (2506900)
26 C2 bytecodePrimSize (2506901)
27 02 pushReceiverVariableBytecode (2506902)
28 B6 bytecodePrimEqual (2506903)
33 00 pushReceiverVariableBytecode (2506904)
34 02 pushReceiverVariableBytecode (2506905)
35 76 pushConstantOneBytecode (2506906)
36 B0 bytecodePrimAdd (2506907)
37 81 extendedStoreBytecode (2506908)
39 10 pushTemporaryVariableBytecode (2506909)
40 C1 bytecodePrimAtPut (2506910)
41 7C returnTopFromMethod (2506911)
24 7C returnTopFromMethod (2506912)
145 7D returnTopFromBlock (2506913)
         "localReturnValue := self internalStackTop."
         "self commonCallerReturn"
         "   self setMethod: (self frameMethod: localFP)."
         "   self fetchNextBytecode"
         "   self internalStackTopPut: localReturnValue  (=14)"
         "I am unable to [print call stack] at this point ==> invalid
frame pointer
            Is this normal at this point?"

55 87 popStackBytecode (2506914)
         "Still unable to [print call stack] at this point ==> invalid
frame pointer
56 45 pushLiteralVariableBytecode (2506915)
57 D4 sendLiteralSelector0ArgsBytecode (2506916)
          "rcvr = ProcessorScheduler:"

ProcessorScheduler>activeProcess
2506915 activeProcess
21 01 pushReceiverVariableBytecode (2506917) --> Process
22 D0 sendLiteralSelector0ArgsBytecode (2506918) --> effectiveProcess

Process>effectiveProcess
2506917 effectiveProcess
21 05 pushReceiverVariableBytecode (2506919)
22 88 duplicateTopBytecode (2506920)
23 73 pushConstantNilBytecode (2506921)
24 C6 bytecodePrimIdentical (2506922)
26 87 popStackBytecode (2506923)
27 70 pushReceiverBytecode (2506924)
28 7C returnTopFromMethod (2506925)
23 7C returnTopFromMethod (2506926)
58 D3 sendLiteralSelector0ArgsBytecode (2506927) --> #suspend
          "messageSelector =   #suspend"
          "rcvr =   16r24B748: a(n) Process
   16r41A280 nil   16r41A280 nil  16r91 =72 (16r48) "priority"
   16r41A280 nil  16r41A280 nil   16r41A280 nil
   16r41A280 nil   16r41A280 nil  16r41A280 nil
    16r1 =0 (16r0) "suspendedPrimitiveFailCode, newly introduced"

Process>suspend
2506926 suspend
    "commonSendOrdinary"
    "  internalExecuteNewMethod"
    "     slowPrimitiveResponse"
    "        dispatchFunctionPointer: "
    "             primitiveSuspend"
    "                  process := self stackTop.
    "                  process = self activeProcess ifTrue:
    "                      [self pop: 1 thenPush: objectMemory nilObject.
    "                      ^self transferTo: self wakeHighestPriority].

In transferTo:,   newProc is....
     16r24B9B8: a(n) Process
   16r41A280 nil   16r24BAD8 a MethodContext       16r8F =71 (16r47)
16r459620 a LinkedList
   16r41A280 nil   16r41A280 nil   16r41A280 nil   16r41A280 nil
   16r41A280 nil       16r55 =42 (16r2A)


Ahahhhh.... priority 71, that is the following forked process...
    [result myAdd: 21.
        [result myAdd: 22.
         self experimentFailAndWakeOther] on: Error do: [result myAdd: 23].
       result myAdd: 24] forkAt: 71.

and its suspendedPrimitiveFailCode ==> 42
as set by primitiveExperimentFailAndWakeOther
And indeed, tracing through transferTo:,
    primFailCode := 42.

So in the simulated image had executed experimentFailAndWakeOther at priority 71
which woke up experimentSuccessAndSleep at priority 72,
transfering execution to "myAdd: 14", and when that block finished,
execution transferred back experimentFailAndWakeOther with its saved
primFailCode.

Now #slowPrimitiveResponse returned to internalExecuteNewMethod
with succeeded := false   "correct per experiment"
and dropped through #internalExecuteNewMethod to #internalActivateNewMethod.
which does...
   methodHeader := objectMemory methodHeaderOf: newMethod.
where newMethod is...
    16r10DF5B0: a(n) CompiledMethod nbytes 48
    16rA0009 =327684 (16r50004)   16r5B0278 #remove:ifAbsent:
    16r5C8700 #ifNil:  16r14B5030 an AdditionalMethodState
    16r9D2A78 a ClassBinding #Process -> 16r0183C860
and not the expected #experimentFailAndWakeOther

So  /newMethod/  also needed to be saved across the context switch.
In the simulated image I added another ivar for this to Process for
this, and these lines into #transferTo:

    objectMemory
         storePointer: SuspendedNewMethodIndex
         ofObject: oldProc
         withValue: newMethod.
   newMethod := objectMemory
         fetchPointer: SuspendedNewMethodIndex
        ofObject: newProc.


Now an assert failed in #checkForAndFollowForwardedPrimitiveState
   self assert: (self saneFunctionPointerForFailureOfPrimIndex: primIndex)].

but we have correctly restored newMethod =
     16rC0DA18: a(n) CompiledMethod nbytes 34
     16r20009 =65540 (16r10004)
     16r1790AB0 #iAmMethodExperimentFailAndWakeOther
     16r5C6630 #primitiveFail  16r147A3B8 an AdditionalMethodState
     16r17D1090 a ClassBinding #OwnedLock -> 16r01116370

and correctly restored primIndex = 561.
but #saneFunctionPointerForFailureOfPrimIndex:
uses a a stale primitiveFunctionPointer which needs to be saved across
the context switch.

---------------
Now I am a little confused about  primitiveFunctionPointer.  By name I
would expect a pointer and to use #storePointer:toObject:withValue ,
but actually I find...
   primitiveFunctionPointer
        ==>  #primitiveSuspend    (which btw is stale)
   self functionPointerFor: primIndex inClass: objectMemory nilObject
       ==> #primitiveExperimentFailAndWakeOther    (which is correct)

So what store method should I use for primitiveFunctionPointer ?

cheers -ben


More information about the Vm-dev mailing list