[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