mirror of
https://github.com/Sneed-Group/Poodletooth-iLand
synced 2024-12-25 04:32:33 -06:00
268 lines
7.2 KiB
Python
268 lines
7.2 KiB
Python
|
""" This module reimplements Python's native thread module using Panda
|
||
|
threading constructs. It's designed as a drop-in replacement for the
|
||
|
thread module for code that works with Panda; it is necessary because
|
||
|
in some compilation models, Panda's threading constructs are
|
||
|
incompatible with the OS-provided threads used by Python's thread
|
||
|
module. """
|
||
|
|
||
|
__all__ = [
|
||
|
'error', 'LockType',
|
||
|
'start_new_thread',
|
||
|
'interrupt_main',
|
||
|
'exit', 'allocate_lock', 'get_ident',
|
||
|
'stack_size',
|
||
|
'forceYield', 'considerYield',
|
||
|
]
|
||
|
|
||
|
# Import PandaModules as pm, so we don't have any namespace collisions.
|
||
|
from pandac import PandaModules as pm
|
||
|
|
||
|
# These methods are defined in Panda, and are particularly useful if
|
||
|
# you may be running in Panda's SIMPLE_THREADS compilation mode.
|
||
|
forceYield = pm.Thread.forceYield
|
||
|
considerYield = pm.Thread.considerYield
|
||
|
|
||
|
class error(StandardError):
|
||
|
pass
|
||
|
|
||
|
class LockType:
|
||
|
""" Implements a mutex lock. Instead of directly subclassing
|
||
|
PandaModules.Mutex, we reimplement the lock here, to allow us to
|
||
|
provide the described Python lock semantics. In particular, this
|
||
|
allows a different thread to release the lock than the one that
|
||
|
acquired it. """
|
||
|
|
||
|
def __init__(self):
|
||
|
self.__lock = pm.Mutex('PythonLock')
|
||
|
self.__cvar = pm.ConditionVar(self.__lock)
|
||
|
self.__locked = False
|
||
|
|
||
|
def acquire(self, waitflag = 1):
|
||
|
self.__lock.acquire()
|
||
|
try:
|
||
|
if self.__locked and not waitflag:
|
||
|
return False
|
||
|
while self.__locked:
|
||
|
self.__cvar.wait()
|
||
|
|
||
|
self.__locked = True
|
||
|
return True
|
||
|
|
||
|
finally:
|
||
|
self.__lock.release()
|
||
|
|
||
|
def release(self):
|
||
|
self.__lock.acquire()
|
||
|
try:
|
||
|
if not self.__locked:
|
||
|
raise error, 'Releasing unheld lock.'
|
||
|
|
||
|
self.__locked = False
|
||
|
self.__cvar.notify()
|
||
|
|
||
|
finally:
|
||
|
self.__lock.release()
|
||
|
|
||
|
def locked(self):
|
||
|
return self.__locked
|
||
|
|
||
|
__enter__ = acquire
|
||
|
|
||
|
def __exit__(self, t, v, tb):
|
||
|
self.release()
|
||
|
|
||
|
_threads = {}
|
||
|
_nextThreadId = 0
|
||
|
_threadsLock = pm.Mutex('thread._threadsLock')
|
||
|
|
||
|
def start_new_thread(function, args, kwargs = {}, name = None):
|
||
|
def threadFunc(threadId, function = function, args = args, kwargs = kwargs):
|
||
|
try:
|
||
|
try:
|
||
|
function(*args, **kwargs)
|
||
|
except SystemExit:
|
||
|
pass
|
||
|
|
||
|
finally:
|
||
|
_remove_thread_id(threadId)
|
||
|
|
||
|
global _nextThreadId
|
||
|
_threadsLock.acquire()
|
||
|
try:
|
||
|
threadId = _nextThreadId
|
||
|
_nextThreadId += 1
|
||
|
|
||
|
if name is None:
|
||
|
name = 'PythonThread-%s' % (threadId)
|
||
|
|
||
|
thread = pm.PythonThread(threadFunc, [threadId], name, name)
|
||
|
thread.setPythonData(threadId)
|
||
|
_threads[threadId] = (thread, {}, None)
|
||
|
|
||
|
thread.start(pm.TPNormal, False)
|
||
|
return threadId
|
||
|
|
||
|
finally:
|
||
|
_threadsLock.release()
|
||
|
|
||
|
def _add_thread(thread, wrapper):
|
||
|
""" Adds the indicated pm.Thread object, with the indicated Python
|
||
|
wrapper, to the thread list. Returns the new thread ID. """
|
||
|
|
||
|
global _nextThreadId
|
||
|
_threadsLock.acquire()
|
||
|
try:
|
||
|
threadId = _nextThreadId
|
||
|
_nextThreadId += 1
|
||
|
|
||
|
thread.setPythonData(threadId)
|
||
|
_threads[threadId] = (thread, {}, wrapper)
|
||
|
return threadId
|
||
|
|
||
|
finally:
|
||
|
_threadsLock.release()
|
||
|
|
||
|
def _get_thread_wrapper(thread, wrapperClass):
|
||
|
""" Returns the thread wrapper for the indicated thread. If there
|
||
|
is not one, creates an instance of the indicated wrapperClass
|
||
|
instead. """
|
||
|
|
||
|
threadId = thread.getPythonData()
|
||
|
if threadId is None:
|
||
|
# The thread has never been assigned a threadId. Go assign one.
|
||
|
|
||
|
global _nextThreadId
|
||
|
_threadsLock.acquire()
|
||
|
try:
|
||
|
threadId = _nextThreadId
|
||
|
_nextThreadId += 1
|
||
|
|
||
|
thread.setPythonData(threadId)
|
||
|
wrapper = wrapperClass(thread, threadId)
|
||
|
_threads[threadId] = (thread, {}, wrapper)
|
||
|
return wrapper
|
||
|
|
||
|
finally:
|
||
|
_threadsLock.release()
|
||
|
|
||
|
else:
|
||
|
# The thread has been assigned a threadId. Look for the wrapper.
|
||
|
_threadsLock.acquire()
|
||
|
try:
|
||
|
t, locals, wrapper = _threads[threadId]
|
||
|
assert t == thread
|
||
|
if wrapper is None:
|
||
|
wrapper = wrapperClass(thread, threadId)
|
||
|
_threads[threadId] = (thread, locals, wrapper)
|
||
|
return wrapper
|
||
|
|
||
|
finally:
|
||
|
_threadsLock.release()
|
||
|
|
||
|
def _get_thread_locals(thread, i):
|
||
|
""" Returns the locals dictionary for the indicated thread. If
|
||
|
there is not one, creates an empty dictionary. """
|
||
|
|
||
|
threadId = thread.getPythonData()
|
||
|
if threadId is None:
|
||
|
# The thread has never been assigned a threadId. Go assign one.
|
||
|
|
||
|
global _nextThreadId
|
||
|
_threadsLock.acquire()
|
||
|
try:
|
||
|
threadId = _nextThreadId
|
||
|
_nextThreadId += 1
|
||
|
|
||
|
thread.setPythonData(threadId)
|
||
|
locals = {}
|
||
|
_threads[threadId] = (thread, locals, None)
|
||
|
return locals.setdefault(i, {})
|
||
|
|
||
|
finally:
|
||
|
_threadsLock.release()
|
||
|
|
||
|
else:
|
||
|
# The thread has been assigned a threadId. Get the locals.
|
||
|
_threadsLock.acquire()
|
||
|
try:
|
||
|
t, locals, wrapper = _threads[threadId]
|
||
|
assert t == thread
|
||
|
return locals.setdefault(i, {})
|
||
|
|
||
|
finally:
|
||
|
_threadsLock.release()
|
||
|
|
||
|
|
||
|
def _remove_thread_id(threadId):
|
||
|
""" Removes the thread with the indicated ID from the thread list. """
|
||
|
|
||
|
_threadsLock.acquire()
|
||
|
try:
|
||
|
thread, locals, wrapper = _threads[threadId]
|
||
|
assert thread.getPythonData() == threadId
|
||
|
del _threads[threadId]
|
||
|
thread.setPythonData(None)
|
||
|
|
||
|
finally:
|
||
|
_threadsLock.release()
|
||
|
|
||
|
|
||
|
def interrupt_main():
|
||
|
# TODO.
|
||
|
pass
|
||
|
|
||
|
def exit():
|
||
|
raise SystemExit
|
||
|
|
||
|
def allocate_lock():
|
||
|
return LockType()
|
||
|
|
||
|
def get_ident():
|
||
|
return pm.Thread.getCurrentThread().this
|
||
|
|
||
|
def stack_size(size = 0):
|
||
|
raise error
|
||
|
|
||
|
|
||
|
class _local(object):
|
||
|
""" This class provides local thread storage using Panda's
|
||
|
threading system. """
|
||
|
|
||
|
def __del__(self):
|
||
|
i = id(self)
|
||
|
|
||
|
# Delete this key from all threads.
|
||
|
_threadsLock.acquire()
|
||
|
try:
|
||
|
for thread, locals, wrapper in _threads.values():
|
||
|
try:
|
||
|
del locals[i]
|
||
|
except KeyError:
|
||
|
pass
|
||
|
|
||
|
finally:
|
||
|
_threadsLock.release()
|
||
|
|
||
|
def __setattr__(self, key, value):
|
||
|
d = _get_thread_locals(pm.Thread.getCurrentThread(), id(self))
|
||
|
d[key] = value
|
||
|
|
||
|
## def __getattr__(self, key):
|
||
|
## d = _get_thread_locals(pm.Thread.getCurrentThread(), id(self))
|
||
|
## try:
|
||
|
## return d[key]
|
||
|
## except KeyError:
|
||
|
## raise AttributeError
|
||
|
|
||
|
def __getattribute__(self, key):
|
||
|
d = _get_thread_locals(pm.Thread.getCurrentThread(), id(self))
|
||
|
if key == '__dict__':
|
||
|
return d
|
||
|
try:
|
||
|
return d[key]
|
||
|
except KeyError:
|
||
|
return object.__getattribute__(self, key)
|
||
|
|
||
|
|
||
|
|