Remove MultiLock implementation
Currently the Curses code is broken. Importing offlineimap.ui.Curses will not succeed due to cyclic imports (threadutils imports ui, but ui wants threadutils.MultiLock). So Curses cannot be chosen. Incidentally, the only part in the code that uses "MultiLock" is the Curses UI, to prevent concurrent access from several threads to the ui-internal thread list and to IO resources such as the screen. Fortunately for these purposes we don't need a MultiLock, so we can do away with that implementation completely. A simple RLock that allows us to have a thread "own" a lock and makes other threads wanting access to the resource wait until the owning thread is finished. The MultiLock implementation looked a bit weird, so simplifying code here is a good thing, it might well be that we fix some "hangs" that have been reported (and that would only ever occur with the Curses UI). Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de> Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
This commit is contained in:
parent
fa60f3f9b7
commit
83a85bb3fb
@ -246,54 +246,3 @@ class InstanceLimitedThread(ExitNotifyThread):
|
|||||||
finally:
|
finally:
|
||||||
if instancelimitedsems and instancelimitedsems[self.instancename]:
|
if instancelimitedsems and instancelimitedsems[self.instancename]:
|
||||||
instancelimitedsems[self.instancename].release()
|
instancelimitedsems[self.instancename].release()
|
||||||
|
|
||||||
|
|
||||||
######################################################################
|
|
||||||
# Multi-lock -- capable of handling a single thread requesting a lock
|
|
||||||
# multiple times
|
|
||||||
######################################################################
|
|
||||||
|
|
||||||
class MultiLock:
|
|
||||||
def __init__(self):
|
|
||||||
self.lock = Lock()
|
|
||||||
self.statuslock = Lock()
|
|
||||||
self.locksheld = {}
|
|
||||||
|
|
||||||
def acquire(self):
|
|
||||||
"""Obtain a lock. Provides nice support for a single
|
|
||||||
thread trying to lock it several times -- as may be the case
|
|
||||||
if one I/O-using object calls others, while wanting to make it all
|
|
||||||
an atomic operation. Keeps a "lock request count" for the current
|
|
||||||
thread, and acquires the lock when it goes above zero, releases when
|
|
||||||
it goes below one.
|
|
||||||
|
|
||||||
This call is always blocking."""
|
|
||||||
|
|
||||||
# First, check to see if this thread already has a lock.
|
|
||||||
# If so, increment the lock count and just return.
|
|
||||||
self.statuslock.acquire()
|
|
||||||
try:
|
|
||||||
threadid = thread.get_ident()
|
|
||||||
|
|
||||||
if threadid in self.locksheld:
|
|
||||||
self.locksheld[threadid] += 1
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
# This is safe because it is a per-thread structure
|
|
||||||
self.locksheld[threadid] = 1
|
|
||||||
finally:
|
|
||||||
self.statuslock.release()
|
|
||||||
self.lock.acquire()
|
|
||||||
|
|
||||||
def release(self):
|
|
||||||
self.statuslock.acquire()
|
|
||||||
try:
|
|
||||||
threadid = thread.get_ident()
|
|
||||||
if self.locksheld[threadid] > 1:
|
|
||||||
self.locksheld[threadid] -= 1
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
del self.locksheld[threadid]
|
|
||||||
self.lock.release()
|
|
||||||
finally:
|
|
||||||
self.statuslock.release()
|
|
||||||
|
@ -16,10 +16,9 @@
|
|||||||
# along with this program; if not, write to the Free Software
|
# along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
from threading import *
|
from threading import RLock, currentThread
|
||||||
from offlineimap.ui.UIBase import UIBase
|
from offlineimap.ui.UIBase import UIBase
|
||||||
import thread
|
import thread
|
||||||
from offlineimap.threadutil import MultiLock
|
|
||||||
|
|
||||||
class BlinkenBase:
|
class BlinkenBase:
|
||||||
"""This is a mix-in class that should be mixed in with either UIBase
|
"""This is a mix-in class that should be mixed in with either UIBase
|
||||||
@ -85,7 +84,8 @@ class BlinkenBase:
|
|||||||
def init_banner(s):
|
def init_banner(s):
|
||||||
s.availablethreadframes = {}
|
s.availablethreadframes = {}
|
||||||
s.threadframes = {}
|
s.threadframes = {}
|
||||||
s.tflock = MultiLock()
|
#tflock protects the s.threadframes manipulation to only happen from 1 thread
|
||||||
|
s.tflock = RLock()
|
||||||
|
|
||||||
def threadExited(s, thread):
|
def threadExited(s, thread):
|
||||||
threadid = thread.threadid
|
threadid = thread.threadid
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
# along with this program; if not, write to the Free Software
|
# along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
from threading import Lock, Event
|
from threading import RLock, Lock, Event
|
||||||
import time
|
import time
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
@ -32,7 +32,8 @@ acctkeys = '1234567890abcdefghijklmnoprstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-=;/.,'
|
|||||||
class CursesUtil:
|
class CursesUtil:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.pairlock = Lock()
|
self.pairlock = Lock()
|
||||||
self.iolock = offlineimap.threadutil.MultiLock()
|
# iolock protects access to the
|
||||||
|
self.iolock = RLock()
|
||||||
self.start()
|
self.start()
|
||||||
|
|
||||||
def initpairs(self):
|
def initpairs(self):
|
||||||
@ -45,9 +46,20 @@ class CursesUtil:
|
|||||||
self.pairlock.release()
|
self.pairlock.release()
|
||||||
|
|
||||||
def lock(self):
|
def lock(self):
|
||||||
|
"""Locks the Curses ui thread
|
||||||
|
|
||||||
|
Can be invoked multiple times from the owning thread. Invoking
|
||||||
|
from a non-owning thread blocks and waits until it has been
|
||||||
|
unlocked by the owning thread."""
|
||||||
self.iolock.acquire()
|
self.iolock.acquire()
|
||||||
|
|
||||||
def unlock(self):
|
def unlock(self):
|
||||||
|
"""Unlocks the Curses ui thread
|
||||||
|
|
||||||
|
Decrease the lock counter by one and unlock the ui thread if the
|
||||||
|
counter reaches 0. Only call this method when the calling
|
||||||
|
thread owns the lock. A RuntimeError is raised if this method is
|
||||||
|
called when the lock is unlocked."""
|
||||||
self.iolock.release()
|
self.iolock.release()
|
||||||
|
|
||||||
def locked(self, target, *args, **kwargs):
|
def locked(self, target, *args, **kwargs):
|
||||||
|
Loading…
Reference in New Issue
Block a user