90 lines
2.7 KiB
Python
90 lines
2.7 KiB
Python
|
|||
|
|
import sys
|
||
|
|
import _thread as thread
|
||
|
|
import threading
|
||
|
|
|
||
|
|
cmd = sys.modules["pymol.cmd"]
|
||
|
|
|
||
|
|
from pymol import _cmd
|
||
|
|
|
||
|
|
# WARNING: internal routines, subject to change
|
||
|
|
def lock_without_glut(_self=cmd):
|
||
|
|
with _self.lock_api_glut:
|
||
|
|
_self.lock(_self)
|
||
|
|
|
||
|
|
class LockCM(object):
|
||
|
|
'''
|
||
|
|
API lock context manager
|
||
|
|
'''
|
||
|
|
def __init__(self, _self=cmd):
|
||
|
|
self.cmd = _self._weakrefproxy
|
||
|
|
def __enter__(self):
|
||
|
|
lock(self.cmd)
|
||
|
|
def __exit__(self, type, value, traceback):
|
||
|
|
unlock(None if type is None else -1, self.cmd)
|
||
|
|
|
||
|
|
def lock(_self=cmd): # INTERNAL -- API lock
|
||
|
|
return _self.lock_api.acquire()
|
||
|
|
|
||
|
|
def lock_attempt(_self=cmd): # INTERNAL
|
||
|
|
return _self.lock_api.acquire(blocking=0)
|
||
|
|
|
||
|
|
def block_flush(_self=cmd):
|
||
|
|
with _self.lockcm:
|
||
|
|
_self.lock_api_allow_flush = 0
|
||
|
|
|
||
|
|
def unblock_flush(_self=cmd):
|
||
|
|
with _self.lockcm:
|
||
|
|
_self.lock_api_allow_flush = 1
|
||
|
|
|
||
|
|
def unlock(result=None,_self=cmd): # INTERNAL
|
||
|
|
'''
|
||
|
|
Release the API lock and flush the command queue
|
||
|
|
'''
|
||
|
|
thread_owns_gui = _self.is_gui_thread()
|
||
|
|
|
||
|
|
if thread_owns_gui and _self.reaper is not None:
|
||
|
|
# TODO what's the use case? How can the finish_launching() thread
|
||
|
|
# die and what's the expected behavior? (see PYMOL-3247)
|
||
|
|
try:
|
||
|
|
if not _self.reaper.is_alive():
|
||
|
|
if _self._pymol.invocation.options.no_gui:
|
||
|
|
_cmd.quit(_self._COb) # TO FIX
|
||
|
|
else:
|
||
|
|
_self.reaper = None
|
||
|
|
except:
|
||
|
|
pass
|
||
|
|
|
||
|
|
_self.lock_api.release()
|
||
|
|
|
||
|
|
# don't flush if we have an incipient error (negative input)
|
||
|
|
if _self.is_error(result):
|
||
|
|
return
|
||
|
|
|
||
|
|
if thread_owns_gui:
|
||
|
|
if _self.lock_api_allow_flush:
|
||
|
|
_cmd.flush_now(_self._COb)
|
||
|
|
else:
|
||
|
|
# TODO: what's the logic here? We assume we have to wait for
|
||
|
|
# cmd.do() calls being executed by GUI thread, but only for 200ms?
|
||
|
|
# What's the concrete use case and would there be a better (more
|
||
|
|
# predictable) logic for it? Isn't this an invitation for race
|
||
|
|
# conditions, e.g. other threads could call cmd.do() and make us
|
||
|
|
# wait for it? (see PYMOL-3248)
|
||
|
|
# NOTE: affects API perf. for "do" and delayed-exec
|
||
|
|
w = 0.0005
|
||
|
|
while w < 0.1 and _cmd.wait_queue(_self._COb):
|
||
|
|
threading.Event().wait(w)
|
||
|
|
w *= 2
|
||
|
|
|
||
|
|
def is_gui_thread(_self=cmd): # internal
|
||
|
|
'''
|
||
|
|
Return true if the current thread is the GUI thread (e.g. GLUT or Qt)
|
||
|
|
or if there is no GUI thread. False otherwise.
|
||
|
|
'''
|
||
|
|
gui_ident = _self._pymol.glutThread
|
||
|
|
return gui_ident is None or gui_ident == thread.get_ident()
|
||
|
|
|
||
|
|
def interrupt(_self=cmd): # asynch -- no locking!
|
||
|
|
_cmd.interrupt(_self._COb,1)
|