[Stackless] Explicit yield/break to Preemptive Stackless Loop
Brian Hardie
bhardie at cs.usfca.edu
Fri Nov 3 02:10:08 CET 2006
Hi Jas,
Thanks for the quick reply, I greatly appreciate it.
I think I should describe my situation a bit more. I am adding to an
existing code base which is built with threads. I am not using
stackless to replace threads, but to complement them. I want to run
a tasklet in a separate Python thread. This thread will serve as the
"controller" for the tasklet. The main reason I want to run a
tasklet is to be able to pickle the it at arbitrary points during
execution. My hope was that I could use preemptive scheduling with
stackless.run(n) to run the tasklet for a fixed number of
instructions, then check to see if it is time to stop doing work and
perhaps be pickled. However, I also need the ability to have the
tasklet explicitly return to the "control" loop, that is, the thread
that calls stackless.run(n).
So, I'm looking for a reliable way to have a single tasklet return
control. While the channel solution that you described works
perfectly well in a single threaded situation, it seems that the
introduction of threads causes channels to not work as expected. I'm
not sure if this is a bug in stackless or not. It seems that others
have successfully used stackless and threads together at the same
time (http://svn.python.org/projects/stackless/sandbox/examples/
threadscheduling.py). When running multi-threaded code that utilizes
the 'channel yield', the thread hangs when the tasklet calls
ch.receive. It seems that it gets removed from the runnable queue,
but that somewhere stackless doesn't realize that the queue is empty
and that it should fall back to the main loop.
Is this something that should be possible without adding to the
stackless c code?
I have included a sample program that demonstrates the problem:
from threading import Thread
import stackless
class myThread(Thread):
def __init__(self, i):
self.i = i
Thread.__init__(self)
def run(self):
if self.i:
self.stackloop()
else:
self.i_count()
def stackloop(self):
self.ch = stackless.channel()
t1 = stackless.tasklet(self.count)()
while stackless.getruncount() > 1:
t = stackless.run(10)
if self.ch.balance != 0:
print "YIELDED"
stackless.tasket(self.sendWakeup)()
raw_input("ENTER TO CONTINUE")
if t:
t.insert()
def sendWakeup(self):
self.ch.send()
def count(self):
i = 0
while 1:
print i
i += 1
self.ch.receive()
def i_count(self):
i=0
while 1:
i+=1
if i% 5000000 == 0:
print "still running..."
non_task = myThread(False)
task = myThread(True)
non_task.start()
task.start()
######## OUTPUT (NOTE: the "still running" string is just a periodic
print that the non-tasklet thread prints) ########
0
still running...
still running...
still running...
On Nov 1, 2006, at 12:14 PM, Jeff Senn wrote:
>
> Hi Brian-
>
> On Nov 1, 2006, at 2:10 PM, Brian Hardie wrote:
>>
>> stackless.schedule() and stackless.schedule_remove() do a fine job of
>> yielding to the next tasklet, but it seems, in my situation of having
>> only a single tasklet, that it does not cause a breakout of the
>> 'Stackless loop'.
>
> I might question why you want to break out of the loop - rather
> than just having another tasklet do that work... (see second
> example below).
>
> There is no "yield" or "break" as you describe since, for most
> examples, there are
> other tasklets wanting to do work. You need to basically remove
> everything from the runnables to get stackless.run() to return
> before a quantum.
>
> ...but assuming that's what you want, for your case (this obviously
> won't
> work if there are more tasklets...) you can just trivially use a
> channel
> to take your tasklet off the run queue. Example below.
> (the only bit of subtlety being that you don't want to call
> "channel.send"
> from outside a tasket...)
>
> -Jas
>
> ------
> import stackless
> def stuff(c):
> print "start"
> for i in xrange(0,1000):
> if i == 500 or i == 600:
> print "pause",i
> c.receive() #block myself
> print "continue"
> print "done"
>
> def restart(c): c.send(None) #use a task to restart other task
>
> c = stackless.channel()
> x = stackless.tasklet(stuff)(c)
> while stackless.getruncount() > 1:
> t = stackless.run(1000)
> print "DO WHATEVER HERE"
> if c.balance < 0: stackless.tasklet(restart)(c)
>
> -------
> ## second example using cooperating processes
> ## and not even bothering with channels
> import stackless
>
> def main():
> print "start"
> for i in xrange(0,1000):
> if i == 500 or i == 600:
> print "pause"
> stackless.schedule() #will make other guy run
> print "continue"
> print "done"
>
> def other():
> while stackless.getruncount() > 1:
> print "periodic check stuff"
> stackless.schedule() #will make first guy run again
>
> stackless.tasklet(main)()
> stackless.tasklet(other)()
> while stackless.getruncount() > 1:
> t = stackless.run(1000)
> if t is not None: t.run() #pick up where we left off
>
>
_______________________________________________
Stackless mailing list
Stackless at stackless.com
http://www.stackless.com/mailman/listinfo/stackless
More information about the Stackless
mailing list