summaryrefslogtreecommitdiff
path: root/Mailman/flock.py
diff options
context:
space:
mode:
Diffstat (limited to 'Mailman/flock.py')
-rw-r--r--Mailman/flock.py198
1 files changed, 0 insertions, 198 deletions
diff --git a/Mailman/flock.py b/Mailman/flock.py
deleted file mode 100644
index 70e6c947a..000000000
--- a/Mailman/flock.py
+++ /dev/null
@@ -1,198 +0,0 @@
-# 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
-import string
-#from stat import ST_NLINK
-ST_NLINK = 3 # faster
-
-DEFAULT_HUNG_TIMEOUT = 15
-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.__kickstart()
-
- def __del__(self):
- if self.locked():
- self.unlock()
-
- def __kickstart(self, force=0):
- # forcing means to remove the original lockfile, and create a new one.
- # this might be necessary if the file contains bogus locker
- # information such that the owner of the lock can't be determined
- if force:
- try:
- os.unlink(self.lockfile)
- except IOError:
- pass
- if not os.path.exists(self.lockfile):
- try:
- # make sure it's group writable
- oldmask = os.umask(002)
- try:
- file = open(self.lockfile, 'w+')
- file.close()
- finally:
- os.umask(oldmask)
- except IOError:
- pass
-
- def __write(self):
- # make sure it's group writable
- oldmask = os.umask(002)
- try:
- fp = open(self.tmpfname, 'w')
- fp.write('%d %s\n' % (os.getpid(), self.tmpfname))
- fp.close()
- finally:
- os.umask(oldmask)
-
- def __read(self):
- # can raise ValueError in two situations:
- #
- # either first element wasn't an integer (a valid pid), or we didn't
- # get a 2-list from the string.split. Either way, the data in the
- # file is bogus, but this is caught higher up
- fp = open(self.tmpfname, 'r')
- try:
- pid, winner = string.split(string.strip(fp.read()))
- finally:
- fp.close()
- return int(pid), winner
-
- # 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 guaranteed 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
- stolen = 0
- while 1:
- # create the hard link and test for exactly 2 links to the file
- os.link(self.lockfile, self.tmpfname)
- if os.stat(self.tmpfname)[ST_NLINK] == 2:
- # we have the lock (since there are no other links to the lock
- # file), so we can piss on the hydrant
- self.__write()
- break
- if timeout and timeout_time < time.time():
- os.unlink(self.tmpfname)
- raise TimeOutError
- # someone else must have gotten the lock. let's find out who it
- # is. if there is some bogosity in the lock file's data then we
- # will steal the lock.
- try:
- pid, winner = self.__read()
- except ValueError:
- os.unlink(self.tmpfname)
- self.__kickstart(force=1)
- continue
- # If we've gotten to here, we should be the winner, because
- # otherwise, an AlreadyCalledLockError should have been raised
- # above, and we should have never gotten into this loop. However,
- # the following scenario can occur, and this is what the stolen
- # flag takes care of:
- #
- # Say that processes A and B are already laying claim to the lock
- # by creating link files, and say A actually has the lock (i.e., A
- # is the winner). We are process C and we lay claim by creating a
- # link file. All is cool, and we'll trip the pid <> last_pid
- # test, unlink our claim, sleep and try again. Second time
- # through our loop, we again determine that A is the winner but
- # because it and B are swapped out, we trip our hung_timeout test
- # and figure we need to steal the lock. So we piss on the hydrant
- # (write our info into the lock file), unlink A's link file and go
- # around the loop again. However, because B is still laying
- # claim, and we never knew it (since it wasn't the winner), we
- # again have 3 links to the lock file the next time through this
- # loop, and the assert will trip.
- #
- # The stolen flag alerts us that this has happened, but I still
- # worry that our logic might be flawed here.
- assert stolen or winner <> self.tmpfname
- # record the previous winner and the current time
- if pid <> last_pid:
- last_pid = pid
- stime = time.time()
- # here's where we potentially steal the lock. if the pid in the
- # lockfile hasn't changed in hung_timeout seconds, then we assume
- # that the locker crashed
- elif stime + self.hung_timeout < time.time():
- self.__write() # steal
- stolen = 1
- try:
- os.unlink(winner)
- except os.error:
- # winner lockfile could be missing
- pass
- os.unlink(self.tmpfname)
- continue
- # okay, someone else has the lock, we didn't steal it, and it
- # hasn't timed out yet. So let's wait for the owner of the lock
- # to give it up. Unlink our claim to the lock and sleep for a
- # while, then try again
- 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):
- if not os.path.exists(self.tmpfname):
- return 0
- pid, winner = self.__read()
- return pid == os.getpid()
-
- # use with caution!!!
- def steal(self):
- self.__write()