win32 file locking
Andrew Bennetts
andrew at canonical.com
Wed May 25 12:55:39 BST 2005
On Wed, May 25, 2005 at 01:43:43AM -0500, John A Meinel wrote:
[...]
>
> def __del__(self):
> """Abort if the file has not already been closed."""
> - self.close()
> + if not self.closed:
> + raise RuntimeError('AtomicFile must have
> commit()/abort()/close() called before it expires.')
>
> def commit(self):
> """Close the file and move to final name."""
>
> This way AtomicFile will throw an exception if it is not used correctly.
There's not much point raising an exception in a __del__ -- who's going to
catch it? There's no caller other than the garbage collector, which will
just log a warning about it. Just explicitly log a warning instead :)
(See, another reason not to use __del__ <wink>)
> The problem for Branch() is that I don't think anyone is going to go
> around and call "unlock()" when they are done with it, meaning that all
> branches are locked for the lifetime of bzrlib. Since bzrlib is meant to
> be a library, potentially used by a GUI, this could be a long time.
>
> The problem is that Branch() is commonly used as a temporary object. For
> instance the 'bzr revno' command is done like:
>
> print Branch('.').revno()
>
> How do you unlock a branch if you never have an object to call
> 'unlock()' on?
It would be nice if this could just work. I don't have an easy answer. If
you can be confident that Branch holds references to things that will never
lead to a reference cycle, __del__ is ok, but I think that's unrealistic.
At a glance, it looks like the DebugInfo idea could be adapted to unlocking
the Branch:
*** modified file 'bzrlib/branch.py'
--- bzrlib/branch.py
+++ bzrlib/branch.py
@@ -155,18 +155,20 @@
raise e
fcntl.lockf(lockfile, lm)
- def unlock():
+ def unlocker():
fcntl.lockf(lockfile, fcntl.LOCK_UN)
os.close(lockfile)
- self._lockmode = None
- self.unlock = unlock
- self._lockmode = mode
except ImportError:
warning("please write a locking method for platform %r" % sys.platform)
- def unlock():
- self._lockmode = None
- self.unlock = unlock
- self._lockmode = mode
+ def unlocker():
+ None
+ self._lockmode = mode
+ def unlock():
+ unlocker()
+ self._lockmode = None
+ self._unlocker.unlock = None
+ self.unlock = unlock
+ self._unlocker = BranchUnlocker(unlocker)
def _need_readlock(self):
@@ -710,6 +712,12 @@
self._write_inventory(inv)
+class BranchUnlocker:
+ def __init__(self, unlock):
+ self.unlock = unlock
+ def __del__(self):
+ if self.unlock is not None:
+ self.unlock()
class ScratchBranch(Branch):
The key is to avoid reference cycles with objects with __del__. It's hard
to prevent that with objects in a public API, like Branch. It's possible
with private objects, like BranchUnlocker.
What do you think?
-Andrew.
More information about the bazaar
mailing list