[Stackless-checkins] r52328 - stackless/sandbox/libraries/uthread-ccp/uthread.py

richard.tew python-checkins at python.org
Sat Oct 14 14:02:17 CEST 2006


Author: richard.tew
Date: Sat Oct 14 14:02:16 2006
New Revision: 52328

Modified:
   stackless/sandbox/libraries/uthread-ccp/uthread.py
Log:
Finally got around to updating this library to replace all the dependencies on the EVE framework.  It should be fully functional and standalone now.

Modified: stackless/sandbox/libraries/uthread-ccp/uthread.py
==============================================================================
--- stackless/sandbox/libraries/uthread-ccp/uthread.py	(original)
+++ stackless/sandbox/libraries/uthread-ccp/uthread.py	Sat Oct 14 14:02:16 2006
@@ -1,9 +1,29 @@
 """Python Microthread Library, version 1.0
+
 Stackless adds tasklets to Python, a more modern form of the
-microthreads this library originally provided.  This highly
-modified version of the original uthread library is used
-internally at CCP Games and provides a range of useful
-functions and classes.
+microthreads this library originally provided.  This modified
+version of the original uthread library provides a range of
+useful functions and classes.
+
+Use of this class generally requires a certain approach to
+using Stackless.  Use only the provided 'uthread.sleep()' and
+'uthread.benice()' functions to yield and never
+'stackless.schedule()'.  The reason for this is that each
+tasklet scheduled is expected to be removed from the scheduler
+when it either yields or exits.  This way the watchdog can be
+relied upon to exit pretty much immediately, and will only
+hit the instruction count timeout when a runaway tasklet
+does not yield because of bad programming.
+
+Do not use 'stackless.run()'.  Instead use 'uthread.run()' as
+it takes care of scheduling tasklets that are sleeping and
+being nice.
+
+Permission was granted by Halldór Fannar Guðjónsson, Chief
+Technical Officer of CCP Games to Richard Tew to release the
+modified version of the original uthread library which they
+use internally.  Changes have been made to replace functionality
+provided by the EVE game framework by Richard Tew.
 """
 
 __version__ = "1.0"
