diff options
| author | viega | 1998-06-13 10:45:52 +0000 |
|---|---|---|
| committer | viega | 1998-06-13 10:45:52 +0000 |
| commit | 2c9ae78bd0c559c2b26c92ea0aad31d9689a7d41 (patch) | |
| tree | 0b1f07f70922719e565c634d481589cdf2848d5a /Mailman/LockFile.py | |
| parent | cf2e26386d6eb6afe55fa9fcb42c02ed30cb9746 (diff) | |
| download | mailman-2c9ae78bd0c559c2b26c92ea0aad31d9689a7d41.tar.gz mailman-2c9ae78bd0c559c2b26c92ea0aad31d9689a7d41.tar.zst mailman-2c9ae78bd0c559c2b26c92ea0aad31d9689a7d41.zip | |
Diffstat (limited to 'Mailman/LockFile.py')
| -rw-r--r-- | Mailman/LockFile.py | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/Mailman/LockFile.py b/Mailman/LockFile.py new file mode 100644 index 000000000..cf1052d91 --- /dev/null +++ b/Mailman/LockFile.py @@ -0,0 +1,100 @@ +# Copyright (C) 1998 by the Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# flock.py: Portable file locking. John Viega, Jun 13, 1998 + + +"""Portable (?) file locking with timeouts. +This code should work with all versions of NFS. +The algorithm was suggested by the GNU/Linux open() man page. Make +sure no malicious people have access to link() to the lock file. +""" + +# Potential change: let the locker insert a field saying when he promises +# to be done with the lock, so if he needs more time than the other +# processes think he needs, he can say so. + +import socket, os, time + +DEFAULT_HUNG_TIMEOUT = 30 +DEFAULT_SLEEP_INTERVAL = .25 + +AlreadyCalledLockError = "AlreadyCalledLockError" +NotLockedError = "NotLockedError" +TimeOutError = "TimeOutError" + +class FileLock: + def __init__(self, lockfile, hung_timeout = DEFAULT_HUNG_TIMEOUT, + sleep_interval = DEFAULT_SLEEP_INTERVAL): + self.lockfile = lockfile + self.hung_timeout = hung_timeout + self.sleep_interval = sleep_interval + self.tmpfname = "%s.%s.%d" % (lockfile, socket.gethostname(), os.getpid()) + self.locked = 0 + if not os.path.exists(self.lockfile): + try: + file = open(self.lockfile, "w+") + except IOError: + pass + + # Note that no one new can grab the lock once we've opened our + # tmpfile until we close it, even if we don't have the lock. So + # checking the PID and stealing the lock are garunteed to be atomic. + def lock(self, timeout = 0): + """Blocks until the lock can be obtained. Raises a TimeOutError + exception if a positive timeout value is given and that time + elapses before the lock is obtained. + """ + if timeout > 0: + timeout_time = time.time() + timeout + last_pid = -1 + if self.locked: + raise AlreadyCalledLockError + while 1: + os.link(self.lockfile, self.tmpfname) + if os.stat(self.tmpfname)[3] == 2: + file = open(self.tmpfname, 'w+') + file.write(`os.getpid(),self.tmpfname`) + file.close() + self.locked = 1 + break + if timeout and timeout_time < time.time(): + raise TimeOutError + file = open(self.tmpfname, 'r') + try: + pid,winner = eval(file.read()) + except SyntaxError: # no info in file... *can* happen + file.close() + continue + file.close() + if pid <> last_pid: + last_pid = pid + stime = time.time() + if (stime + self.hung_timeout < time.time()) and self.hung_timeout > 0: + file = open(self.tmpfname, 'w+') + file.write(`os.getpid(),self.tmpfname`) + os.unlink(winner) + self.locked = 1 + break + os.unlink(self.tmpfname) + time.sleep(self.sleep_interval) + # This could error if the lock is stolen. You must catch it. + def unlock(self): + if not self.locked(): + raise NotLockedError + os.unlink(self.tmpfname) + def locked(self): + return os.path.exists(self.tmpfname) and self.locked
\ No newline at end of file |
