From 8bef90a9e62e3191e426b9429e20e136a7a37a3d Mon Sep 17 00:00:00 2001 From: bwarsaw Date: Thu, 18 Oct 2001 22:14:22 +0000 Subject: 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. --- Mailman/LockFile.py | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) (limited to 'Mailman/LockFile.py') 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 -- cgit v1.2.3-70-g09d2