Stackless notes #1
Creating a tasklet
When you create a tasklet, it is automatically scheduled. This places it at the end of the list of scheduled tasklets, and out of all the tasklets currently waiting to run, it will get its turn last.
Scheduling a new tasklet:
>>> def f():There is no need to hold a reference to the tasklet to keep it alive. The scheduler holds a reference to all scheduled tasklets.
... pass
...
>>> stackless.tasklet(f)()
Binding a callable to a tasklet:
>>> def f(arg, kwarg=None):Supplying the parameters for the callable:
... pass
...
>>> t = stackless.tasklet(f)
>>> t.scheduled
False
>>> t.alive
False
>>> t = stackless.tasklet(f)Running the scheduler
>>> t(1, kwarg=2)
<stackless.tasklet object="" at="" 0x01b031b0="">
>>> t.alive
True
>>> t.scheduled
True
Once started, it will continue to run until it has no scheduled tasklets remaining.
>>> def f():If a tasklet never exits, the scheduler will continue to run forever. This would happen with the following function:
... print "scheduled once"
... stackless.schedule()
... print "scheduled twice"
...
>>> stackless.tasklet(f)()
<stackless.tasklet object at 0x01B03170>
>>> stackless.run()
scheduled once
scheduled twice
>>>
>>> def f():Running the scheduler normally
... while True:
... stackless.schedule()
...
>>>
As already shown, the normal way to use Stackless Python, is to create your tasklets and then to run the scheduler until they have all either blocked out of it, or run to completion.
An example function to schedule as a tasklet:
def SomeFunction():Create and schedule the tasklet:
while not shouldExit:
# Do whatever it is this tasklet is supposed to do.
pass
# We need to yield to allow the other scheduled tasklets to run.
stackless.schedule()
stackless.tasklet(SomeFunction)()Run the scheduler until there are no remaining scheduled tasklets:
stackless.run()Using this approach, Stackless is a framework and it drives the running of your application. If your application needs to do something, then it needs to create a tasklet to run alongside the others in the scheduler.
Running the scheduler in a more flexible way
It is often less constraining not to have to shape your program to fit inside a framework. This is not how Stackless was used above, in the normal manner. But it can be done by approaching things a little bit differently.
This is what we do in EVE Online, as Kristján Valur Jónsson described in his presentation at PyCon 2006. The approach is described in the Stackless Python idioms wiki page, as the 'Being Nice' idiom.
In a nutshell, rather than yielding your tasklets to the end of the scheduled tasklet list, you yield them onto a channel and out of the scheduler. This is the 'BeNice' function:
def LoopingFunction():The scheduler will then exit after each tasklet has been run once. Then when you next get to the point when you can run the scheduler again, you first wake up each tasklet on your yield channel, then run the scheduler.
BeNice()
def MainLoop():The Stackless 'schedule' method should not be used to yield any more, otherwise you will block on the scheduler running until the tasklets which use it are blocked off the scheduler in some other way, or have exited.
while not exit:
RunNiceTasklets()
stackless.run()
An example implementation of this is provided in the uthread module.
import uthreadOf course, as is, it is not much use. You need to integrate your logic into the loop within your own version of the 'Run' function. Otherwise, you are not much better off than just using Stackless normally.
def LoopingFunction():
uthread.BeNice()
if __name__ == "__main__":
uthread.Run()