@@ -11,7 +31,8 @@
 __license__ = \
 """Python Microthread Library version 1.0
 Copyright (C)2000  Will Ware, Christian Tismer
-Copyright (C)2006  CCP Games
+Copyright (C)2000-2006 CCP Games
+Copytight (C)2006 Richard Tew
 
 Permission to use, copy, modify, and distribute this software and its
 documentation for any purpose and without fee is hereby granted,
@@ -27,7 +48,8 @@
 ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
-OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE."""
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+"""
 
 import stackless
 import sys
@@ -128,6 +150,82 @@
 def IsCurrentSynonymOf(synonym_threadid):
     return IsSynonymOf(id(stackless.getcurrent()), synonym_threadid)
 
+# Sleeping related logic.
+
+sleepingTasklets = []
+
+def Sleep(secondsToWait):
+    '''
+    Yield the calling tasklet until the given number of seconds have passed.
+    '''
+    global sleepingTasklets
+    channel = stackless.channel()
+    endTime = time.time() + secondsToWait
+    sleepingTasklets.append((endTime, channel))
+    sleepingTasklets.sort()
+    # Block until we get sent an awakening notification.
+    channel.receive()
+
+def CheckSleepingTasklets():
+    '''
+    Function for internal uthread.py usage.
+    '''
+    global sleepingTasklets
+    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)
+
+# Being nice related logic.
+
+yieldChannel = stackless.channel()
+
+def BeNice():
+    '''
+    Yield the calling tasklet.  Use instead of schedule in order to keep
+    the scheduler empty.
+    '''
+    global yieldChannel
+    yieldChannel.receive()
+
+# We need to have a tasklet of our own calling this, then the watchdog.
+def RunNiceTasklets():
+    '''
+    Function for internal uthread.py usage.
+    '''
+    global yieldChannel
+    # 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
+
+# Scheduling related logic.
+
+def Run():
+    '''
+    Use instead of stackless.run() in order.to allow Sleep and BeNice
+    to work.  If this is not called and BeNice is used and RunNiceTasklets
+    is not called or Sleep is used and CheckSleepingTasklets is not called,
+    then any tasklets which call BeNice or Sleep respectively will block
+    indefinitely as there will be nothing to wake them up.
+
+    This function will exit when there are no remaining tasklets to run,
+    whether being nice or sleeping.
+    '''
+    while yieldChannel.balance or len(sleepingTasklets):
+        RunNiceTasklets()
+        t = stackless.run(10000000)
+        if t is not None:
+            # Need better standard handling of this case.
+            # Could StackTrace I guess?
+            raise RuntimeError("Runaway tasklet", t)
+        CheckSleepingTasklets()
 
 semaphores               = weakref.WeakKeyDictionary({})
 
@@ -678,7 +776,7 @@
 
 def Pool(ctx,func,*args,**keywords):
     '''
-        executes apply(args,keywords) on a new uthread.  The uthread in question is taken
+        Executes apply(args, keywords) on a new uthread.  The uthread in question is taken
         from a thread pool, rather than created one-per-shot call.  ctx is used as the
         thread context.  This should generally be used for short-lived threads to reduce
         overhead.
@@ -775,6 +873,12 @@
 
 locks = {}
 def Lock(object, *args):
+    '''
+    Blocks the calling tasklet until a specific globally accessible lock is
+    acquired.  The lock acquired is defined by the arguments passed to this
+    function.  The lock is not reentrant and any attempt by a tasklet to
+    reacquire the lock it already holds will result in a deadlock related error.
+    '''
     global locks
     t = (id(object), args)
     if t not in locks:
@@ -782,9 +886,14 @@
     locks[t].acquire()
 
 def TryLock(object, *args):
+    '''
+    Attempts to acquire a specific globally accessible lock.  The lock to be
+    acquired is defined by the arguments passed to this function.  If the lock
+    is not currently available, then False will be returned.  If the lock is
+    available, it will be acquired and True will be returned.
+    '''
     global locks
     t = (id(object), args)
-
     if t not in locks:
         locks[t] = Semaphore(t, strict=False)
     if not locks[t].IsCool():
@@ -793,6 +902,12 @@
     return True
 
 def ReentrantLock(object, *args):
+    '''
+    Blocks the calling tasklet until a specific globally accessible lock is
+    acquired, unless the calling tasklet has already acquired it in which
+    case it is reacquired in a reentrant manner.  The lock to be acquired is
+    defined by the arguments passed to this function.
+    '''
     global locks
     t = (id(object), args)
     if t not in locks:
@@ -800,12 +915,37 @@
     locks[t].acquire()
 
 def UnLock(object, *args):
+    '''
+    Releases a lock which the calling tasklet has previously acquired.  The
+    lock to be released is defined by the arguments passed to this function.
+    If the calling tasklet has acquired the lock several times reentrantly
+    then the lock will not be released unblocking other waiting tasklets
+    until all the reentrant locking actions have been matched with unlocking
+    actions.
+    '''
     global locks
     t = (id(object), args)
     locks[t].release()
     if (t in locks) and (locks[t].IsCool()): # may be gone or changed by now
         del locks[t]
 
+def with_instance_locking(f):
+    '''
+        Decorator which provides instance level locking.
+        When used on an instance method locks the instance for the duration
+        of the function call.  Requires that the first argument is the
+        instance the lock belongs to, which will be implicit with decorated
+        instance methods.
+    '''
+    def new_f(self, *args, **kwds):
+        Lock(self)
+        try:
+            return f(self, *args, **kwds)
+        finally:
+            UnLock(self)
+    return new_f
+
+
 # Exported names.
 parallel = Parallel
 worker = PoolWorker
@@ -814,4 +954,6 @@
 pool = Pool
 poolWithoutTheStars = PoolWithoutTheStars
 
-
+sleep = Sleep
+benice = BeNice
+run = Run

-------------- next part --------------
_______________________________________________
Stackless-checkins mailing list
Stackless-checkins at stackless.com
http://www.stackless.com/mailman/listinfo/stackless-checkins


More information about the Stackless-checkins mailing list