~bzr-pqm/bzr/bzr.dev

4100.1.4 by Martin Pool
LockContention on OS locks now includes the filename
1
# Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
577 by Martin Pool
- merge portable lock module from John
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
577 by Martin Pool
- merge portable lock module from John
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
577 by Martin Pool
- merge portable lock module from John
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
577 by Martin Pool
- merge portable lock module from John
16
17
1553.5.39 by Martin Pool
More lock docs
18
"""Locking using OS file locks or file existence.
577 by Martin Pool
- merge portable lock module from John
19
1553.5.46 by Martin Pool
doc
20
Note: This method of locking is generally deprecated in favour of LockDir, but
21
is used to lock local WorkingTrees, and by some old formats.  It's accessed
22
through Transport.lock_read(), etc.
577 by Martin Pool
- merge portable lock module from John
23
24
This module causes two methods, lock() and unlock() to be defined in
25
any way that works on the current platform.
26
27
It is not specified whether these locks are reentrant (i.e. can be
28
taken repeatedly by a single process) or whether they exclude
2353.3.9 by John Arbash Meinel
Update the lock code and test code so that if more than one
29
different threads in a single process.  That reentrancy is provided by
1553.5.39 by Martin Pool
More lock docs
30
LockableFiles.
615 by Martin Pool
Major rework of locking code:
31
32
This defines two classes: ReadLock and WriteLock, which can be
33
implemented in different ways on different platforms.  Both have an
34
unlock() method.
614 by Martin Pool
- unify two defintions of LockError
35
"""
577 by Martin Pool
- merge portable lock module from John
36
1185.65.29 by Robert Collins
Implement final review suggestions.
37
import errno
2353.3.12 by John Arbash Meinel
Don't create the alternative lock types unless we are on windows.
38
import sys
577 by Martin Pool
- merge portable lock module from John
39
2353.3.11 by John Arbash Meinel
Code cleanup
40
from bzrlib import (
41
    errors,
42
    osutils,
43
    trace,
44
    )
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
45
from bzrlib.hooks import HookPoint, Hooks
3331.3.1 by Robert Collins
* ``LockDir`` lock acquisition and release now trigger hooks allowing
46
47
3724.1.1 by Martin Pool
Move Lock hooks onto a base Lock class and make them more consistent with other lock classes
48
class LockHooks(Hooks):
49
50
    def __init__(self):
51
        Hooks.__init__(self)
4119.3.2 by Robert Collins
Migrate existing hooks over to the new HookPoint infrastructure.
52
        self.create_hook(HookPoint('lock_acquired',
53
            "Called with a bzrlib.lock.LockResult when a physical lock is "
54
            "acquired.", (1, 8), None))
55
        self.create_hook(HookPoint('lock_released',
56
            "Called with a bzrlib.lock.LockResult when a physical lock is "
57
            "released.", (1, 8), None))
3724.1.1 by Martin Pool
Move Lock hooks onto a base Lock class and make them more consistent with other lock classes
58
59
60
class Lock(object):
61
    """Base class for locks.
62
63
    :cvar hooks: Hook dictionary for operations on locks.
64
    """
65
66
    hooks = LockHooks()
67
68
3331.3.1 by Robert Collins
* ``LockDir`` lock acquisition and release now trigger hooks allowing
69
class LockResult(object):
3331.3.5 by Martin Pool
Move physical lock hooks onto new PhysicalLock class variable
70
    """Result of an operation on a lock; passed to a hook"""
3331.3.1 by Robert Collins
* ``LockDir`` lock acquisition and release now trigger hooks allowing
71
3331.3.2 by Robert Collins
Polish on lock hooks to be easier to use.
72
    def __init__(self, lock_url, details=None):
3331.3.1 by Robert Collins
* ``LockDir`` lock acquisition and release now trigger hooks allowing
73
        """Create a lock result for lock with optional details about the lock."""
3331.3.2 by Robert Collins
Polish on lock hooks to be easier to use.
74
        self.lock_url = lock_url
3331.3.1 by Robert Collins
* ``LockDir`` lock acquisition and release now trigger hooks allowing
75
        self.details = details
