~bzr-pqm/bzr/bzr.dev

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