Retrying a message send

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

Retrying a message send

Igor Stasenko
Hello,

suppose you having a method:

foo
  <primitive: ... >
  self recompileAndRetry


a #recompileAndRetry should do following:
 1) recompile method #foo and install new version of #foo into class
 2) restart execution from callee of #foo, so that it will call the
same method again (but its renewed version)


This is similar to what debugger does, when you changing a method in
code pane and accepting it, except that it restarting a
context of called method, instead of restarting from a point where
caller context did a message send (and so, if a new method contains a
primitive,
it will be called by VM).

Any ideas, how i can do (2) easily?

My own thoughts is following:

recompileAndRetry
   | retryCtx retryMethod caller method rcvr |
   retryCtx := thisContext sender.
   caller := retryCtx sender.

   method := retryCtx method.
   "here we recompile a method, like following: "
   method := method methodClass recompile: method selector.

   "now we're resending the same message"
   args := Array new: method numArgs.
   1 to: args size do: [ :i |  args at: i put: (retryCtx tempAt: i) ].


   thisContext terminateTo: caller.
   ^ retryCtx receiver perform: method selector arguments: args


--
Best regards,
Igor Stasenko AKA sig.

Reply | Threaded
Open this post in threaded view
|

Re: Retrying a message send

Igor Stasenko
On 11 April 2010 11:33, Igor Stasenko <[hidden email]> wrote:

> Hello,
>
> suppose you having a method:
>
> foo
>  <primitive: ... >
>  self recompileAndRetry
>
>
> a #recompileAndRetry should do following:
>  1) recompile method #foo and install new version of #foo into class
>  2) restart execution from callee of #foo, so that it will call the
> same method again (but its renewed version)
>
>
> This is similar to what debugger does, when you changing a method in
> code pane and accepting it, except that it restarting a
> context of called method, instead of restarting from a point where
> caller context did a message send (and so, if a new method contains a
> primitive,
> it will be called by VM).
>
> Any ideas, how i can do (2) easily?
>
> My own thoughts is following:
>
> recompileAndRetry
>   | retryCtx retryMethod caller method rcvr |
>   retryCtx := thisContext sender.
>   caller := retryCtx sender.
>
>   method := retryCtx method.
>   "here we recompile a method, like following: "
>   method := method methodClass recompile: method selector.
>
>   "now we're resending the same message"
>   args := Array new: method numArgs.
>   1 to: args size do: [ :i |  args at: i put: (retryCtx tempAt: i) ].
>
>
>   thisContext terminateTo: caller.
>   ^ retryCtx receiver perform: method selector arguments: args
>

Forgot to add:
the problem with the above, that if i restart it in that way, a call
stack, when i enter the new #foo method will be following:

caller context -> #recompileAndRetry context  -> #foo

while i want, obviously, a clean stack state, as if this method
invoked naturally by interpreter:

caller context -> #foo


>
> --
> Best regards,
> Igor Stasenko AKA sig.
>



--
Best regards,
Igor Stasenko AKA sig.

Reply | Threaded
Open this post in threaded view
|

Re: Retrying a message send

Eliot Miranda-2
In reply to this post by Igor Stasenko


On Sun, Apr 11, 2010 at 1:33 AM, Igor Stasenko <[hidden email]> wrote:
Hello,

suppose you having a method:

foo
 <primitive: ... >
 self recompileAndRetry


a #recompileAndRetry should do following:
 1) recompile method #foo and install new version of #foo into class
 2) restart execution from callee of #foo, so that it will call the
same method again (but its renewed version)


This is similar to what debugger does, when you changing a method in
code pane and accepting it, except that it restarting a
context of called method, instead of restarting from a point where
caller context did a message send (and so, if a new method contains a
primitive,
it will be called by VM).

Any ideas, how i can do (2) easily?

One trick is to return the result of compileAndRetry, e.g.

foo
 <primitive: ... >
 ^self recompileAndRetry

or e.g.

