/brz/remove-bazaar

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