76
77
    def __eq__(self, other):
3331.3.2 by Robert Collins
Polish on lock hooks to be easier to use.
78
        return self.lock_url == other.lock_url and self.details == other.details
1711.8.1 by John Arbash Meinel
Branch.lock_read/lock_write/unlock should handle failures
79
577 by Martin Pool
- merge portable lock module from John
80
3224.5.32 by Andrew Bennetts
Tidy conditional imports in bzrlib/lock.py as suggested by John's review.
81
try:
82
    import fcntl
83
    have_fcntl = True
84
except ImportError:
85
    have_fcntl = False
86
87
have_pywin32 = False
88
have_ctypes_win32 = False
89
if sys.platform == 'win32':
90
    import msvcrt
91
    try:
92
        import win32con, win32file, pywintypes, winerror
93
        have_pywin32 = True
94
    except ImportError:
95
        pass
96
97
    try:
98
        import ctypes
99
        have_ctypes_win32 = True
100
    except ImportError:
101
        pass
102
103
2555.3.2 by Martin Pool
rename _base_Lock to _OSLock
104
class _OSLock(object):
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
105
2353.3.4 by John Arbash Meinel
Clean up the lock.py code to use less indenting, and conform to better coding practise.
106
    def __init__(self):
107
        self.f = None
2353.3.10 by John Arbash Meinel
Cleanup errors, and change ReadOnlyLockError to pass around more details.
108
        self.filename = None
2353.3.4 by John Arbash Meinel
Clean up the lock.py code to use less indenting, and conform to better coding practise.
109
615 by Martin Pool
Major rework of locking code:
110
    def _open(self, filename, filemode):
2353.3.11 by John Arbash Meinel
Code cleanup
111
        self.filename = osutils.realpath(filename)
656 by Martin Pool
- create branch lock files if they don't exist
112
        try:
2353.3.10 by John Arbash Meinel
Cleanup errors, and change ReadOnlyLockError to pass around more details.
113
            self.f = open(self.filename, filemode)
656 by Martin Pool
- create branch lock files if they don't exist
114
            return self.f
115
        except IOError, e:
2353.3.5 by John Arbash Meinel
Fall back on ctypes to get LockFileEx, because msvcrt.locking()
116
            if e.errno in (errno.EACCES, errno.EPERM):
