Skip to content.

logo Stackless Python


Personal tools
Views

Idioms

last edited 1 year ago by rmtew

This is a collection of useful snippets of code which make using Stackless easier, or tweak it to behave a different way.

Being Nice

When you are using tasklets within another framework, sometimes you need to put that framework before Stackless. One way to do this is to use an alternate method of yielding a tasklet, other than ''stackless.schedule''. ''BeNice'' is another way.

Idiom:

yieldChannel = stackless.channel()

def BeNice():
    yieldChannel.receive()

def ScheduleTasklets():
    # Only schedule as many tasklets as there are waiting when
    # we start.  This is because some of the tasklets we awaken
    # may BeNice their way back onto the channel.
    n = -yieldChannel.balance
    while n > 0:
        yieldChannel.send(None)
        n -= 1

    # Run any scheduled tasklets.  We should never find ourselves
    # having interrupted one, as that would be more likely to indicate
    # an infinite loop, as tasklets should use BeNice and never end
    # up in the scheduler.
    interruptedTasklet = stackless.run(1000000)
    if interruptedTasklet:
        # Print a stacktrace for the tasklet.
        raise RuntimeError("Better handling needed")

def f():
    while True:
        BeNice()
stackless.tasklet(f)()

while True:
    ScheduleTasklets()
    # Handle framework messages.
    # Wait a while to reduce CPU usage?

Micromanaging

It is useful to be able to know when your tasklets actually run, have uncaught exceptions or are about to exit because the function they were bound to returned.

The simplest approach is to avoid subclassing stackless.tasklet and just bind a normal tasklet to a wrapping function instead of the desired one, then the wrapping function calls the desired one in turn. This either requires a custom function be used to create any tasklets or a little extra work when creating them.

Idiom:

def ManageTasklet(func, args, kwargs):
    print "The tasklet has just been run."
    try:
        func(*args, **kwargs)
    except Exception, e:
        print "Handle an exception."
        raise e
    print "Managed function returned."

def CreateTasklet(func, *args, **kwargs):
    return stackless.tasklet(ManageTasklet)(func, args, kwargs)

Example:

def f(*args, **kwargs):
    print "f called with", args, kwargs

t = CreateTasklet(f, 1, 2, 3, x=1)
t.run()

Output:

The tasklet has just been run.
f called with (1, 2, 3) {'x': 1}
Managed function returned.

A way to do the same thing, but to avoid a using the custom function, or extra boilerplate is to override the tasklet class's __call__ function. This way, any tasklets created naturally get managed how you want.

Idiom:

def newCall(self, *args, **kwargs):
    oldFunction = self.tempval
    def newFunction(oldFunction, args, kwargs):
        print "The tasklet has just been run."
        try:
            oldFunction(*args, **kwargs) 
        except Exception, e:
            print "Handle an exception."
            raise e
        print "Managed function returned."
    self.tempval = newFunction
    stackless.tasklet.setup(self, oldFunction, args, kwargs)
    return self

stackless.tasklet.__call__ = newCall

Example:

def f(*args, **kwargs):
    print "f called with", args, kwargs

t = stackless.tasklet(f)(1, 2, 3, x=1)
t.run()

Output:

The tasklet has just been run.
f called with (1, 2, 3) {'x': 1}
Managed function returned.

Naming

Naming your tasklets allows them to be distinguished from each other.

Idiom:

class NamedTasklet(stackless.tasklet):
    __slots__ = [ "name" ]

    def __str__(self):
        return "<NamedTasklet(%s)>" % self.name

def CreateNamedTasklet(name, func, *args, **kwargs):
    t = NamedTasklet(func)(*args, **kwargs)
    t.name = name
    return t

Sleeping

There is often a need to block a tasklet until a desired amount of time has passed. The simplest approach is to just reschedule it repeatedly, checking to see if the time to continue has arrived.

Idiom:

secondsToWait = 10.0
endTime = time.time() + secondsToWait
while time.time() < endTime:
    stackless.schedule()

However, having each tasklet that waits continually get rescheduled in order to recheck whether it should resume its activities can cause an unnecessarily heavy use of resources. It is convenient to write a common function that is called in order to block a tasklet until it should be awoken. Here is one way to do that.

Idiom - Sleep function for general use:

sleepingTasklets = []

def Sleep(secondsToWait):
    channel = stackless.channel()
    endTime = time.time() + secondsToWait
    sleepingTasklets.append((endTime, channel))
    sleepingTasklets.sort()
    # Block until we get sent an awakening notification.
    channel.receive()

def ManageSleepingTasklets():
    while 1:
        if len(sleepingTasklets):
            endTime = sleepingTasklets[0][0]
            if endTime <= time.time():
                channel = sleepingTasklets[0][1]
                del sleepingTasklets[0]
                # We have to send something, but it doesn't matter what as it is not used.
                channel.send(None)
        stackless.schedule()

stackless.tasklet(ManageSleepingTasklets)()

Example - Sleep function in use:

def TestSleep()
    print "pre", time.time()
    Sleep(5.0)
    print "post", time.time()
  
stackless.tasklet(TestSleep)()
stackless.run()

Output:

pre 1133381175.27
post 1133381180.28
<no more sleeping tasklets so ManageSleepingTasklets gets scheduled infinitely>

 

Powered by Plone