foo
 <primitive: ... error: error>
 (self shouldRecompileAndRetry: error) ifTrue:
      [^self recompileAndRetry].
  self primitiveFailed
 
you then don't need to worry about munging the stack to get it right.  You simply answer the result of tail-calling the primitive again at the end of recompileAndRetry.  That's what VisualWorks does with FFI and user primitive calls that can fail because they're not yet linked.  Searching for symbols in libraries and loading libraries is all done with image code invoked from e.g. ^self externalCallError: errorCode.

HTH
Eliot


My own thoughts is following:

recompileAndRetry
  | retryCtx retryMethod caller method rcvr |
  retryCtx := thisContext sender.
  caller := retryCtx sender.

  method := retryCtx method.
  "here we recompile a method, like following: "
  method := method methodClass recompile: method selector.

  "now we're resending the same message"
  args := Array new: method numArgs.
  1 to: args size do: [ :i |  args at: i put: (retryCtx tempAt: i) ].


  thisContext terminateTo: caller.
  ^ retryCtx receiver perform: method selector arguments: args


--
Best regards,
Igor Stasenko AKA sig.




Reply | Threaded
Open this post in threaded view
|

Re: Retrying a message send

Igor Stasenko
On 12 April 2010 21:28, Eliot Miranda <[hidden email]> wrote:

>
>
> On Sun, Apr 11, 2010 at 1:33 AM, Igor Stasenko <[hidden email]> wrote:
>>
>> Hello,
>>
>> suppose you having a method:
>>
>> foo
>>  <primitive: ... >
>>  self recompileAndRetry
>>
>>
>> a #recompileAndRetry should do following:
>>  1) recompile method #foo and install new version of #foo into class
>>  2) restart execution from callee of #foo, so that it will call the
>> same method again (but its renewed version)
>>
>>
>> This is similar to what debugger does, when you changing a method in
>> code pane and accepting it, except that it restarting a
>> context of called method, instead of restarting from a point where
>> caller context did a message send (and so, if a new method contains a
>> primitive,
>> it will be called by VM).
>>
>> Any ideas, how i can do (2) easily?
>
> One trick is to return the result of compileAndRetry, e.g.
> foo
>  <primitive: ... >
>  ^self recompileAndRetry
> or e.g.
> foo
>  <primitive: ... error: error>
>  (self shouldRecompileAndRetry: error) ifTrue:
>       [^self recompileAndRetry].
>   self primitiveFailed
>
> you then don't need to worry about munging the stack to get it right.  You
> simply answer the result of tail-calling the primitive again at the end of
> recompileAndRetry.  That's what VisualWorks does with FFI and user primitive
> calls that can fail because they're not yet linked.  Searching for symbols
> in libraries and loading libraries is all done with image code invoked from
> e.g. ^self externalCallError: errorCode.

Thanks. That's what i actually came to myself:
- loading library
- loading library function
- generating callout code
- installing the code into a method
- perform the method as a tail-call with same arguments

I just was a little concerned that my method can't see its original
caller (a context which originally made a call),
so a method can't rely on a stack structure.

> HTH
> Eliot
>>
>> My own thoughts is following:
>>
>> recompileAndRetry
>>   | retryCtx retryMethod caller method rcvr |
>>   retryCtx := thisContext sender.
>>   caller := retryCtx sender.
>>
>>   method := retryCtx method.
>>   "here we recompile a method, like following: "
>>   method := method methodClass recompile: method selector.
>>
>>   "now we're resending the same message"
>>   args := Array new: method numArgs.
>>   1 to: args size do: [ :i |  args at: i put: (retryCtx tempAt: i) ].
>>
>>
>>   thisContext terminateTo: caller.
>>   ^ retryCtx receiver perform: method selector arguments: args
>>
>>
>> --
>> Best regards,
>> Igor Stasenko AKA sig.
>>
>
>
>
>
>



--
Best regards,
Igor Stasenko AKA sig.