summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbwarsaw2001-10-18 22:14:22 +0000
committerbwarsaw2001-10-18 22:14:22 +0000
commit8bef90a9e62e3191e426b9429e20e136a7a37a3d (patch)
treee9e505973b5af6215d99c11e4ccb0f3662b45a54
parent1032cfa3ee1139817b655c604ab46a0d6992c5db (diff)
downloadmailman-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.py43
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