Per-account locking
Previously, we were simply locking offlineimap whenever it was running. Howver there is no reason why we shouldn't be able to invoke it in parallel, e.g. to synchronize several accounts in one offlineimap each. This patch implements the locking per-account, so that it is possible to sync different accounts at the same time. If in refresh mode, we will attempt to loop three times before giving up. This also fixes Debian bug #586655 Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
This commit is contained in:
parent
42c9e04966
commit
c7938dc081
@ -13,6 +13,13 @@ others.
|
|||||||
New Features
|
New Features
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
* Implement per-account locking, so that it will possible to sync
|
||||||
|
different accounts at the same time. The old global lock is still in
|
||||||
|
place for backward compatibility reasons (to be able to run old and
|
||||||
|
new versions of OfflineImap concurrently) and will be removed in the
|
||||||
|
future. Starting with this version, OfflineImap will be
|
||||||
|
forward-compatible with the per-account locking style.
|
||||||
|
|
||||||
Changes
|
Changes
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
@ -25,6 +25,11 @@ import os
|
|||||||
from sys import exc_info
|
from sys import exc_info
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
|
try:
|
||||||
|
import fcntl
|
||||||
|
except:
|
||||||
|
pass # ok if this fails, we can do without
|
||||||
|
|
||||||
def getaccountlist(customconfig):
|
def getaccountlist(customconfig):
|
||||||
return customconfig.getsectionlist('Account')
|
return customconfig.getsectionlist('Account')
|
||||||
|
|
||||||
@ -159,6 +164,35 @@ class SyncableAccount(Account):
|
|||||||
functions :meth:`syncrunner`, :meth:`sync`, :meth:`syncfolders`,
|
functions :meth:`syncrunner`, :meth:`sync`, :meth:`syncfolders`,
|
||||||
used for syncing."""
|
used for syncing."""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
Account.__init__(self, *args, **kwargs)
|
||||||
|
self._lockfd = None
|
||||||
|
self._lockfilepath = os.path.join(self.config.getmetadatadir(),
|
||||||
|
"%s.lock" % self)
|
||||||
|
|
||||||
|
def lock(self):
|
||||||
|
"""Lock the account, throwing an exception if it is locked already"""
|
||||||
|
self._lockfd = open(self._lockfilepath, 'w')
|
||||||
|
try:
|
||||||
|
fcntl.lockf(self._lockfd, fcntl.LOCK_EX|fcntl.LOCK_NB)
|
||||||
|
except NameError:
|
||||||
|
#fcntl not available (Windows), disable file locking... :(
|
||||||
|
pass
|
||||||
|
except IOError:
|
||||||
|
self._lockfd.close()
|
||||||
|
raise OfflineImapError("Could not lock account %s." % self,
|
||||||
|
OfflineImapError.ERROR.REPO)
|
||||||
|
|
||||||
|
def unlock(self):
|
||||||
|
"""Unlock the account, deleting the lock file"""
|
||||||
|
#If we own the lock file, delete it
|
||||||
|
if self._lockfd and not self._lockfd.closed:
|
||||||
|
self._lockfd.close()
|
||||||
|
try:
|
||||||
|
os.unlink(self._lockfilepath)
|
||||||
|
except OSError:
|
||||||
|
pass #Failed to delete for some reason.
|
||||||
|
|
||||||
def syncrunner(self):
|
def syncrunner(self):
|
||||||
self.ui.registerthread(self.name)
|
self.ui.registerthread(self.name)
|
||||||
self.ui.acct(self.name)
|
self.ui.acct(self.name)
|
||||||
@ -175,6 +209,7 @@ class SyncableAccount(Account):
|
|||||||
while looping:
|
while looping:
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
|
self.lock()
|
||||||
self.sync()
|
self.sync()
|
||||||
except (KeyboardInterrupt, SystemExit):
|
except (KeyboardInterrupt, SystemExit):
|
||||||
raise
|
raise
|
||||||
@ -194,6 +229,7 @@ class SyncableAccount(Account):
|
|||||||
if self.refreshperiod:
|
if self.refreshperiod:
|
||||||
looping = 3
|
looping = 3
|
||||||
finally:
|
finally:
|
||||||
|
self.unlock()
|
||||||
if looping and self.sleeper() >= 2:
|
if looping and self.sleeper() >= 2:
|
||||||
looping = 0
|
looping = 0
|
||||||
self.ui.acctdone(self.name)
|
self.ui.acctdone(self.name)
|
||||||
|
@ -30,14 +30,6 @@ from offlineimap.ui import UI_LIST, setglobalui, getglobalui
|
|||||||
from offlineimap.CustomConfig import CustomConfigParser
|
from offlineimap.CustomConfig import CustomConfigParser
|
||||||
|
|
||||||
|
|
||||||
try:
|
|
||||||
import fcntl
|
|
||||||
hasfcntl = 1
|
|
||||||
except:
|
|
||||||
hasfcntl = 0
|
|
||||||
|
|
||||||
lockfd = None
|
|
||||||
|
|
||||||
class OfflineImap:
|
class OfflineImap:
|
||||||
"""The main class that encapsulates the high level use of OfflineImap.
|
"""The main class that encapsulates the high level use of OfflineImap.
|
||||||
|
|
||||||
@ -46,17 +38,6 @@ class OfflineImap:
|
|||||||
oi = OfflineImap()
|
oi = OfflineImap()
|
||||||
oi.run()
|
oi.run()
|
||||||
"""
|
"""
|
||||||
def lock(self, config, ui):
|
|
||||||
global lockfd, hasfcntl
|
|
||||||
if not hasfcntl:
|
|
||||||
return
|
|
||||||
lockfd = open(config.getmetadatadir() + "/lock", "w")
|
|
||||||
try:
|
|
||||||
fcntl.flock(lockfd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
||||||
except IOError:
|
|
||||||
ui.locked()
|
|
||||||
ui.terminate(1)
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""Parse the commandline and invoke everything"""
|
"""Parse the commandline and invoke everything"""
|
||||||
|
|
||||||
@ -253,7 +234,6 @@ class OfflineImap:
|
|||||||
config.set(section, "folderfilter", folderfilter)
|
config.set(section, "folderfilter", folderfilter)
|
||||||
config.set(section, "folderincludes", folderincludes)
|
config.set(section, "folderincludes", folderincludes)
|
||||||
|
|
||||||
self.lock(config, ui)
|
|
||||||
self.config = config
|
self.config = config
|
||||||
|
|
||||||
def sigterm_handler(signum, frame):
|
def sigterm_handler(signum, frame):
|
||||||
|
Loading…
Reference in New Issue
Block a user