Source code for cup.thread
#!/usr/bin/env python
# -*- coding: utf-8 -*
# Copyright: [CUP] - See LICENSE for details.
# Author: Zhaominghao, Guannan Ma
"""
:description:
cup thread module
"""
__all__ = ['async_raise', 'CupThread', 'RWLock', 'thread_alive']
import threading
import time
import ctypes
import cup
[docs]def async_raise(tid, exctype):
"""
Raises an exception in the threads with id tid
:param tid:
thread id in python
:param exctype:
exception class, e.g. IOError
"""
return ctypes.pythonapi.PyThreadState_SetAsyncExc(
tid,
ctypes.py_object(exctype)
)
[docs]def thread_alive(obj):
"""
check if thread is alive. Py2 py3 compatible
:raise Exception:
if the obejct does not have attr obj.is_alive and obj.isAlive,
the lib will raise ValueError
"""
if hasattr(obj, 'is_alive'):
return obj.is_alive()
elif hasattr(obj, 'isAlive'):
return obj.isAlive()
else:
raise ValueError('obj is not a object instance of threading.Thread')
[docs]class CupThread(threading.Thread):
"""
CupThread is a sub-class inherited from threading.Thread;
.. HINT::
CupThread has 3 more methods:
1. raise_exc, to send a raise-exception signal to the thread,\
TRY to let the thread raise an exception.
2. get_my_tid, get thread id
3. terminate, to stop the thread
.. CAUTION::
Notice if a thread is busy running under kernel-sysmode, it may not
response to the signals! In other words, it may not raise any
exception/terminate even though cup has send a CupThread signal!
"""
[docs] def get_my_tid(self):
"""
return thread id
"""
if not thread_alive(self):
cup.log.warn('the thread is not active')
return None
# do we have it cached?
if hasattr(self, '_thread_id'):
# pylint: disable=E0203
return self._thread_id
# pylint: disable=W0212
# no, look for it in the _active dict
for tid, tobj in threading._active.items():
if tobj is self:
# pylint: disable=W0201
self._thread_id = tid
return tid
return None
[docs] def raise_exc(self, exctype):
"""
asynchrously send 'raise exception' signal to the thread.
:param exctype:
raise Exception, exctype type is class
:return:
return 1 on success. 0 otherwise.
"""
return async_raise(self.get_my_tid(), exctype)
[docs] def terminate(self, times=15):
"""
asynchrously terminate the thread.
Return True if the termination is successful or the thread is already
stopped. Return False, otherwise.
:times:
retry times until call for failure.
"""
cnt = 0
while thread_alive(self):
self.raise_exc(cup.err.ThreadTermException)
time.sleep(1)
cnt += 1
if cnt > times:
return False
return True
[docs]class RWLock(object):
"""
Read Write Lock is a typical lock type in computer world.
Code example:
::
from cup import thread
rwlock = thread.RWLock()
# can acquire read lock
rwlock.acquire_readlock()
# can acquire read lock again if there has not been WRITE locked
rwlock.acquire_readlock() # <-- this will succeed
# rwlock.acquire_writelock() # <--- this will hang if uncommented
rwlock.release_readlock()
rwlock.acquire_writelock()
# rwlock.acquire_readlock() # <--- this will hang if uncommented
rwlock.release_writelock()
"""
def __init__(self):
self._lock = threading.Lock()
self._cond = threading.Condition(self._lock)
self._rd_num = 0
self._wt_num = 0
[docs] def acquire_writelock(self, wait_time=None):
"""
Acquire write lock.
.. IMPORTANT::
If wait_time is not None and wait_time >=0,
cup will wait until wait_time passes. If the call timeouts and
cannot get the lock, will raise RuntimeError
"""
self._cond.acquire()
if self._wt_num > 0 or self._rd_num > 0:
try:
self._cond.wait(wait_time)
except RuntimeError as error:
raise RuntimeError(str(error))
self._wt_num += 1
self._cond.release()
[docs] def release_writelock(self):
"""
release write lock
"""
self._cond.acquire()
self._wt_num -= 1
if self._wt_num == 0:
self._cond.notify_all()
self._cond.release()
[docs] def acquire_readlock(self, wait_time=None):
"""
Acquire readlock.
:param wait_time:
same to wait_time for acquire_writelock
:raise:
RuntimeError if after wait_time, cup still can NOT getthe lock
"""
self._cond.acquire()
if self._wt_num > 0:
try:
self._cond.wait(wait_time)
except RuntimeError as error:
raise RuntimeError(error)
self._rd_num += 1
self._cond.release()
[docs] def release_readlock(self):
"""
release read lock
"""
self._cond.acquire()
self._rd_num -= 1
if self._rd_num == 0 and self._wt_num == 0:
self._cond.notify()
self._cond.release()
# vi:set tw=0 ts=4 sw=4 nowrap fdm=indent