From python-checkins at python.org Sat Feb 17 01:30:13 2007 From: python-checkins at python.org (christian.tismer) Date: Sat, 17 Feb 2007 01:30:13 +0100 (CET) Subject: [Stackless-checkins] r53810 - stackless/trunk/Stackless/module/stacklessmodule.c Message-ID: <20070217003013.996821E400B@bag.python.org> Author: christian.tismer Date: Sat Feb 17 01:30:13 2007 New Revision: 53810 Modified: stackless/trunk/Stackless/module/stacklessmodule.c Log: added tiny helpers _gc_untrack and _gc_track. This is support for my new leak detector which gets massively accelerated. Modified: stackless/trunk/Stackless/module/stacklessmodule.c ============================================================================== --- stackless/trunk/Stackless/module/stacklessmodule.c (original) +++ stackless/trunk/Stackless/module/stacklessmodule.c Sat Feb 17 01:30:13 2007 @@ -714,6 +714,41 @@ #endif +/****************************************************** + + Special support for RPython + + ******************************************************/ + +/* + In RPython, we are not allowed to use cyclic references + without explicitly breaking them, or we leak memory. + I wrote a tool to find out which code line causes + unreachable objects. It works by running a full GC run + after every line. To make this reasonably fast, it makes + sense to remove the existing GC objects temporarily. + */ + +static PyObject * +_gc_untrack(PyObject *self, PyObject *ob) +{ + PyObject_GC_UnTrack(ob); + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject * +_gc_track(PyObject *self, PyObject *ob) +{ + PyObject_GC_Track(ob); + Py_INCREF(Py_None); + return Py_None; +} + +static char _gc_untrack__doc__[] = +"_gc_untrack, gc_track -- remove or add an object from the gc list."; + + /* List of functions defined in the module */ #define PCF PyCFunction @@ -748,6 +783,10 @@ slp_pickle_moduledict__doc__}, {"get_thread_info", (PCF)get_thread_info, METH_VARARGS, get_thread_info__doc__}, + {"_gc_untrack", (PCF)_gc_untrack, METH_O, + _gc_untrack__doc__}, + {"_gc_track", (PCF)_gc_track, METH_O, + _gc_untrack__doc__}, #ifdef STACKLESS_SPY {"_peek", (PCF)_peek, METH_O, _peek__doc__}, _______________________________________________ Stackless-checkins mailing list Stackless-checkins at stackless.com http://www.stackless.com/mailman/listinfo/stackless-checkins From python-checkins at python.org Wed Feb 28 19:08:53 2007 From: python-checkins at python.org (richard.tew) Date: Wed, 28 Feb 2007 19:08:53 +0100 (CET) Subject: [Stackless-checkins] r54021 - stackless/sandbox/examples/stacklesssocket.py Message-ID: <20070228180853.DEA5D1E4004@bag.python.org> Author: richard.tew Date: Wed Feb 28 19:08:52 2007 New Revision: 54021 Modified: stackless/sandbox/examples/stacklesssocket.py Log: Added support for UDP sockets. May not be correct, but seems to work as demonstrated by the simple test case. Also added a _fileobject reference so that urllib2 will work. Modified: stackless/sandbox/examples/stacklesssocket.py ============================================================================== --- stackless/sandbox/examples/stacklesssocket.py (original) +++ stackless/sandbox/examples/stacklesssocket.py Wed Feb 28 19:08:52 2007 @@ -23,7 +23,7 @@ # Possible improvements: # - More correct error handling. When there is an error on a socket found by # poll, there is no idea what it actually is. -# - Launching each bit of incoming data in its own tasklet on the readChannel +# - Launching each bit of incoming data in its own tasklet on the recvChannel # send is a little over the top. It should be possible to add it to the # rest of the queued data @@ -38,6 +38,9 @@ error = stdsocket.error timeout = stdsocket.timeout +# urllib2 apparently uses this directly. We need to cater for that. +_fileobject = stdsocket._fileobject + # WARNING: this function blocks and is not thread safe. # The only solution is to spawn a thread to handle all # getaddrinfo requests. Implementing a stackless DNS @@ -111,6 +114,10 @@ class dispatcher(asyncore.dispatcher): + connectChannel = None + acceptChannel = None + recvChannel = None + def __init__(self, sock): # This is worth doing. I was passing in an invalid socket which was # an instance of dispatcher and it was causing tasklet death. @@ -118,62 +125,117 @@ raise StandardError("Invalid socket passed to dispatcher") asyncore.dispatcher.__init__(self, sock) - self.acceptChannel = stackless.channel() - self.connectChannel = stackless.channel() - self.readChannel = stackless.channel() + # if self.socket.type == SOCK_DGRAM: + # self.dgramRecvChannels = {} + # self.dgramReadBuffers = {} + #else: + self.recvChannel = stackless.channel() + self.readBuffer = None - self.readBuffer = '' - self.outBuffer = '' + self.sendBuffer = '' + self.sendToBuffers = [] def writable(self): - return (not self.connected) or len(self.outBuffer) + if self.socket.type != SOCK_DGRAM and not self.connected: + return True + return len(self.sendBuffer) or len(self.sendToBuffers) def accept(self): + if not self.acceptChannel: + self.acceptChannel = stackless.channel() return self.acceptChannel.receive() def connect(self, address): asyncore.dispatcher.connect(self, address) - if not self.connected: + # UDP sockets do not connect. + if self.socket.type != SOCK_DGRAM and not self.connected: + if not self.connectChannel: + self.connectChannel = stackless.channel() self.connectChannel.receive() def send(self, data): - self.outBuffer += data + self.sendBuffer += data stackless.schedule() return len(data) def sendall(self, data): # WARNING: this will busy wait until all data is sent - self.outBuffer += data - while self.outBuffer: + # It should be possible to do away with the busy wait with + # the use of a channel. + self.sendBuffer += data + while self.sendBuffer: stackless.schedule() return len(data) + def sendto(self, sendData, sendAddress): + waitChannel = None + for idx, (data, address, channel, sentBytes) in enumerate(self.sendToBuffers): + if address == sendAddress: + self.sendToBuffers[idx] = (data + sendData, address, channel, sentBytes) + waitChannel = channel + break + if waitChannel is None: + waitChannel = stackless.channel() + self.sendToBuffers.append((sendData, sendAddress, waitChannel, 0)) + return waitChannel.receive() + # Read at most byteCount bytes. def recv(self, byteCount): + if self.readBuffer is None: + self.readBuffer = "" if len(self.readBuffer) < byteCount: - data = self.readChannel.receive() - self.readBuffer += data + self.readBuffer += self.recvChannel.receive() stackless.schedule() ret = self.readBuffer[:byteCount] self.readBuffer = self.readBuffer[byteCount:] return ret + def recvfrom(self, byteCount): + if self.readBuffer is None: + self.readBuffer = [] + + ret = "" + address = None + while 1: + while len(self.readBuffer): + data, dataAddress = self.readBuffer[0] + if address is None: + address = dataAddress + elif address != dataAddress: + # They got all the sequenial data from the given address. + return ret, address + + ret += data + if len(ret) >= byteCount: + # We only partially used up this data. + self.readBuffer[0] = ret[byteCount:], address + return ret[:byteCount], address + + # We completely used up this data. + del self.readBuffer[0] + + self.readBuffer.append(self.recvChannel.receive()) + def close(self): asyncore.dispatcher.close(self) self.connected = False self.accepting = False - self.outBuffer = None # breaks the loop in sendall + self.sendBuffer = None # breaks the loop in sendall # Clear out all the channels with relevant errors. - while self.acceptChannel.balance < 0: + while self.acceptChannel and self.acceptChannel.balance < 0: self.acceptChannel.send_exception(error, 9, 'Bad file descriptor') - while self.connectChannel.balance < 0: + while self.connectChannel and self.connectChannel.balance < 0: self.connectChannel.send_exception(error, 10061, 'Connection refused') - while self.readChannel.balance < 0: - self.readChannel.send_exception(error, 10054, 'Connection reset by peer') + while self.recvChannel and self.recvChannel.balance < 0: + self.recvChannel.send_exception(error, 10054, 'Connection reset by peer') + + # asyncore doesn't support this. Why not? + def fileno(self): + return self.socket.fileno() def handle_accept(self): - if self.acceptChannel.balance < 0: + if self.acceptChannel and self.acceptChannel.balance < 0: currentSocket, clientAddress = asyncore.dispatcher.accept(self) currentSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # Give them the asyncore based socket, not the standard one. @@ -182,7 +244,10 @@ # Inform the blocked connect call that the connection has been made. def handle_connect(self): - self.connectChannel.send(None) + if self.socket.type != SOCK_DGRAM: + if not self.connectChannel: + self.connectChannel = stackless.channel() + self.connectChannel.send(None) # Asyncore says its done but self.readBuffer may be non-empty # so can't close yet. Do nothing and let 'recv' trigger the close. @@ -196,32 +261,40 @@ def handle_read(self): try: - buf = asyncore.dispatcher.recv(self, 20000) + if self.socket.type == SOCK_DGRAM: + ret, address = self.socket.recvfrom(20000) + stackless.tasklet(self.recvChannel.send)((ret, address)) + else: + ret = asyncore.dispatcher.recv(self, 20000) + stackless.tasklet(self.recvChannel.send)(ret) except stdsocket.error, err: # XXX Is this correct? # If there's a read error assume the connection is # broken and drop any pending output - if self.outBuffer: - self.outBuffer = "" + if self.sendBuffer: + self.sendBuffer = "" # Why can't I pass the 'err' by itself? - self.readChannel.send_exception(stdsocket.error, err) - else: - stackless.tasklet(self.readChannel.send)(buf) + self.recvChannel.send_exception(stdsocket.error, err) def handle_write(self): - if len(self.outBuffer): - sentBytes = asyncore.dispatcher.send(self, self.outBuffer[:512]) - self.outBuffer = self.outBuffer[sentBytes:] + if len(self.sendBuffer): + sentBytes = asyncore.dispatcher.send(self, self.sendBuffer[:512]) + self.sendBuffer = self.sendBuffer[sentBytes:] + elif len(self.sendToBuffers): + data, address, channel, oldSentBytes = self.sendToBuffers[0] + sentBytes = self.socket.sendto(data, address) + totalSentBytes = oldSentBytes + sentBytes + if len(data) > sentBytes: + self.sendToBuffers[0] = data[sentBytes:], address, channel, totalSentBytes + else: + del self.sendToBuffers[0] + stackless.tasklet(channel.send)(totalSentBytes) # In order for incoming connections to be stackless compatible, # they need to be wrapped by an asyncore based dispatcher subclass. def wrap_accept_socket(self, currentSocket): return stacklesssocket(currentSocket) - # asyncore doesn't support this. Why not? - def fileno(self): - return self.socket.fileno() - if __name__ == '__main__': import struct @@ -232,7 +305,7 @@ dataLength = len(data) print "creating listen socket" - def ManageListener(address): + def TestTCPServer(address): global info, data, dataLength listenSocket = socket(AF_INET, SOCK_STREAM) @@ -263,7 +336,6 @@ if currentSocket.recv(4) != "": print "server recv(2)", i, "FAIL" break - else: currentSocket.close() @@ -277,7 +349,7 @@ print "Done server" - def TestClientConnections(address): + def TestTCPClient(address): global info, data, dataLength # Attempt 1: @@ -300,22 +372,69 @@ else: print "client test", 2, "FAIL" - def TestMonkeyPatch(uri): + def TestMonkeyPatchUrllib(uri): # replace the system socket with this module import sys + oldSocket = sys.modules["socket"] sys.modules["socket"] = __import__(__name__) - import urllib # must occur after monkey-patching! - f = urllib.urlopen(uri) - if not isinstance(f.fp._sock, stacklesssocket): - raise AssertionError("failed to apply monkeypatch") - s = f.read() - if len(s) != 0: - print "Fetched", len(s), "bytes via replaced urllib" - else: - raise AssertionError("no text received?") + try: + import urllib # must occur after monkey-patching! + f = urllib.urlopen(uri) + if not isinstance(f.fp._sock, stacklesssocket): + raise AssertionError("failed to apply monkeypatch") + s = f.read() + if len(s) != 0: + print "Fetched", len(s), "bytes via replaced urllib" + else: + raise AssertionError("no text received?") + finally: + sys.modules["socket"] = oldSocket - stackless.tasklet(ManageListener)(testAddress) - stackless.tasklet(TestClientConnections)(testAddress) - stackless.tasklet(TestMonkeyPatch)("http://python.org/") + def TestMonkeyPatchUDP(address): + # replace the system socket with this module + import sys + oldSocket = sys.modules["socket"] + sys.modules["socket"] = __import__(__name__) + try: + def UDPServer(address): + listenSocket = socket(AF_INET, SOCK_DGRAM) + listenSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) + listenSocket.bind(address) + + i = 1 + cnt = 0 + while 1: + #print "waiting for connection test", i + #currentSocket, clientAddress = listenSocket.accept() + #print "received connection", i, "from", clientAddress + + print "waiting to receive" + t = listenSocket.recvfrom(256) + cnt += len(t[0]) + print "received", t[0], cnt + if cnt == 512: + break + + def UDPClient(address): + clientSocket = socket(AF_INET, SOCK_DGRAM) + # clientSocket.connect(address) + print "sending 512 byte packet" + sentBytes = clientSocket.sendto("-"+ ("*" * 510) +"-", address) + print "sent 512 byte packet", sentBytes + + stackless.tasklet(UDPServer)(address) + stackless.tasklet(UDPClient)(address) + stackless.run() + finally: + sys.modules["socket"] = oldSocket + + stackless.tasklet(TestTCPServer)(testAddress) + stackless.tasklet(TestTCPClient)(testAddress) stackless.run() + + stackless.tasklet(TestMonkeyPatchUrllib)("http://python.org/") + stackless.run() + + TestMonkeyPatchUDP(testAddress) + print "result: SUCCESS" _______________________________________________ Stackless-checkins mailing list Stackless-checkins at stackless.com http://www.stackless.com/mailman/listinfo/stackless-checkins From python-checkins at python.org Wed Feb 28 19:36:29 2007 From: python-checkins at python.org (richard.tew) Date: Wed, 28 Feb 2007 19:36:29 +0100 (CET) Subject: [Stackless-checkins] r54032 - stackless/sandbox/examples/twisted_timer.py stackless/sandbox/examples/twisted_yield.py stackless/sandbox/examples/twisted_yielddefer.py Message-ID: <20070228183629.7DFC51E4004@bag.python.org> Author: richard.tew Date: Wed Feb 28 19:36:28 2007 New Revision: 54032 Added: stackless/sandbox/examples/twisted_timer.py stackless/sandbox/examples/twisted_yield.py stackless/sandbox/examples/twisted_yielddefer.py Log: Checked in 3 Twisted and Stackless examples from Greg Hazel. Added: stackless/sandbox/examples/twisted_timer.py ============================================================================== --- (empty file) +++ stackless/sandbox/examples/twisted_timer.py Wed Feb 28 19:36:28 2007 @@ -0,0 +1,76 @@ +# Twisted and Stackless - example 1 +# +# In this example, most of the time is spent in the twisted event loop. This +# approach allows you to control the granularity of tasklet execution based on +# time. At 30fps, a timer executes that schedules stackless tasklets to run - a +# simple tasklet just prints "do-op". +# +# by Greg Hazel +# +# If you have any questions related to this example: +# +# - If they are related to how Twisted works, please contact the +# Twisted mailing list: +# +# http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python +# +# - If they are related to how Stackless works, please contact +# the Stackless mailing list: +# +# http://www.tismer.com/mailman/listinfo/stackless +# +# Otherwise if they are related to how Twisted works in conjunction with +# Stackless, please contact the Stackless mailing list: +# +# http://www.tismer.com/mailman/listinfo/stackless + +import time +import stackless +from twisted.internet import task, reactor + +# a few globals for fun +ideal_fps = 30.0 +fps = 0 +clock = getattr(time, 'clock', time.time) +last_time = clock() + + +def frame(): + global fps, last_time + + # a silly fps counter + this_time = clock() + d = 1.0 / (this_time - last_time) + fps = (fps + d) / 2.0 + last_time = this_time + print fps + + # allow tasklets to run + stackless.schedule() + +# start the timer +t = task.LoopingCall(frame) +t.start(1.0/ideal_fps) + + +def tasklet(): + + while True: + # complicated operation with side-effects + print 'do-op' + + # allow the reactor to resume + stackless.schedule() + + # don't loop forever + if not reactor.running: + break + + +# start the simple tasklet +stackless.tasklet(tasklet)() + +# start the reactor +stackless.tasklet(reactor.run)() +# start the stackless scheduler +stackless.run() Added: stackless/sandbox/examples/twisted_yield.py ============================================================================== --- (empty file) +++ stackless/sandbox/examples/twisted_yield.py Wed Feb 28 19:36:28 2007 @@ -0,0 +1,82 @@ +# Twisted and Stackless - example 2 +# +# In this example, a large portion of the time is spent in stackless. This +# approach allows you to control the granularity of tasklet execution based on +# cooperative yield points in the tasklet. At 30fps, a timer executes that +# prints the fps. A simple tasklet just prints "." and yields to the reactor. +# The reactor processes all pending events, then reschedules the tasklet. +# +# This example eats a lot of cpu, because no time is spent blocking - the +# control is constantly being passed back and forth between the reactor and +# stackless. +# +# by Greg Hazel +# +# If you have any questions related to this example: +# +# - If they are related to how Twisted works, please contact the +# Twisted mailing list: +# +# http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python +# +# - If they are related to how Stackless works, please contact +# the Stackless mailing list: +# +# http://www.tismer.com/mailman/listinfo/stackless +# +# Otherwise if they are related to how Twisted works in conjunction with +# Stackless, please contact the Stackless mailing list: +# +# http://www.tismer.com/mailman/listinfo/stackless + + +import sys +import time +import stackless +from twisted.internet import task, reactor + +# a few globals for fun +ideal_fps = 30.0 +fps = 0 +clock = getattr(time, 'clock', time.time) +last_time = clock() + + +def frame(): + global fps, last_time + + # a silly fps counter + this_time = clock() + d = 1.0 / (this_time - last_time) + fps = (fps + d) / 2.0 + last_time = this_time + print '\n', fps + + +# start the timer +t = task.LoopingCall(frame) +t.start(1.0/ideal_fps) + + +def tasklet(): + + while True: + # complicated operation with side-effects + sys.stdout.write('.') + + # allow the reactor to run all pending events, then come back + reactor.callLater(0, stackless.schedule) + stackless.schedule() + + # don't loop forever + if not reactor.running: + break + + +# start the simple tasklet +stackless.tasklet(tasklet)() + +# start the reactor +stackless.tasklet(reactor.run)() +# start the stackless scheduler +stackless.run() Added: stackless/sandbox/examples/twisted_yielddefer.py ============================================================================== --- (empty file) +++ stackless/sandbox/examples/twisted_yielddefer.py Wed Feb 28 19:36:28 2007 @@ -0,0 +1,144 @@ +# Twisted and Stackless - example 3 +# +# In this example, most of the time is spent in the twisted event loop. This +# approach allows you to control the granularity of tasklet execution based on +# deferred operations a tasklet waits on. At 30fps, a timer executes that +# prints the fps. A simple tasklet begins a deferred operation, and waits for +# the result. During that time, control is returned to the reactor. +# +# Because of the possibility that a deferred operation completes immediately, +# a channel subclass is used to hold the value and return it without any +# rescheduling. stackless.channel.send() would otherwise block the tasklet +# before it could call stackless.channel.receive(). +# +# by Greg Hazel +# +# If you have any questions related to this example: +# +# - If they are related to how Twisted works, please contact the +# Twisted mailing list: +# +# http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python +# +# - If they are related to how Stackless works, please contact +# the Stackless mailing list: +# +# http://www.tismer.com/mailman/listinfo/stackless +# +# Otherwise if they are related to how Twisted works in conjunction with +# Stackless, please contact the Stackless mailing list: +# +# http://www.tismer.com/mailman/listinfo/stackless + +import time +import stackless +from twisted.internet import task, reactor, defer +from twisted.python import failure + +# a few globals for fun +ideal_fps = 30.0 +fps = 0 +clock = getattr(time, 'clock', time.time) +last_time = clock() + + +def frame(): + global fps, last_time + + # a silly fps counter + this_time = clock() + d = 1.0 / (this_time - last_time) + fps = (fps + d) / 2.0 + last_time = this_time + print fps + + +# start the timer +t = task.LoopingCall(frame) +t.start(1.0/ideal_fps) + + +# a fake operation that returns a defered +def deferredOp(): + + df = defer.Deferred() + + # complete in 0.5 seconds + reactor.callLater(0.5, df.callback, "result!") + # a deferred that completed already + #df.callback("result!") + + # we could also fake an error + #reactor.callLater(0.5, df.errback, failure.Failure(Exception("failed"))) + # a deferred that failed already + #df.errback(failure.Failure(Exception("failed"))) + + return df + + +# a stackless channel which can hold a single value without scheduling. +# handy for tasklets that might send data to themselves before calling receive. +class NWChannel(stackless.channel): + + def send_nowait(self, v): + if self.balance == 0: + self.value = v + else: + self.send(v) + + def send_exception_nowait(self, type, value): + if self.balance == 0: + self.exc = (type, value) + else: + self.send_exception(type, value) + + def receive(self): + if hasattr(self, 'value'): + v = self.value + del self.value + return v + if hasattr(self, 'exc'): + type, value = self.exc + del self.exc + raise type, value + return stackless.channel.receive(self) + + +def good(r, me, return_channel): + return_channel.send_nowait(r) + # if the deferred is called back immediately, this function will be called + # from the original tasklet. no need to reschedule. + if stackless.getcurrent() != me: + stackless.schedule() + +def bad(f, me, return_channel): + return_channel.send_exception_nowait(f.type, f.value) + # if the deferred fails immediately, this function will be called + # from the original tasklet. no need to reschedule. + if stackless.getcurrent() != me: + stackless.schedule() + +def tasklet(): + return_channel = NWChannel() + me = stackless.getcurrent() + + while True: + # a deferred operation + df = deferredOp() + + df.addCallback(good, me, return_channel) + df.addErrback(bad, me, return_channel) + x = return_channel.receive() + print x + + # don't loop forever + if not reactor.running: + break + +# start the simple tasklet +stackless.tasklet(tasklet)() + +# start the reactor +stackless.tasklet(reactor.run)() +# start the stackless scheduler +stackless.run() _______________________________________________ Stackless-checkins mailing list Stackless-checkins at stackless.com http://www.stackless.com/mailman/listinfo/stackless-checkins From python-checkins at python.org Wed Feb 21 19:04:37 2007 From: python-checkins at python.org (richard.tew) Date: Wed, 21 Feb 2007 19:04:37 +0100 (CET) Subject: [Stackless-checkins] r53840 - stackless/sandbox/examples/scheduleAlternative.py stackless/sandbox/examples/scheduleNormal.py Message-ID: <20070221180437.D1C2D1E4002@bag.python.org> Author: richard.tew Date: Wed Feb 21 19:04:35 2007 New Revision: 53840 Added: stackless/sandbox/examples/scheduleAlternative.py stackless/sandbox/examples/scheduleNormal.py Log: Two examples meant to illustrate the two approaches to scheduling. Added: stackless/sandbox/examples/scheduleAlternative.py ============================================================================== --- (empty file) +++ stackless/sandbox/examples/scheduleAlternative.py Wed Feb 21 19:04:35 2007 @@ -0,0 +1,114 @@ +# +# The alternative approach to scheduling. +# +# Author: Richard Tew +# +# This code was written to serve as an example of Stackless Python usage. +# Feel free to email me with any questions, comments, or suggestions for +# improvement. +# +# Benefits of this approach: +# +# - It lends itself well to embedding, if implemented in C or C++. +# +# - It allows more precise control. +# +# Limitations of this approach: +# +# - The expectation is that the scheduler will be empty because of this +# will exit pretty much immediately. So anything which calls to +# 'stackless.schedule' rather than using 'BeNice' or 'Sleep' will +# break this scheduling model and prevent it from working as +# expected. After all, if there are tasklets in the scheduler it +# continues to run and does not exit. +# +# Ideally 'stackless.schedule' should be patched to call BeNice. Or +# calls to it just avoided completely. +# + +import time +import stackless + +yieldChannel = stackless.channel() +sleepingTasklets = [] + +# Utility functions for tasklets to call. + +def BeNice(): + """ Signal that the tasklet can be interrupted. """ + yieldChannel.receive() + +def Sleep(secondsToWait): + """ Put the current tasklet to sleep for a number of seconds. """ + channel = stackless.channel() + endTime = time.time() + secondsToWait + sleepingTasklets.append((endTime, channel)) + sleepingTasklets.sort() + # Block until we get sent an awakening notification. + channel.receive() + +# Scheduler running related functions. + +def CheckSleepingTasklets(): + """ Awaken all tasklets which are due to be awakened. """ + while len(sleepingTasklets): + endTime = sleepingTasklets[0][0] + if endTime > time.time(): + break + 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) + +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. Well they + # would + n = -yieldChannel.balance + while n > 0: + yieldChannel.send(None) + n -= 1 + + CheckSleepingTasklets() + + # Run any tasklets which need to be scheduled. As long as the BeNice and + # Sleep callers do not use schedule they should never be in the scheduler + # at this point, but rather back in the yield channel or on a sleep channel. + # Loosely guessing, I would say that only newly created tasklets should + # ever be in the scheduler. And nothing should stay in there for that + # long before moving out by using Sleep or BeNice. If something does stay + # in there too long then it is not yielding or is keeping the scheduler + # running by using 'stackless.schedule' which is not compatible with the + # way this method intends the scheduler to be used. + + interruptedTasklet = stackless.run(1000000) + if interruptedTasklet: + # Should really print a stacktrace from the tasklet so it can be + # rewritten to 'be nice'. Alternatively the tasklet could be killed + # at this point if that suits the application. + interruptedTasklet.insert() + +exitScheduler = False + +def Run(): + def Test(n): + global exitScheduler + while n: + if n % 2 == 1: + print n, "BeNice" + BeNice() + else: + print n, "Sleep" + Sleep(1.0) + n -= 1 + exitScheduler = True + + # Do 10 iterations. + stackless.tasklet(Test)(10) + + while not exitScheduler: + ScheduleTasklets() + +if __name__ == '__main__': + Run() Added: stackless/sandbox/examples/scheduleNormal.py ============================================================================== --- (empty file) +++ stackless/sandbox/examples/scheduleNormal.py Wed Feb 21 19:04:35 2007 @@ -0,0 +1,74 @@ +# +# The normal approach to scheduling. +# +# Author: Richard Tew +# +# This code was written to serve as an example of Stackless Python usage. +# Feel free to email me with any questions, comments, or suggestions for +# improvement. +# +# Benefits of this approach: +# +# - It is easy to use from the interpreter. +# +# Limitations of this approach: +# +# - If there are no tasklets that run for the life of the application +# or script then the scheduler may exit when things are not complete. +# One example of this is when all tasklets which were in the +# scheduler and did not complete are waiting on channels. +# + +import time +import stackless + +exitScheduler = False + +sleepingTasklets = [] + +# Utility functions for tasklets to call. + +def Sleep(secondsToWait): + """ Put the current tasklet to sleep for a number of seconds. """ + channel = stackless.channel() + endTime = time.time() + secondsToWait + sleepingTasklets.append((endTime, channel)) + sleepingTasklets.sort() + # Block until we get sent an awakening notification. + channel.receive() + +# Scheduler running related functions. + +def ManageSleepingTasklets(): + """ Awaken all tasklets which are due to be awakened. """ + while not exitScheduler: + while len(sleepingTasklets): + endTime = sleepingTasklets[0][0] + if endTime > time.time(): + break + channel = sleepingTasklets[0][1] + del sleepingTasklets[0] + # It does not matter what we send as it is not used. + channel.send(None) + stackless.schedule() + + +def Run(): + def Test(n): + global exitScheduler + while n: + print n, "Sleep" + Sleep(1.0) + n -= 1 + exitScheduler = True + + stackless.tasklet(Test)(10) + stackless.tasklet(ManageSleepingTasklets)() + + # This will run until there are no scheduled tasklets. But because we + # create one to manage the sleeping tasklets this will mean it will run + # indefinitely. + stackless.run() + +if __name__ == '__main__': + Run() _______________________________________________ Stackless-checkins mailing list Stackless-checkins at stackless.com http://www.stackless.com/mailman/listinfo/stackless-checkins From python-checkins at python.org Wed Feb 21 18:42:56 2007 From: python-checkins at python.org (richard.tew) Date: Wed, 21 Feb 2007 18:42:56 +0100 (CET) Subject: [Stackless-checkins] r53839 - stackless/sandbox/examples/channelWithReceiveTimeout.py Message-ID: <20070221174256.BFFBF1E4002@bag.python.org> Author: richard.tew Date: Wed Feb 21 18:42:51 2007 New Revision: 53839 Added: stackless/sandbox/examples/channelWithReceiveTimeout.py Log: Added an example which creates a subclass of channel to timeout tasklets waiting to receive something on it. Added: stackless/sandbox/examples/channelWithReceiveTimeout.py ============================================================================== --- (empty file) +++ stackless/sandbox/examples/channelWithReceiveTimeout.py Wed Feb 21 18:42:51 2007 @@ -0,0 +1,139 @@ +# +# One way of timing out tasklets waiting on a channel. +# +# Author: Richard Tew +# +# This code was written to serve as an example of Stackless Python usage. +# Feel free to email me with any questions, comments, or suggestions for +# improvement. +# +# Limitations of this approach: +# +# - There is no need to know the order the tasklets are waiting on the +# channel because of the uniform expiration period so we can assume +# that the first to expire will be the first in line on the channel +# and we can remove it by sending an exception. +# + +import time, weakref +import stackless + +exitScheduler = False + +sleepingTasklets = [] + +# Utility functions for tasklets to call. + +def Sleep(secondsToWait): + """ Put the current tasklet to sleep for a number of seconds. """ + channel = stackless.channel() + endTime = time.time() + secondsToWait + sleepingTasklets.append((endTime, channel)) + sleepingTasklets.sort() + # Block until we get sent an awakening notification. + channel.receive() + +# Scheduler running related functions. + +def ManageSleepingTasklets(): + """ Awaken all tasklets which are due to be awakened. """ + while not exitScheduler: + while len(sleepingTasklets): + endTime = sleepingTasklets[0][0] + if endTime > time.time(): + break + channel = sleepingTasklets[0][1] + del sleepingTasklets[0] + # It does not matter what we send as it is not used. + channel.send(None) + stackless.schedule() + + +class ChannelWaitExpiration(Exception): + pass + +class TimeLimitedReceiveChannel(stackless.channel): + def __init__(self, *args, **kwargs): + self.expirySeconds = 5.0 + self.receiveAddTimes = [] + + stackless.channel.__init__(self, *args, **kwargs) + + def receive(self): + if self.balance > 0: + # We can return from this immediately. + return stackless.channel.receive(self) + + if self.balance == 0: + def ManageWaitingTasklets(c): + while c.balance < 0: + earliestExpirationTime = None + for timeAdded, tasklet in self.receiveAddTimes: + if not tasklet.blocked: + # This tasklet has already been removed but since + # it hasn't been scheduled yet it hasn't exited its + # call to our receive function and cleared out its + # entry. This can happen if the channel is configured + # to schedule waiting tasklets when they receive + # rather than running them immediately (i.e. via the + # 'preference' attribute). + continue + if timeAdded + self.expirySeconds < time.time(): + # This tasklet is guaranteed to be the first tasklet + # on the channel. + self.send_exception(ChannelWaitExpiration) + else: + # Otherwise note the time at which the next expiring + # tasklet expires so we can sleep until then. Since + # all expiration delays are the same amount of time + # from the period of addition, we can guess the + # earliest we will need to check next. + earliestExpirationTime = timeAdded + self.expirySeconds + break + # Wait until the earliest we will need to next check. + if earliestExpirationTime is None: + Sleep(self.expirySeconds) + else: + Sleep(time.time() - earliestExpirationTime) + stackless.tasklet(ManageWaitingTasklets)(self) + + t = time.time(), weakref.proxy(stackless.getcurrent()) + self.receiveAddTimes.append(t) + self.receiveAddTimes.sort() + + try: + ret = stackless.channel.receive(self) + self.receiveAddTimes.remove(t) + return ret + except: + # Any exception should be met with the same handling. + self.receiveAddTimes.remove(t) + raise + +def Run(): + c = TimeLimitedReceiveChannel() + + def Test(c): + global exitScheduler + t = time.time() + print "Waiting for %0.1f seconds" % c.expirySeconds + try: + c.receive() + except Exception, e: + if isinstance(e, ChannelWaitExpiration): + print "Exited receive, took %0.1f seconds" % (time.time() - t) + else: + print "Unexpected exit '%s', took %0.1f seconds" % (str(e), time.time() - t) + finally: + exitScheduler = True + + stackless.tasklet(Test)(c) + stackless.tasklet(ManageSleepingTasklets)() + + # This will run until there are no scheduled tasklets. But because we + # create one to manage the sleeping tasklets this will mean it will run + # indefinitely. + stackless.run() + +if __name__ == '__main__': + Run() _______________________________________________ Stackless-checkins mailing list Stackless-checkins at stackless.com http://www.stackless.com/mailman/listinfo/stackless-checkins