[Stackless] added tasklet.throw

Kristján Valur Jónsson kristjan at ccpgames.com
Sat Apr 6 11:29:16 CEST 2013


    > I think I'm confused :-)

No wonder.  Let's assume you have a dead tasklet, S.

S.run() and S.insert()

will both throw a RuntimeError, complaining that a dead tasklet can't be run.

but s.raise_exception(IndexError)

will happily work.  the tasklet _will_ be scheduled to run and will raise this exception and die again.  In effect, you are resurrecting the tasklet, rebuilding it to a NULL frame, and running it again.  But a tasklet with a "null" frame is not really legal.  And resurrecting taskletss is illegal.  The reason this works currently is because of a quirk in the scheduling code.

And because s.throw() now supports non-immediate exception delivery, this behaviour can't be emulated safely.  PyTasklet_Insert() won't accept a dead tasklet.

So, I think it is cleaner to just disallow this, like s.run() and s.insert() already do.



> def tasklet_bootstrap(f, *args, **kwargs):
>   try:
>       ret = f(*args, **kwargs)
>   except:
>       # handle
> stackless.set_callable_wrapper(tasklet_bootstrap)
This is currently indirectly possible, by setting stackless.tasklettype (IIRC) wehre you can put a tasklet subclass that overrides the __new__ etc.
But the problem with both approaches is that an exception delivered before the tasklet has had the chance to run for the first time won't be handled by this.  Hence my suggestion to set a global handler.  Having a module level handler is fine, IMO.  Adding such a beast is also simple to do.  Perhaps I'll make the experiment.

K



________________________________
Frá: Richard Tew [richard.m.tew at gmail.com]
Sent: 5. apríl 2013 20:56
To: Kristján Valur Jónsson
Cc: The Stackless Python Mailing List
Efni: Re: [Stackless] added tasklet.throw

On Sat, Apr 6, 2013 at 5:13 AM, Kristján Valur Jónsson <kristjan at ccpgames.com<mailto:kristjan at ccpgames.com>> wrote:

Ok, I have committed a change to the 2.7 branch (didn’t want to do the full merging yet, until we stabilize):

Here’s the comment:



modifying edge case semantics of tasklet.throw()

1) sending to a new tasklet works like a running tasklet.  It is scheduled,

gets an unhandled exception, and things proceed as per usual in that case.

2) Sending to a dead tasklet causes a RuntimeError, unless one is sending

a TaskletExit, in which case it is just ignored.



This is now pretty much the same as tasklet.raise_exception().

The thing is though, that tasklet.raise_exception() and kill() behave in a strange way when the target is already dead.  The dead tasklet is actually scheduled and the scheduling mechanism is put in motion, even though its “frame” is null.  It is by pure chance that things don’t break.  So, the case is handled like a regular uncaught exception.  But this is strange, since a tasklet can by definition have only one such.

Another reason I couldn’t emulate that behaviour with tasklet.throw(), is because it is not possible, or nice, to do so with non-immediate effect.  we shouldn’t have a dead tasklet in the runnable queue.

Hence the change.  If the tasklet is dead, then senditng it an exception is clearly an error, much as trying to rebind a dead tasklet is.  Except for the kill, which I think ought to be a harmless special case to allow.

What do you think?

I think I'm confused :-)



Btw,

This does not preclude thinking more about how to deal with unhandled exceptions.  The current built-in behaviour is to invoke them on the mani tasklet.  But here is an idea:  How about defining a class method on the tasklet class, default_handler(exc, val, tb), which does this?  Then, frameworks and others can redefine this in a subclass.

The benefit of doing this, rather than having people wrap their main functions in try:except, is that this will work even when the tasklet hasn’t started yet, when the tasklet is fresh in the runnable queue.

Is there a need for multiple versions of this in the same Python runtime?  I'm not convinced, and would prefer that we kept it as simple as possible.  To me, it seems a better fit to do a more standard global "stackless.set_exception_handler".  But I'd prefer we don't take this approach at all.

When I think about this and recall different frameworks I've written and the things which got in the way, I'd prefer we made it more generic and flexible.  Why just have an exception handler?  I think it should be a globally applied callable wrapper for tasklets.  But rather than manually needing to subclass a tasklet, have it applied automatically.

e.g.

def tasklet_bootstrap(f, *args, **kwargs):
    try:
        ret = f(*args, **kwargs)
    except:
        # handle

stackless.set_callable_wrapper(tasklet_bootstrap)

Cheers,
Richard.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.stackless.com/pipermail/stackless/attachments/20130406/fc8dbed8/attachment-0001.html>


More information about the Stackless mailing list