diff options
| author | bwarsaw | 2001-10-18 22:14:22 +0000 |
|---|---|---|
| committer | bwarsaw | 2001-10-18 22:14:22 +0000 |
| commit | 8bef90a9e62e3191e426b9429e20e136a7a37a3d (patch) | |
| tree | e9e505973b5af6215d99c11e4ccb0f3662b45a54 | |
| parent | 1032cfa3ee1139817b655c604ab46a0d6992c5db (diff) | |
| download | mailman-8bef90a9e62e3191e426b9429e20e136a7a37a3d.tar.gz mailman-8bef90a9e62e3191e426b9429e20e136a7a37a3d.tar.zst mailman-8bef90a9e62e3191e426b9429e20e136a7a37a3d.zip | |
Added some internal methods to help with mailmanctl's transfer of lock
ownership to its forked children. You shouldn't normally use these
methods, but to make mailmanctl maximally convenient, we need to
create a lock in a parent and manage the lock in the child.
_transfer_to(): Called in the parent, this twiddles with lock
internals to set the temp file name to include the pid of the child
(passed in). It also transfers lock claims to the new temp file,
i.e. the child, and removes the claim made by the parent (the previous
winner).
_take_possession(): Called by the child, this resets the temp file
name to include the pid of the child. For this, it had the pid of the
parent process prior to the fork.
__del__(): Don't finalize() the lock if we don't own it (ownership is
implied by creation, and relinquished by _transfer_to()). This way,
when the parent transfers ownership to the child and then exits with
sys.exit(), the act of destroying the lock instance in the parent
won't unlock the lock.
__init__(): Initialize __owned to 1.
| -rw-r--r-- | Mailman/LockFile.py | 43 |
1 files changed, 42 insertions, 1 deletions
diff --git a/Mailman/LockFile.py b/Mailman/LockFile.py index a4b9dbb66..ca888623c 100644 --- a/Mailman/LockFile.py +++ b/Mailman/LockFile.py @@ -176,6 +176,8 @@ class LockFile: lockfile, socket.gethostname(), os.getpid()) self.__withlogging = withlogging self.__logprefix = os.path.split(self.__lockfile)[1] + # For transferring ownership across a fork. + self.__owned = 1 def set_lifetime(self, lifetime): """Set a new lock lifetime. @@ -333,7 +335,46 @@ class LockFile: self.unlock(unconditionally=1) def __del__(self): - self.finalize() + if self.__owned: + self.finalize() + + # Use these only if you're transfering ownership to a child process across + # a fork. Use at your own risk, but it should be race-condition safe. + # _transfer_to() is called in the parent, passing in the pid of the + # child. _take_possession() is called in the child, and blocks until the + # parent has transferred possession to the child. + def _transfer_to(self, pid): + # First touch it so it won't get broken while we're fiddling about. + self.__touch() + # Find out current claim's temp filename + winner = self.__read() + # Now twiddle ours to the given pid + self.__tmpfname = '%s.%s.%d' % ( + self.__lockfile, socket.gethostname(), pid) + # Create a hard link from the global lock file to the temp file. This + # actually does things in reverse order of normal operation because we + # know that lockfile exists, and tmpfname better not! + os.link(self.__lockfile, self.__tmpfname) + # Now update the lock file to contain a reference to the new owner + self.__write() + # Toggle off our ownership of the file so we don't try to finalize it + # in our __del__() + self.__owned = 0 + # Unlink the old winner, completing the transfer + os.unlink(winner) + # And do some sanity checks + assert self.__linkcount() == 2 + assert self.locked() + self.__writelog('transferred the lock') + + def _take_possession(self): + self.__tmpfname = '%s.%s.%d' % ( + self.__lockfile, socket.gethostname(), os.getpid()) + # Wait until the linkcount is 2, indicating the parent has completed + # the transfer. + while self.__linkcount() <> 2: + time.sleep(0.25) + self.__writelog('took possession of the lock') # # Private interface |