2872.5.1 by Martin Pool
Avoid internal error tracebacks on failure to lock on readonly transport (#129701).
117
                raise errors.LockFailed(self.filename, str(e))
656 by Martin Pool
- create branch lock files if they don't exist
118
            if e.errno != errno.ENOENT:
119
                raise
120
121
            # maybe this is an old branch (before may 2005)
2353.3.11 by John Arbash Meinel
Code cleanup
122
            trace.mutter("trying to create missing lock %r", self.filename)
2353.3.9 by John Arbash Meinel
Update the lock code and test code so that if more than one
123
2353.3.10 by John Arbash Meinel
Cleanup errors, and change ReadOnlyLockError to pass around more details.
124
            self.f = open(self.filename, 'wb+')
656 by Martin Pool
- create branch lock files if they don't exist
125
            return self.f
126
2353.3.4 by John Arbash Meinel
Clean up the lock.py code to use less indenting, and conform to better coding practise.
127
    def _clear_f(self):
128
        """Clear the self.f attribute cleanly."""
129
        if self.f:
130
            self.f.close()
131
            self.f = None
132
615 by Martin Pool
Major rework of locking code:
133
    def __del__(self):
134
        if self.f:
135
            from warnings import warn
136
            warn("lock on %r not released" % self.f)
137
            self.unlock()
2353.3.9 by John Arbash Meinel
Update the lock code and test code so that if more than one
138
615 by Martin Pool
Major rework of locking code:
139
    def unlock(self):
140
        raise NotImplementedError()
141
142
2353.3.9 by John Arbash Meinel
Update the lock code and test code so that if more than one
143
_lock_classes = []
2353.3.4 by John Arbash Meinel
Clean up the lock.py code to use less indenting, and conform to better coding practise.
144
145
146
if have_fcntl:
147
    LOCK_SH = fcntl.LOCK_SH
148
    LOCK_NB = fcntl.LOCK_NB
149
    lock_EX = fcntl.LOCK_EX
150
615 by Martin Pool
Major rework of locking code:
151
2555.3.2 by Martin Pool
rename _base_Lock to _OSLock
152
    class _fcntl_FileLock(_OSLock):
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
153
154
        def _unlock(self):
1185.9.2 by Harald Meland
Use fcntl.lockf() rather than fcntl.flock() to support NFS file systems.
155
            fcntl.lockf(self.f, fcntl.LOCK_UN)
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
156
            self._clear_f()
157
158
615 by Martin Pool
Major rework of locking code:
159
    class _fcntl_WriteLock(_fcntl_FileLock):
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
160
2353.4.1 by John Arbash Meinel
(broken) Change fcntl locks to be properly exclusive within the same process.
161
        _open_locks = set()
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
162
615 by Martin Pool
Major rework of locking code:
163
        def __init__(self, filename):
2353.3.4 by John Arbash Meinel
Clean up the lock.py code to use less indenting, and conform to better coding practise.
164
            super(_fcntl_WriteLock, self).__init__()
2353.4.2 by John Arbash Meinel
[merge] LockCleanup changes.
165
            # Check we can grab a lock before we actually open the file.
166
            self.filename = osutils.realpath(filename)
2363.3.3 by John Arbash Meinel
make Write locks not block on Read locks, so that revert tests don't fail
167
            if self.filename in _fcntl_WriteLock._open_locks:
2353.4.1 by John Arbash Meinel
(broken) Change fcntl locks to be properly exclusive within the same process.
168
                self._clear_f()
169
                raise errors.LockContention(self.filename)
170
2353.4.2 by John Arbash Meinel
[merge] LockCleanup changes.
171
            self._open(self.filename, 'rb+')
2255.10.2 by John Arbash Meinel
Update to dirstate locking.
172
            # reserve a slot for this lock - even if the lockf call fails,
173
            # at thisi point unlock() will be called, because self.f is set.
174
            # TODO: make this fully threadsafe, if we decide we care.
2353.4.1 by John Arbash Meinel
(broken) Change fcntl locks to be properly exclusive within the same process.
175
            _fcntl_WriteLock._open_locks.add(self.filename)
615 by Martin Pool
Major rework of locking code:
176
            try:
2255.10.2 by John Arbash Meinel
Update to dirstate locking.
177
                # LOCK_NB will cause IOError to be raised if we can't grab a
178
                # lock right away.
179
                fcntl.lockf(self.f, fcntl.LOCK_EX | fcntl.LOCK_NB)
1185.65.29 by Robert Collins
Implement final review suggestions.
180
            except IOError, e:
2255.10.2 by John Arbash Meinel
Update to dirstate locking.
181
                if e.errno in (errno.EAGAIN, errno.EACCES):
182
                    # We couldn't grab the lock
183
                    self.unlock()
1185.65.29 by Robert Collins
Implement final review suggestions.
184
                # we should be more precise about whats a locking
185
                # error and whats a random-other error
4100.1.4 by Martin Pool
LockContention on OS locks now includes the filename
186
                raise errors.LockContention(self.filename, e)
615 by Martin Pool
Major rework of locking code:
187
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
188
        def unlock(self):
2353.4.1 by John Arbash Meinel
(broken) Change fcntl locks to be properly exclusive within the same process.
189
            _fcntl_WriteLock._open_locks.remove(self.filename)
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
190
            self._unlock()
191
192
615 by Martin Pool
Major rework of locking code:
193
    class _fcntl_ReadLock(_fcntl_FileLock):
1185.65.29 by Robert Collins
Implement final review suggestions.
194
2353.4.1 by John Arbash Meinel
(broken) Change fcntl locks to be properly exclusive within the same process.
195
        _open_locks = {}
2353.3.3 by John Arbash Meinel
Define an explicit error when trying to grab a write lock on a readonly file.
196
2353.4.11 by John Arbash Meinel
Remove the unused _ignore_write_lock parameter.
197
        def __init__(self, filename):
2353.3.4 by John Arbash Meinel
Clean up the lock.py code to use less indenting, and conform to better coding practise.
198
            super(_fcntl_ReadLock, self).__init__()
2353.4.2 by John Arbash Meinel
[merge] LockCleanup changes.
199
            self.filename = osutils.realpath(filename)
2353.4.1 by John Arbash Meinel
(broken) Change fcntl locks to be properly exclusive within the same process.
200
            _fcntl_ReadLock._open_locks.setdefault(self.filename, 0)
201
            _fcntl_ReadLock._open_locks[self.filename] += 1
1185.65.29 by Robert Collins
Implement final review suggestions.
202
            self._open(filename, 'rb')
615 by Martin Pool
Major rework of locking code:
203
            try:
2255.10.2 by John Arbash Meinel
Update to dirstate locking.
204
                # LOCK_NB will cause IOError to be raised if we can't grab a
205
                # lock right away.
206
                fcntl.lockf(self.f, fcntl.LOCK_SH | fcntl.LOCK_NB)
1185.65.29 by Robert Collins
Implement final review suggestions.
207
            except IOError, e:
208
                # we should be more precise about whats a locking
209
                # error and whats a random-other error
4100.1.4 by Martin Pool
LockContention on OS locks now includes the filename
210
                raise errors.LockContention(self.filename, e)
615 by Martin Pool
Major rework of locking code:
211
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
212
        def unlock(self):
2353.4.1 by John Arbash Meinel
(broken) Change fcntl locks to be properly exclusive within the same process.
213
            count = _fcntl_ReadLock._open_locks[self.filename]
214
            if count == 1:
215
                del _fcntl_ReadLock._open_locks[self.filename]
216
            else:
217
                _fcntl_ReadLock._open_locks[self.filename] = count - 1
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
218
            self._unlock()
219
2353.4.3 by John Arbash Meinel
Implement a 'ReadLock.temporary_write_lock()' to upgrade to a write-lock in-process.
220
        def temporary_write_lock(self):
221
            """Try to grab a write lock on the file.
222
223
            On platforms that support it, this will upgrade to a write lock
224
            without unlocking the file.
225
            Otherwise, this will release the read lock, and try to acquire a
226
            write lock.
227
228
            :return: A token which can be used to switch back to a read lock.
229
            """
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
230
            if self.filename in _fcntl_WriteLock._open_locks:
231
                raise AssertionError('file already locked: %r'
232
                    % (self.filename,))
2353.4.7 by John Arbash Meinel
Change the temporary_write_lock api, so that it always returns a lock object,
233
            try:
234
                wlock = _fcntl_TemporaryWriteLock(self)
235
            except errors.LockError:
236
                # We didn't unlock, so we can just return 'self'
237
                return False, self
238
            return True, wlock
2353.4.3 by John Arbash Meinel
Implement a 'ReadLock.temporary_write_lock()' to upgrade to a write-lock in-process.
239
240
2555.3.2 by Martin Pool
rename _base_Lock to _OSLock
241
    class _fcntl_TemporaryWriteLock(_OSLock):
2353.4.3 by John Arbash Meinel
Implement a 'ReadLock.temporary_write_lock()' to upgrade to a write-lock in-process.
242
        """A token used when grabbing a temporary_write_lock.
243
244
        Call restore_read_lock() when you are done with the write lock.
245
        """
246
247
        def __init__(self, read_lock):
248
            super(_fcntl_TemporaryWriteLock, self).__init__()
249
            self._read_lock = read_lock
250
            self.filename = read_lock.filename
251
252
            count = _fcntl_ReadLock._open_locks[self.filename]
253
            if count > 1:
254
                # Something else also has a read-lock, so we cannot grab a
255
                # write lock.
256
                raise errors.LockContention(self.filename)
257
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
258
            if self.filename in _fcntl_WriteLock._open_locks:
259
                raise AssertionError('file already locked: %r'
260
                    % (self.filename,))
2353.4.3 by John Arbash Meinel
Implement a 'ReadLock.temporary_write_lock()' to upgrade to a write-lock in-process.
261
262
            # See if we can open the file for writing. Another process might
263
            # have a read lock. We don't use self._open() because we don't want
264
            # to create the file if it exists. That would have already been
265
            # done by _fcntl_ReadLock
266
            try:
267
                new_f = open(self.filename, 'rb+')
268
            except IOError, e:
269
                if e.errno in (errno.EACCES, errno.EPERM):
2872.5.1 by Martin Pool
Avoid internal error tracebacks on failure to lock on readonly transport (#129701).
270
                    raise errors.LockFailed(self.filename, str(e))
2353.4.3 by John Arbash Meinel
Implement a 'ReadLock.temporary_write_lock()' to upgrade to a write-lock in-process.
271
                raise
272
            try:
273
                # LOCK_NB will cause IOError to be raised if we can't grab a
274
                # lock right away.
2379.3.1 by John Arbash Meinel
Cherry-pick the 2 locking fixes from the 0.15 branch.
275
                fcntl.lockf(new_f, fcntl.LOCK_EX | fcntl.LOCK_NB)
2353.4.3 by John Arbash Meinel
Implement a 'ReadLock.temporary_write_lock()' to upgrade to a write-lock in-process.
276
            except IOError, e:
277
                # TODO: Raise a more specific error based on the type of error
4100.1.4 by Martin Pool
LockContention on OS locks now includes the filename
278
                raise errors.LockContention(self.filename, e)
2353.4.3 by John Arbash Meinel
Implement a 'ReadLock.temporary_write_lock()' to upgrade to a write-lock in-process.
279
            _fcntl_WriteLock._open_locks.add(self.filename)
280
281
            self.f = new_f
282
283
        def restore_read_lock(self):
2353.4.4 by John Arbash Meinel
Implement temporary_write_lock, and restore_read_lock for win32 locks.
284
            """Restore the original ReadLock."""
285
            # For fcntl, since we never released the read lock, just release the
286
            # write lock, and return the original lock.
2353.4.3 by John Arbash Meinel
Implement a 'ReadLock.temporary_write_lock()' to upgrade to a write-lock in-process.
287
            fcntl.lockf(self.f, fcntl.LOCK_UN)
288
            self._clear_f()
289
            _fcntl_WriteLock._open_locks.remove(self.filename)
290
            # Avoid reference cycles
291
            read_lock = self._read_lock
292
            self._read_lock = None
293
            return read_lock
294
1852.4.3 by Robert Collins
Alter working tree lock and unlock tests to test via the interface rather than via unpublished internals.
295
2353.3.9 by John Arbash Meinel
Update the lock code and test code so that if more than one
296
    _lock_classes.append(('fcntl', _fcntl_WriteLock, _fcntl_ReadLock))
577 by Martin Pool
- merge portable lock module from John
297
2353.3.11 by John Arbash Meinel
Code cleanup
298
2353.3.12 by John Arbash Meinel
Don't create the alternative lock types unless we are on windows.
299
if have_pywin32 and sys.platform == 'win32':
2353.3.4 by John Arbash Meinel
Clean up the lock.py code to use less indenting, and conform to better coding practise.
300
    LOCK_SH = 0 # the default
301
    LOCK_EX = win32con.LOCKFILE_EXCLUSIVE_LOCK
302
    LOCK_NB = win32con.LOCKFILE_FAIL_IMMEDIATELY
303
304
2555.3.2 by Martin Pool
rename _base_Lock to _OSLock
305
    class _w32c_FileLock(_OSLock):
2353.3.4 by John Arbash Meinel
Clean up the lock.py code to use less indenting, and conform to better coding practise.
306
307
        def _lock(self, filename, openmode, lockmode):
2353.3.5 by John Arbash Meinel
Fall back on ctypes to get LockFileEx, because msvcrt.locking()
308
            self._open(filename, openmode)
2353.3.4 by John Arbash Meinel
Clean up the lock.py code to use less indenting, and conform to better coding practise.
309
2353.3.5 by John Arbash Meinel
Fall back on ctypes to get LockFileEx, because msvcrt.locking()
310
            self.hfile = msvcrt.get_osfhandle(self.f.fileno())
2353.3.4 by John Arbash Meinel
Clean up the lock.py code to use less indenting, and conform to better coding practise.
311
            overlapped = pywintypes.OVERLAPPED()
312
            try:
2353.3.5 by John Arbash Meinel
Fall back on ctypes to get LockFileEx, because msvcrt.locking()
313
                win32file.LockFileEx(self.hfile, lockmode, 0, 0x7fff0000,
314
                                     overlapped)
2353.3.4 by John Arbash Meinel
Clean up the lock.py code to use less indenting, and conform to better coding practise.
315
            except pywintypes.error, e:
316
                self._clear_f()
317
                if e.args[0] in (winerror.ERROR_LOCK_VIOLATION,):
318
                    raise errors.LockContention(filename)
319
                ## import pdb; pdb.set_trace()
320
                raise
321
            except Exception, e:
322
                self._clear_f()
4100.1.4 by Martin Pool
LockContention on OS locks now includes the filename
323
                raise errors.LockContention(filename, e)
2353.3.4 by John Arbash Meinel
Clean up the lock.py code to use less indenting, and conform to better coding practise.
324
325
        def unlock(self):
2353.3.5 by John Arbash Meinel
Fall back on ctypes to get LockFileEx, because msvcrt.locking()
326
            overlapped = pywintypes.OVERLAPPED()
2353.3.4 by John Arbash Meinel
Clean up the lock.py code to use less indenting, and conform to better coding practise.
327
            try:
328
                win32file.UnlockFileEx(self.hfile, 0, 0x7fff0000, overlapped)
329
                self._clear_f()
330
            except Exception, e:
4100.1.4 by Martin Pool
LockContention on OS locks now includes the filename
331
                raise errors.LockContention(self.filename, e)
2353.3.4 by John Arbash Meinel
Clean up the lock.py code to use less indenting, and conform to better coding practise.
332
333
334
    class _w32c_ReadLock(_w32c_FileLock):
335
        def __init__(self, filename):
336
            super(_w32c_ReadLock, self).__init__()
337
            self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
338
2353.4.4 by John Arbash Meinel
Implement temporary_write_lock, and restore_read_lock for win32 locks.
339
        def temporary_write_lock(self):
340
            """Try to grab a write lock on the file.
341
342
            On platforms that support it, this will upgrade to a write lock
343
            without unlocking the file.
344
            Otherwise, this will release the read lock, and try to acquire a
345
            write lock.
346
347
            :return: A token which can be used to switch back to a read lock.
348
            """
349
            # I can't find a way to upgrade a read lock to a write lock without
350
            # unlocking first. So here, we do just that.
351
            self.unlock()
2353.4.7 by John Arbash Meinel
Change the temporary_write_lock api, so that it always returns a lock object,
352
            try:
353
                wlock = _w32c_WriteLock(self.filename)
354
            except errors.LockError:
355
                return False, _w32c_ReadLock(self.filename)
356
            return True, wlock
2353.4.4 by John Arbash Meinel
Implement temporary_write_lock, and restore_read_lock for win32 locks.
357
2353.3.11 by John Arbash Meinel
Code cleanup
358
2353.3.4 by John Arbash Meinel
Clean up the lock.py code to use less indenting, and conform to better coding practise.
359
    class _w32c_WriteLock(_w32c_FileLock):
360
        def __init__(self, filename):
361
            super(_w32c_WriteLock, self).__init__()
362
            self._lock(filename, 'rb+', LOCK_EX + LOCK_NB)
363
2353.4.4 by John Arbash Meinel
Implement temporary_write_lock, and restore_read_lock for win32 locks.
364
        def restore_read_lock(self):
365
            """Restore the original ReadLock."""
366
            # For win32 we had to completely let go of the original lock, so we
367
            # just unlock and create a new read lock.
368
            self.unlock()
369
            return _w32c_ReadLock(self.filename)
370
2353.3.11 by John Arbash Meinel
Code cleanup
371
2353.3.9 by John Arbash Meinel
Update the lock code and test code so that if more than one
372
    _lock_classes.append(('pywin32', _w32c_WriteLock, _w32c_ReadLock))
373
2353.3.11 by John Arbash Meinel
Code cleanup
374
3224.5.1 by Andrew Bennetts
Lots of assorted hackery to reduce the number of imports for common operations. Improves 'rocks', 'st' and 'help' times by ~50ms on my laptop.
375
if have_ctypes_win32:
2353.3.5 by John Arbash Meinel
Fall back on ctypes to get LockFileEx, because msvcrt.locking()
376
    # These constants were copied from the win32con.py module.
377
    LOCKFILE_FAIL_IMMEDIATELY = 1
378
    LOCKFILE_EXCLUSIVE_LOCK = 2
379
    # Constant taken from winerror.py module
380
    ERROR_LOCK_VIOLATION = 33
381
382
    LOCK_SH = 0
383
    LOCK_EX = LOCKFILE_EXCLUSIVE_LOCK
384
    LOCK_NB = LOCKFILE_FAIL_IMMEDIATELY
385
    _LockFileEx = ctypes.windll.kernel32.LockFileEx
386
    _UnlockFileEx = ctypes.windll.kernel32.UnlockFileEx
387
    _GetLastError = ctypes.windll.kernel32.GetLastError
388
389
    ### Define the OVERLAPPED structure.
390
    #   http://msdn2.microsoft.com/en-us/library/ms684342.aspx
391
    # typedef struct _OVERLAPPED {
392
    #   ULONG_PTR Internal;
393
    #   ULONG_PTR InternalHigh;
394
    #   union {
395
    #     struct {
396
    #       DWORD Offset;
397
    #       DWORD OffsetHigh;
398
    #     };
399
    #     PVOID Pointer;
400
    #   };
401
    #   HANDLE hEvent;
2353.3.9 by John Arbash Meinel
Update the lock code and test code so that if more than one
402
    # } OVERLAPPED,
2353.3.5 by John Arbash Meinel
Fall back on ctypes to get LockFileEx, because msvcrt.locking()
403
404
    class _inner_struct(ctypes.Structure):
405
        _fields_ = [('Offset', ctypes.c_uint), # DWORD
406
                    ('OffsetHigh', ctypes.c_uint), # DWORD
407
                   ]
408
409
    class _inner_union(ctypes.Union):
410
        _fields_  = [('anon_struct', _inner_struct), # struct
411
                     ('Pointer', ctypes.c_void_p), # PVOID
412
                    ]
413
414
    class OVERLAPPED(ctypes.Structure):
415
        _fields_ = [('Internal', ctypes.c_void_p), # ULONG_PTR
416
                    ('InternalHigh', ctypes.c_void_p), # ULONG_PTR
417
                    ('_inner_union', _inner_union),
418
                    ('hEvent', ctypes.c_void_p), # HANDLE
419
                   ]
420
2555.3.2 by Martin Pool
rename _base_Lock to _OSLock
421
    class _ctypes_FileLock(_OSLock):
2353.3.5 by John Arbash Meinel
Fall back on ctypes to get LockFileEx, because msvcrt.locking()
422
423
        def _lock(self, filename, openmode, lockmode):
424
            self._open(filename, openmode)
425
426
            self.hfile = msvcrt.get_osfhandle(self.f.fileno())
427
            overlapped = OVERLAPPED()
428
            result = _LockFileEx(self.hfile, # HANDLE hFile
429
                                 lockmode,   # DWORD dwFlags
430
                                 0,          # DWORD dwReserved
431
                                 0x7fffffff, # DWORD nNumberOfBytesToLockLow
432
                                 0x00000000, # DWORD nNumberOfBytesToLockHigh
2353.4.9 by John Arbash Meinel
[merge] bzr.dev 2359
433
                                 ctypes.byref(overlapped), # lpOverlapped
2353.3.5 by John Arbash Meinel
Fall back on ctypes to get LockFileEx, because msvcrt.locking()
434
                                )
435
            if result == 0:
436
                self._clear_f()
437
                last_err = _GetLastError()
438
                if last_err in (ERROR_LOCK_VIOLATION,):
439
                    raise errors.LockContention(filename)
4100.1.4 by Martin Pool
LockContention on OS locks now includes the filename
440
                raise errors.LockContention(filename,
441
                    'Unknown locking error: %s' % (last_err,))
2353.3.5 by John Arbash Meinel
Fall back on ctypes to get LockFileEx, because msvcrt.locking()
442
443
        def unlock(self):
444
            overlapped = OVERLAPPED()
445
            result = _UnlockFileEx(self.hfile, # HANDLE hFile
446
                                   0,          # DWORD dwReserved
447
                                   0x7fffffff, # DWORD nNumberOfBytesToLockLow
448
                                   0x00000000, # DWORD nNumberOfBytesToLockHigh
2353.4.9 by John Arbash Meinel
[merge] bzr.dev 2359
449
                                   ctypes.byref(overlapped), # lpOverlapped
2353.3.5 by John Arbash Meinel
Fall back on ctypes to get LockFileEx, because msvcrt.locking()
450
                                  )
451
            self._clear_f()
452
            if result == 0:
453
                self._clear_f()
454
                last_err = _GetLastError()
4100.1.4 by Martin Pool
LockContention on OS locks now includes the filename
455
                raise errors.LockContention(self.filename,
456
                    'Unknown unlocking error: %s' % (last_err,))
2353.3.5 by John Arbash Meinel
Fall back on ctypes to get LockFileEx, because msvcrt.locking()
457
458
459
    class _ctypes_ReadLock(_ctypes_FileLock):
460
        def __init__(self, filename):
461
            super(_ctypes_ReadLock, self).__init__()
462
            self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
463
2353.4.4 by John Arbash Meinel
Implement temporary_write_lock, and restore_read_lock for win32 locks.
464
        def temporary_write_lock(self):
465
            """Try to grab a write lock on the file.
466
467
            On platforms that support it, this will upgrade to a write lock
468
            without unlocking the file.
469
            Otherwise, this will release the read lock, and try to acquire a
470
            write lock.
471
472
            :return: A token which can be used to switch back to a read lock.
473
            """
474
            # I can't find a way to upgrade a read lock to a write lock without
475
            # unlocking first. So here, we do just that.
476
            self.unlock()
2353.4.7 by John Arbash Meinel
Change the temporary_write_lock api, so that it always returns a lock object,
477
            try:
478
                wlock = _ctypes_WriteLock(self.filename)
479
            except errors.LockError:
480
                return False, _ctypes_ReadLock(self.filename)
481
            return True, wlock
2353.3.11 by John Arbash Meinel
Code cleanup
482
2353.3.5 by John Arbash Meinel
Fall back on ctypes to get LockFileEx, because msvcrt.locking()
483
    class _ctypes_WriteLock(_ctypes_FileLock):
484
        def __init__(self, filename):
485
            super(_ctypes_WriteLock, self).__init__()
486
            self._lock(filename, 'rb+', LOCK_EX + LOCK_NB)
487
2353.4.4 by John Arbash Meinel
Implement temporary_write_lock, and restore_read_lock for win32 locks.
488
        def restore_read_lock(self):
489
            """Restore the original ReadLock."""
490
            # For win32 we had to completely let go of the original lock, so we
491
            # just unlock and create a new read lock.
492
            self.unlock()
2353.4.6 by John Arbash Meinel
ctypes locks should return ctypes locks.
493
            return _ctypes_ReadLock(self.filename)
2353.4.4 by John Arbash Meinel
Implement temporary_write_lock, and restore_read_lock for win32 locks.
494
2353.3.11 by John Arbash Meinel
Code cleanup
495
2353.3.9 by John Arbash Meinel
Update the lock code and test code so that if more than one
496
    _lock_classes.append(('ctypes', _ctypes_WriteLock, _ctypes_ReadLock))
497
498
499
if len(_lock_classes) == 0:
2353.3.11 by John Arbash Meinel
Code cleanup
500
    raise NotImplementedError(
501
        "We must have one of fcntl, pywin32, or ctypes available"
502
        " to support OS locking."
503
        )
504
2353.3.9 by John Arbash Meinel
Update the lock code and test code so that if more than one
505
506
# We default to using the first available lock class.
507
_lock_type, WriteLock, ReadLock = _lock_classes[0]
508