Tasklets — Lightweight threads

Tasklets wrap functions, allowing them to be launched as microthreads to berun within the scheduler.

Launching a tasklet:

stackless.tasklet(callable)(*args,**kwargs)

That is the most common way of launching a tasklet. This does not just createa tasklet, but it also automatically inserts the created tasklet into thescheduler.

Example - launching a more concrete tasklet:

>>>deffunc(*args,**kwargs):...print("scheduled with",args,"and",kwargs)...>>>stackless.tasklet(func)(1,2,3,string="test")<stackless.tasklet object at 0x01C58030>>>>stackless.run()scheduled with (1, 2, 3) and {'string': 'test'}

Tasklets, main, current and more

There are two especially notable tasklets, the main tasklet and the currenttasklet.

The main tasklet is fixed, and it is the initial thread of execution ofyour application. Chances are that it is running the scheduler.

The current tasklet however, is the tasklet that is currently running. Itmight be the main tasklet, if no other tasklets are being run. Otherwise,it is the entry in the scheduler’s chain of runnable tasklets, that iscurrently executing.

Example - is the main tasklet the current tasklet:

stackless.main==stackless.current

Example - is the current tasklet the main tasklet:

stackless.current.is_main==1

Example - how many tasklets are scheduled:

stackless.runcount

Note

The main tasklet factors into thestackless.runcount value. If youare checking how many tasklets are in the scheduler from your main loop,you need to keep in mind that there will be another tasklet in there overand above the ones you explicitly created.

Thetasklet class

classtasklet(func=None,args=None,kwargs=None)

This class exposes the form of lightweight thread (the tasklet) provided byStackless-Python. Wrapping a callable object and arguments to pass intoit when it is invoked, the callable is run within the tasklet.

Tasklets are usually created in the following manner:

>>>stackless.tasklet(func)(1,2,3,name="test")

The above code is equivalent to:

>>>t=stackless.tasklet()>>>t.bind(func)>>>t.setup(1,2,3,name="test")

and

>>>t=stackless.tasklet()>>>t.bind(func,(1,2,3),{"name":"test"})>>>t.insert()

In fact, thetasklet.__init__ method just callstasklet.bind()and thetasklet.__call__ method callstasklet.setup().

Note that when an implicittasklet.insert() is invoked, there is no needto hold a reference to the created tasklet.

tasklet.bind(func=None,args=None,kwargs=None)

Bind the tasklet to the given callable object,func:

>>>t=stackless.tasklet()>>>t.bind(func)

In most every case, programmers will instead passfunc into the taskletconstructor:

>>>t=stackless.tasklet(func)

Note that the tasklet cannot be run until it has been provided witharguments to callfunc. They can be provided asargs and/orkwargsto this function, or through a subsequent call totasklet.setup(). The difference is that when providing them totasklet.bind(), the tasklet is not made runnable yet.

func can be None when providing arguments, in which case a previous calltotasklet.bind() must have provided the function.

To clear the binding of a tasklet set all arguments toNone. Thisis especially useful, if you run a tasklet only partially:

>>>deffunc():...try:......# part 1...stackless.schedule_remove()......# part 2...finally:......# cleanup>>>t=stackless.tasklet(func)()>>>stackless.enable_softswitch(True)>>>stackless.run()# execute part 1 of func>>>t.bind(None)# unbind func(). Don't execute the finally block

If a tasklet is alive, it can be rebound only if the tasklet isnot the current tasklet and if the tasklet is not scheduled andif the tasklet is restorable.bind() raisesRuntimeError,if these conditions are not met.

tasklet.setup(*args,**kwargs)

Provide the tasklet with arguments to pass into its bound callable:

>>>t=stackless.tasklet()>>>t.bind(func)>>>t.setup(1,2,name="test")

In most every case, programmers will instead pass the arguments andcallable into the tasklet constructor instead:

>>>t=stackless.tasklet(func)(1,2,name="test")

Note that when tasklets have been bound to a callable object andprovided with arguments to pass to it, they are implicitlyscheduled and will be run in turn when the scheduler is next run.

The methodsetup() is equivalent to:

>>>defsetup(self,*args,**kwargs):>>>assertisinstance(self,stackless.tasklet)>>>withstackless.atomic():>>>ifself.alive:>>>raise(RuntimeError("tasklet is alive")>>>self.bind(None,args,kwargs)>>>self.insert()>>>returnself
tasklet.insert()

Insert a tasklet at the end of the scheduler runnables queue, given that it isn’t blocked.Blocked tasklets need to be reactivated by channels.

tasklet.remove()

Remove a tasklet from the runnables queue.

Note

If this tasklet has a non-trivial C-state attached, Stacklesswill kill the tasklet when the containing thread terminates.Since this will happen in some unpredictable order, it may cause unwantedside-effects. Therefore it is recommended to either run tasklets to theend or to explicitlykill() them.

tasklet.run()

If the tasklet is alive and not blocked on a channel, then it will be runimmediately. However, this behaves differently depending on whetherthe tasklet is in the scheduler’s chain of runnable tasklets.

Example - running a tasklet that is scheduled:

>>>deff(name):...whileTrue:...c=stackless.current...m=stackless.main...assertc.scheduled...print("%s id=%s, next.id=%s, main.id=%s, main.scheduled=%r"%(name,id(c),id(c.next),id(m),m.scheduled))...stackless.schedule()...>>>t1=stackless.tasklet(f)("t1")>>>t2=stackless.tasklet(f)("t2")>>>t3=stackless.tasklet(f)("t3")>>>>>>t1.run()t1 id=36355632, next.id=36355504, main.id=30571120, main.scheduled=Truet2 id=36355504, next.id=36355888, main.id=30571120, main.scheduled=Truet3 id=36355888, next.id=30571120, main.id=30571120, main.scheduled=True

What you see here is thatt1 is not the only tasklet that ran. Whent1yields, the next tasklet in the chain is scheduled and so forth until thetasklet that actually rant1 - that is the main tasklet - is scheduled andresumes execution.

If you were to runt2 instead oft1, then we would have only seen theoutput oft2 andt3, because the tasklet callingrun is beforet1 in the chain.

Removing the tasklet to be run from the scheduler before it is actuallyrun, gives more predictable results as shown in the following example. Butkeep in mind that the scheduler is still being run and the chain is stillinvolved, the only reason it looks correct is tht the act of removing thetasklet effectively moves it before the tasklet that callsremove().

Example - running a tasklet that is not scheduled:

>>>t2.remove()<stackless.tasklet object at 0x022ABDB0>>>>t2.run()t2 id=36355504, next.id=36356016, main.id=36356016, main.scheduled=True>>>t2.scheduledTrue

While the ability to run a tasklet directly is useful on occasion, thatthe scheduler is still involved and that this is merely directing itsoperation in limited ways, is something you need to be aware of.

tasklet.switch()

Similar totasklet.run() except that the calling tasklet ispaused. This function can be used to implementraw scheduling without involvingthe scheduling queue.

The target tasklet must belong to the same thread as the caller.

Example - switch to a tasklet that is scheduled. Function f is defined asin the previous example:

>>> t1 = stackless.tasklet(f)("t1")>>> t2 = stackless.tasklet(f)("t2")>>> t3 = stackless.tasklet(f)("t3")>>> t1.switch()t1 id=36413744, next.id=36413808, main.id=36413680, main.scheduled=Falset2 id=36413808, next.id=36413872, main.id=36413680, main.scheduled=Falset3 id=36413872, next.id=36413744, main.id=36413680, main.scheduled=Falset1 id=36413744, next.id=36413808, main.id=36413680, main.scheduled=Falset2 id=36413808, next.id=36413872, main.id=36413680, main.scheduled=Falset3 id=36413872, next.id=36413744, main.id=36413680, main.scheduled=Falset1 id=36413744, next.id=36413808, main.id=36413680, main.scheduled=False...Traceback (most recent call last):  File "<stdin>", line 1, in <module>  File "<stdin>", line 6, in fKeyboardInterrupt>>>

What you see here is that the main tasklet was removed from the scheduler.Therefore the scheduler runs until it got interrupted by a keyboard interrupt.

tasklet.raise_exception(exc_class,*args)

Raise an exception on the given tasklet.exc_class is required to be asub-class ofException. It is instantiated with the given argumentsargs and raised within the given tasklet.

In order to make best use of this function, you should be familiar withhow tasklets and the schedulerdeal with exceptions, and the purpose of theTaskletExitexception.

If you try to raise an exception on a tasklet, that is not alive, the methodfails, except ifexc_class isTaskletExit and the tasklet already ended.

Changed in version 3.3.7:In case of an error Stackless versions before 3.3.7 raiseexc_class(*args).Later versions raisesRuntimeError.

tasklet.throw(exc=None,val=None,tb=None,pending=False)

Raise an exception on the given tasklet. The semantics are similarto theraise keywords, and so, this can be used to send an existingexception to the tasklet.

ifpending evaluates to True, then the target tasklet will be maderunnable and the caller continues. Otherwise, the target will be insertedbefore the current tasklet in the queue and switched to immediately.

If you try to raise an exception on a tasklet, that is not alive, the methodraisesRuntimeError on the caller. There is one exception:you can safely raiseTaskletExit, if the tasklet already ended.

tasklet.kill(pending=False)

Terminates the tasklet and unblocks it, if the tasklet was blockedon a channel. If the tasklet already ran to its end, the method doesnothing. If the tasklet has no thread, the method simply ends thetasklet. Otherwise it raises theTaskletExit exceptionon the tasklet.pending has the same meaning as fortasklet.throw().

This can be considered to be shorthand for:

>>>ift.alive:>>>t.throw(TaskletExit,pending=pending)
tasklet.set_atomic(flag)

This method is used to construct a block of code within which the taskletwill not be auto-scheduled when preemptive scheduling. It is useful forwrapping critical sections that should not be interrupted:

old_value=t.set_atomic(1)# Implement unsafe logic here.t.set_atomic(old_value)

Note that this will also prevent involuntary thread switching, i.e. thethread will hang on to theGIL for the duration.

tasklet.bind_thread([thread_id])

Rebind the tasklet to the current thread, or aPython® thread withthe giventhread_id.

This is only safe to do with just-created tasklets, or soft-switchabletasklets. This is the case when a tasklet has just been unpickled. Thenit can be useful in order to hand it off to a different thread for execution.

The relationship between tasklets and threads iscovered elsewhere.

tasklet.set_ignore_nesting(flag)

It is probably best not to use this until you understand nesting levels:

old_value=t.set_ignore_nesting(1)# Implement unsafe logic here.t.set_ignore_nesting(old_value)

The following (read-only) attributes allow tasklet state to be checked:

tasklet.alive

This attribute isTrue while a tasklet is still running. Tasklets thatare not running will most likely have either run to completion and exited,or will have unexpectedly exited through an exception of some kind.

tasklet.paused

This attribute isTrue when a tasklet is alive, but not scheduled orblocked on a channel. This state is entered after atasklet.bind() with2 or 3 arguments, atasklet.remove() or by the main tasklet, when itis acting as a watchdog.

tasklet.blocked

This attribute isTrue when a tasklet is blocked on a channel.

tasklet.scheduled

This attribute isTrue when the tasklet is either in the runnables listor blocked on a channel.

tasklet.restorable

This attribute isTrue, if the tasklet can be completely restored bypickling/unpickling. If a tasklet is restorable, it is possible to continuerunning the unpickled tasklet from whatever point in execution it may be.

All tasklets can be pickled for debugging/inspectionpurposes, but an unpickled tasklet might have lost runtime information (C stack).For the tasklet to be runnable, it must not have lost runtime information(C stack usage for instance).

The following attributes allow checking of user set situations:

tasklet.atomic

This attribute isTrue while this tasklet is within atasklet.set_atomic() block

tasklet.block_trap

Setting this attribute toTrue prevents the tasklet from being blockedon a channel.

tasklet.ignore_nesting

This attribute isTrue while this tasklet is within atasklet.set_ignore_nesting() block

The following attributes allow identification of tasklet place:

tasklet.is_current

This attribute isTrue if the tasklet is the current taskletof the thread it belongs to. To see if a tasklet is the currently executingtasklet in the current thread use the followingPython® code:

importstacklessdefis_current(tasklet):returntaskletisstackless.current
tasklet.is_main

This attribute isTrue if the tasklet is the main tasklet of the thread itbelongs to. To check if a tasklet is the main tasklet of the current threaduse the followingPython® code:

importstacklessdefis_current_main(tasklet):returntaskletisstackless.main
tasklet.thread_id

This attribute is the id of the thread the tasklet belongs to. If itsthread has terminated, the attribute value is-1.

The relationship between tasklets and threads iscovered elsewhere.

In almost every case, tasklets will be linked into a chain of tasklets. Thismight be the scheduler itself, otherwise it will be a channel the tasklet isblocked on.

The following attributes allow a tasklets place in a chain to be identified:

tasklet.prev

The previous tasklet in the chain that this tasklet is linked into.

tasklet.next

The next tasklet in the chain that this tasklet is linked into.

The following attributes are intended only for implementing debuggers,profilers, coverage tools and the like. Their behavior is part of theimplementation platform, rather than part of the language definition,and thus may not be available in allStackless-Python implementations.

tasklet.trace_function
tasklet.profile_function

The trace / profile function of the tasklet. These attributesare the tasklet counterparts of the functionssys.settrace(),sys.gettrace(),sys.setprofile() andsys.getprofile().

Tasklet Life Cycle

Here is a somewhat simplified state chart that shows the life cycle of atasklet instance. The chart does not show the nesting-level, the thread-idand the flags atomic, ignore-nesting, block-trap and restorable.

../../_images/tasklet_state_chart.png

Furthermore the diagram does not show the scheduler functionsstackless.run(),stackless.schedule() andstackless.schedule_remove(). For the purpose of understanding thestate transitions these functions are roughly equivalent to the followingPython® definitions:

defrun():main=stackless.currentdefwatchdog():whilestackless.runcount>1:stackless.current.next.run()main.switch()stackless.tasklet(watchdog)().switch()defschedule():stackless.current.next.run()defschedule_remove():stackless.current.next.switch()