Folks -
Another one of the issues I really need a solution for is simple callback support into the interpreter. For me, the simplest thing to do that can potentially support all the relevant use cases is to merely provide an interface that (via setjmp/longjmp) ensures that the C callstack remains in order, e.g., prevents returns "through" other callback frames. I'm thinking about an interface like here: int interpretEnter(int *cbID); - (Re-)Enters the interpreter. Different from interpret() in such that it creates an environment (via setjmp) to return to. Fills in the cbID with an ID that is used in the return call. int interpretExit(int cbID); - Returns from a previous call to interpretEnter() with the provided callback ID. Both of the above functions may fail - interpretEnter() would fail if for some reason the interpreter cannot be entered (no setjmp/longjmp support, or failure to allocate jmpbuf). interpretExit() would fail if the callback ID is not the last "matching" ID. Note that the above does NOT make any assumptions on the means by which the VM gets notified about the occurrence of the callback itself nor any arguments etc. It is up to the plugin to provide that information by its idiosyncratic means. Question: Does anyone have a problem with the above? I'm trying to design "just enough" so that projects like wxSqueak or others can cooperate without knowing too much about each other. Cheers, - Andreas |
Last time I used setjmp/longjmp was probably 1990. Back then it was
considered a seriously tacky thing to do and a cause of lost performance. I think BrouHaHa got non-trivially faster after I beat Eliot up to get him to stop using it in the interpreter code. Is it less of an issue these days? tim -- tim Rowledge; [hidden email]; http://www.rowledge.org/tim Strange OpCodes: TOAC: Turn Off Air Conditioner |
Hi Tim -
setjmp/longjmp are probably slow as hell but the real issue is whether we want to keep changes minimalistic and local (I do) or whether we're up for some serious rework of other parts (I'm not). An alternative I have pondered was to give interpret() an argument, namely a pointer to a variable that external code can set when it's time to return. This would solve the problem without doing setjmp/longjmp, BUT it would come at the cost of a pointer-dereferencing and test at every single bytecode (at the very least at each primitiveResponse but there are gotchas with that) and would potentially defeat the dispatch optimization in gnuification. Knowing you guys (and knowing myself too) those tradeoffs would be completely unacceptable for the VM in general - if you want to use callbacks and you're willing to pay a price for that then you really should pay the price when you use the callback and not elsewhere. That's why I'm opting for a setjmp/longjmp solution. Besides, given the simplistic interface if another implementation becomes available it should be possible to implement an interface as trivial as the one proposed here. Since the interface doesn't expose anything about its implementation (e.g., all you get is an opaque callback ID) it's quite literally up to the VM how it wants to implement the semantics properly. It could even do this via threading and IPC if that were of interest. Cheers, - Andreas tim Rowledge wrote: > Last time I used setjmp/longjmp was probably 1990. Back then it was > considered a seriously tacky thing to do and a cause of lost > performance. I think BrouHaHa got non-trivially faster after I beat > Eliot up to get him to stop using it in the interpreter code. > > Is it less of an issue these days? > > tim > -- > tim Rowledge; [hidden email]; http://www.rowledge.org/tim > Strange OpCodes: TOAC: Turn Off Air Conditioner > > > |
> An alternative I have pondered was to give interpret() an argument,
> namely a pointer to a variable that external code can set when it's > time to return. This would solve the problem without doing > setjmp/longjmp, BUT it would come at the cost of a > pointer-dereferencing and test at every single bytecode (at the very > least at each primitiveResponse but there are gotchas with that) and > would potentially defeat the dispatch optimization in gnuification. I don't know the vm internals at all, but a standard place to put such tests in other contexts is on backward-jumps and method-returns (maybe that's what primitiveResponse is). The cost of that wouldn't be too significant. ../Dave |
On 6-Jun-06, at 12:59 PM, Dave Mason wrote: >> An alternative I have pondered was to give interpret() an argument, >> namely a pointer to a variable that external code can set when it's >> time to return. This would solve the problem without doing >> setjmp/longjmp, BUT it would come at the cost of a >> pointer-dereferencing and test at every single bytecode (at the very >> least at each primitiveResponse but there are gotchas with that) and >> would potentially defeat the dispatch optimization in gnuification. > > I don't know the vm internals at all, but a standard place to put such > tests in other contexts is on backward-jumps and method-returns (maybe > that's what primitiveResponse is). The cost of that wouldn't be too > significant. That's where the interruptCheck stuff is done Dave; and if it is plausible to hang off that (?) then all that would need doing is to call forceInterruptCheck() from the plugin to get a fuller check ASAP. IF (big if in glowing capitals) handling callbacks could be dealt with within the typical interrupt checking work it would be a bit less onerous than at arbitrary points. How are you proposing to deal with the callback at the higher levels? Is the plugin going to be allowed to cons up a message and 'send' it? Or.....? tim -- tim Rowledge; [hidden email]; http://www.rowledge.org/tim Useful random insult:- Settled some during shipping and handling. |
The older (way older) macintosh os-8.x based netscape browser plugin
would run the interpreter loop for N instructions (milliseconds) I think, then exit the loop, thus halting the envrionment and return control to the browser, on the next call from the browser we would reenter the interpreter loop and process the next bytecode as if nothing happened... I believe stubs for that logic still exist. On 6-Jun-06, at 1:10 PM, tim Rowledge wrote: > > On 6-Jun-06, at 12:59 PM, Dave Mason wrote: > >>> An alternative I have pondered was to give interpret() an argument, >>> namely a pointer to a variable that external code can set when it's >>> time to return. This would solve the problem without doing >>> setjmp/longjmp, BUT it would come at the cost of a >>> pointer-dereferencing and test at every single bytecode (at the very >>> least at each primitiveResponse but there are gotchas with that) and >>> would potentially defeat the dispatch optimization in gnuification. >> >> I don't know the vm internals at all, but a standard place to put >> such >> tests in other contexts is on backward-jumps and method-returns >> (maybe >> that's what primitiveResponse is). The cost of that wouldn't be too >> significant. > > That's where the interruptCheck stuff is done Dave; and if it is > plausible to hang off that (?) then all that would need doing is to > call forceInterruptCheck() from the plugin to get a fuller check > ASAP. IF (big if in glowing capitals) handling callbacks could be > dealt with within the typical interrupt checking work it would be a > bit less onerous than at arbitrary points. > > How are you proposing to deal with the callback at the higher > levels? Is the plugin going to be allowed to cons up a message and > 'send' it? Or.....? > > tim > -- > tim Rowledge; [hidden email]; http://www.rowledge.org/tim > Useful random insult:- Settled some during shipping and handling. > > -- ======================================================================== === John M. McIntosh <[hidden email]> 1-800-477-2659 Corporate Smalltalk Consulting Ltd. http://www.smalltalkconsulting.com ======================================================================== === |
In reply to this post by timrowledge
tim Rowledge wrote:
> How are you proposing to deal with the callback at the higher levels? Is > the plugin going to be allowed to cons up a message and 'send' it? Or.....? This should be up to the plugin. I can't think of a good interface right now given that we don't have enough examples to look at and learn from. Personally, I'm just signaling a semaphore before the callback and have the image "pull up" the arguments via primitives. This avoids the need to do too much at the primitive level. Cheers, - Andreas |
On 6-Jun-06, at 5:00 PM, Andreas Raab wrote: > tim Rowledge wrote: >> How are you proposing to deal with the callback at the higher >> levels? Is the plugin going to be allowed to cons up a message and >> 'send' it? Or.....? > > This should be up to the plugin. I can't think of a good interface > right now given that we don't have enough examples to look at and > learn from. Personally, I'm just signaling a semaphore before the > callback and have the image "pull up" the arguments via primitives. > This avoids the need to do too much at the primitive level. So would simply using the normal semaphore signalling be ok? We already have external semaphore capability so it would certainly be simple to provide! If we implemented callbacks by taking a block (or a MessageSend?), making a Process that will use it, make the process wait on a Semaphore and pass the semaphore index down to the plugin, then signalling the semaphore would run the block/whatever and presumably ask the plugin for relevant arguments. As long as it can be non-synchronous that should work for a lot of cases, surely? tim -- tim Rowledge; [hidden email]; http://www.rowledge.org/tim Klingon Code Warrior:- 9) "A TRUE Klingon warrior does not comment his code!" |
tim Rowledge wrote:
> > On 6-Jun-06, at 5:00 PM, Andreas Raab wrote: > >> tim Rowledge wrote: >> >>> How are you proposing to deal with the callback at the higher >>> levels? Is the plugin going to be allowed to cons up a message and >>> 'send' it? Or.....? >> >> >> This should be up to the plugin. I can't think of a good interface >> right now given that we don't have enough examples to look at and >> learn from. Personally, I'm just signaling a semaphore before the >> callback and have the image "pull up" the arguments via primitives. >> This avoids the need to do too much at the primitive level. > > > So would simply using the normal semaphore signalling be ok? We already > have external semaphore capability so it would certainly be simple to > provide! If we implemented callbacks by taking a block (or a > MessageSend?), making a Process that will use it, make the process wait > on a Semaphore and pass the semaphore index down to the plugin, then > signalling the semaphore would run the block/whatever and presumably > ask the plugin for relevant arguments. As long as it can be > non-synchronous that should work for a lot of cases, surely? > Hi guys, This is basically what I'm currently doing in wxSqueak. I have two different flavors of callbacks, one for event loops and another for other callbacks. The only real difference between these two is that the non-event callbacks have to handle a variety of arguments. In both cases the callbacks must appear to be handled synchronously from the viewpoint of the (wx) calling routine. The only serious difficulty I've encountered with this scheme is with nested callbacks. I have a number of cases where a callback handler triggers another callback. The problem is that when the second callback is handled, the process in which the first primitive was called may resume prematurely, causing the first primitive to get an incorrect return value. The only way I found to reliably handle this scenario is to have the first primitive pass in another semaphore and a pointer in which to store the return value, and have the primitive signal the semphore when it's done. It's a bit ugly, but it does work. It would be much better if the processes involved could be controlled by the framework. Do you have any thoughts on how this could be handled better? .. Rob |
> Do you have any thoughts on how this could be handled better?
How about we just suspend the currently active process when we re-enter the interpreter? As long as the callback is synchronous (it better be) this should work just fine, right? Cheers, - Andreas Rob Gayvert wrote: > tim Rowledge wrote: >> >> On 6-Jun-06, at 5:00 PM, Andreas Raab wrote: >> >>> tim Rowledge wrote: >>> >>>> How are you proposing to deal with the callback at the higher >>>> levels? Is the plugin going to be allowed to cons up a message and >>>> 'send' it? Or.....? >>> >>> >>> This should be up to the plugin. I can't think of a good interface >>> right now given that we don't have enough examples to look at and >>> learn from. Personally, I'm just signaling a semaphore before the >>> callback and have the image "pull up" the arguments via primitives. >>> This avoids the need to do too much at the primitive level. >> >> >> So would simply using the normal semaphore signalling be ok? We >> already have external semaphore capability so it would certainly be >> simple to provide! If we implemented callbacks by taking a block (or >> a MessageSend?), making a Process that will use it, make the process >> wait on a Semaphore and pass the semaphore index down to the plugin, >> then signalling the semaphore would run the block/whatever and >> presumably ask the plugin for relevant arguments. As long as it can >> be non-synchronous that should work for a lot of cases, surely? >> > > Hi guys, > > This is basically what I'm currently doing in wxSqueak. I have two > different flavors of callbacks, one for event loops and another for > other callbacks. The only real difference between these two is that the > non-event callbacks have to handle a variety of arguments. In both cases > the callbacks must appear to be handled synchronously from the viewpoint > of the (wx) calling routine. The only serious difficulty I've > encountered with this scheme is with nested callbacks. I have a number > of cases where a callback handler triggers another callback. The problem > is that when the second callback is handled, the process in which the > first primitive was called may resume prematurely, causing the first > primitive to get an incorrect return value. > > The only way I found to reliably handle this scenario is to have the > first primitive pass in another semaphore and a pointer in which to > store the return value, and have the primitive signal the semphore when > it's done. It's a bit ugly, but it does work. It would be much better if > the processes involved could be controlled by the framework. > > Do you have any thoughts on how this could be handled better? > > .. Rob > > > > > > |
Andreas Raab wrote:
> > Do you have any thoughts on how this could be handled better? > > How about we just suspend the currently active process when we re-enter > the interpreter? As long as the callback is synchronous (it better be) > this should work just fine, right? Yes, that should do the trick. I tried doing something like this awhile back, but couldn't get it to work reliably. I suspect the problem was that the process oop might move during a callback, in which case trying to resume it would cause a crash. It sounds like your other changes to keep oops across primitives would solve this. .. Rob > > Cheers, > - Andreas > > Rob Gayvert wrote: > >> tim Rowledge wrote: >> >>> >>> On 6-Jun-06, at 5:00 PM, Andreas Raab wrote: >>> >>>> tim Rowledge wrote: >>>> >>>>> How are you proposing to deal with the callback at the higher >>>>> levels? Is the plugin going to be allowed to cons up a message and >>>>> 'send' it? Or.....? >>>> >>>> >>>> >>>> This should be up to the plugin. I can't think of a good interface >>>> right now given that we don't have enough examples to look at and >>>> learn from. Personally, I'm just signaling a semaphore before the >>>> callback and have the image "pull up" the arguments via primitives. >>>> This avoids the need to do too much at the primitive level. >>> >>> >>> >>> So would simply using the normal semaphore signalling be ok? We >>> already have external semaphore capability so it would certainly be >>> simple to provide! If we implemented callbacks by taking a block (or >>> a MessageSend?), making a Process that will use it, make the process >>> wait on a Semaphore and pass the semaphore index down to the >>> plugin, then signalling the semaphore would run the block/whatever >>> and presumably ask the plugin for relevant arguments. As long as it >>> can be non-synchronous that should work for a lot of cases, surely? >>> >> >> Hi guys, >> >> This is basically what I'm currently doing in wxSqueak. I have two >> different flavors of callbacks, one for event loops and another for >> other callbacks. The only real difference between these two is that >> the non-event callbacks have to handle a variety of arguments. In both >> cases the callbacks must appear to be handled synchronously from the >> viewpoint of the (wx) calling routine. The only serious difficulty >> I've encountered with this scheme is with nested callbacks. I have a >> number of cases where a callback handler triggers another callback. >> The problem is that when the second callback is handled, the process >> in which the first primitive was called may resume prematurely, >> causing the first primitive to get an incorrect return value. >> >> The only way I found to reliably handle this scenario is to have the >> first primitive pass in another semaphore and a pointer in which to >> store the return value, and have the primitive signal the semphore >> when it's done. It's a bit ugly, but it does work. It would be much >> better if the processes involved could be controlled by the framework. >> >> Do you have any thoughts on how this could be handled better? >> >> .. Rob >> >> >> >> >> >> > |
Hi Rob -
Rob Gayvert wrote: > Andreas Raab wrote: >> > Do you have any thoughts on how this could be handled better? >> >> How about we just suspend the currently active process when we >> re-enter the interpreter? As long as the callback is synchronous (it >> better be) this should work just fine, right? > > Yes, that should do the trick. I tried doing something like this awhile > back, but couldn't get it to work reliably. I suspect the problem was > that the process oop might move during a callback, in which case trying > to resume it would cause a crash. It sounds like your other changes to > keep oops across primitives would solve this. Yes, although (if you look at the changes) I decided to keep this internal in the VM because it is also necessary to ensure proper resumption upon callback return (otherwise the return value will be inaccurate). So the ultimate solution goes like this: Upon entering the callback, suspend the active process forcefully. Upon returning from the callback resume the active process forcefully(!) so that the "returning primitive" (e.g., the primitive which invoked the callback) can set the proper return value. As far as I am aware that solves the trick completely (check out the code) at the cost of requiring that callbacks are only entered during the execution of primitives (meaning that the interpreter's state is externalized). Cheers, - Andreas |
Free forum by Nabble | Edit this page |