/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/lock.py

  • Committer: Richard Wilbur
  • Date: 2016-02-04 19:07:28 UTC
  • mto: This revision was merged to the branch mainline in revision 6618.
  • Revision ID: richard.wilbur@gmail.com-20160204190728-p0zvfii6zase0fw7
Update COPYING.txt from the original http://www.gnu.org/licenses/gpl-2.0.txt  (Only differences were in whitespace.)  Thanks to Petr Stodulka for pointing out the discrepancy.

Show diffs side-by-side

added added

removed removed

Lines of Context:
33
33
unlock() method.
34
34
"""
35
35
 
 
36
from __future__ import absolute_import
 
37
 
36
38
import contextlib
37
39
import errno
38
40
import os
39
41
import sys
40
42
import warnings
41
43
 
42
 
from . import (
 
44
from bzrlib import (
43
45
    debug,
44
46
    errors,
45
47
    osutils,
46
48
    trace,
47
49
    )
48
 
from .hooks import Hooks
49
 
from .i18n import gettext
50
 
 
 
50
from bzrlib.hooks import Hooks
 
51
from bzrlib.i18n import gettext
51
52
 
52
53
class LockHooks(Hooks):
53
54
 
54
55
    def __init__(self):
55
 
        Hooks.__init__(self, "breezy.lock", "Lock.hooks")
56
 
        self.add_hook(
57
 
            'lock_acquired',
58
 
            "Called with a breezy.lock.LockResult when a physical lock is "
 
56
        Hooks.__init__(self, "bzrlib.lock", "Lock.hooks")
 
57
        self.add_hook('lock_acquired',
 
58
            "Called with a bzrlib.lock.LockResult when a physical lock is "
59
59
            "acquired.", (1, 8))
60
 
        self.add_hook(
61
 
            'lock_released',
62
 
            "Called with a breezy.lock.LockResult when a physical lock is "
 
60
        self.add_hook('lock_released',
 
61
            "Called with a bzrlib.lock.LockResult when a physical lock is "
63
62
            "released.", (1, 8))
64
 
        self.add_hook(
65
 
            'lock_broken',
66
 
            "Called with a breezy.lock.LockResult when a physical lock is "
 
63
        self.add_hook('lock_broken',
 
64
            "Called with a bzrlib.lock.LockResult when a physical lock is "
67
65
            "broken.", (1, 15))
68
66
 
69
67
 
89
87
 
90
88
    def __repr__(self):
91
89
        return '%s(%s, %s)' % (self.__class__.__name__,
92
 
                               self.lock_url, self.details)
 
90
                             self.lock_url, self.details)
93
91
 
94
92
 
95
93
class LogicalLockResult(object):
98
96
    :ivar unlock: A callable which will unlock the lock.
99
97
    """
100
98
 
101
 
    def __init__(self, unlock, token=None):
 
99
    def __init__(self, unlock):
102
100
        self.unlock = unlock
103
 
        self.token = token
104
101
 
105
102
    def __repr__(self):
106
103
        return "LogicalLockResult(%s)" % (self.unlock)
107
104
 
108
 
    def __enter__(self):
109
 
        return self
110
 
 
111
 
    def __exit__(self, exc_type, exc_val, exc_tb):
112
 
        # If there was an error raised, prefer the original one
113
 
        try:
114
 
            self.unlock()
115
 
        except BaseException:
116
 
            if exc_type is None:
117
 
                raise
118
 
        return False
119
105
 
120
106
 
121
107
def cant_unlock_not_held(locked_object):
122
108
    """An attempt to unlock failed because the object was not locked.
123
109
 
124
 
    This provides a policy point from which we can generate either a warning or
125
 
    an exception.
 
110
    This provides a policy point from which we can generate either a warning 
 
111
    or an exception.
126
112
    """
127
113
    # This is typically masking some other error and called from a finally
128
114
    # block, so it's useful to have the option not to generate a new error
130
116
    # raise LockNotHeld.
131
117
    if 'unlock' in debug.debug_flags:
132
118
        warnings.warn("%r is already unlocked" % (locked_object,),
133
 
                      stacklevel=3)
 
119
            stacklevel=3)
134
120
    else:
135
121
        raise errors.LockNotHeld(locked_object)
136
122
 
141
127
except ImportError:
142
128
    have_fcntl = False
143
129
 
 
130
have_pywin32 = False
144
131
have_ctypes_win32 = False
145
132
if sys.platform == 'win32':
146
133
    import msvcrt
147
134
    try:
 
135
        import win32file, pywintypes, winerror
 
136
        have_pywin32 = True
 
137
    except ImportError:
 
138
        pass
 
139
 
 
140
    try:
148
141
        import ctypes
149
142
        have_ctypes_win32 = True
150
143
    except ImportError:
162
155
        try:
163
156
            self.f = open(self.filename, filemode)
164
157
            return self.f
165
 
        except IOError as e:
 
158
        except IOError, e:
166
159
            if e.errno in (errno.EACCES, errno.EPERM):
167
160
                raise errors.LockFailed(self.filename, str(e))
168
161
            if e.errno != errno.ENOENT:
195
188
            fcntl.lockf(self.f, fcntl.LOCK_UN)
196
189
            self._clear_f()
197
190
 
 
191
 
198
192
    class _fcntl_WriteLock(_fcntl_FileLock):
199
193
 
200
194
        _open_locks = set()
223
217
                # LOCK_NB will cause IOError to be raised if we can't grab a
224
218
                # lock right away.
225
219
                fcntl.lockf(self.f, fcntl.LOCK_EX | fcntl.LOCK_NB)
226
 
            except IOError as e:
 
220
            except IOError, e:
227
221
                if e.errno in (errno.EAGAIN, errno.EACCES):
228
222
                    # We couldn't grab the lock
229
223
                    self.unlock()
235
229
            _fcntl_WriteLock._open_locks.remove(self.filename)
236
230
            self._unlock()
237
231
 
 
232
 
238
233
    class _fcntl_ReadLock(_fcntl_FileLock):
239
234
 
240
235
        _open_locks = {}
257
252
                # LOCK_NB will cause IOError to be raised if we can't grab a
258
253
                # lock right away.
259
254
                fcntl.lockf(self.f, fcntl.LOCK_SH | fcntl.LOCK_NB)
260
 
            except IOError as e:
 
255
            except IOError, e:
261
256
                # we should be more precise about whats a locking
262
257
                # error and whats a random-other error
263
258
                raise errors.LockContention(self.filename, e)
282
277
            """
283
278
            if self.filename in _fcntl_WriteLock._open_locks:
284
279
                raise AssertionError('file already locked: %r'
285
 
                                     % (self.filename,))
 
280
                    % (self.filename,))
286
281
            try:
287
282
                wlock = _fcntl_TemporaryWriteLock(self)
288
283
            except errors.LockError:
290
285
                return False, self
291
286
            return True, wlock
292
287
 
 
288
 
293
289
    class _fcntl_TemporaryWriteLock(_OSLock):
294
290
        """A token used when grabbing a temporary_write_lock.
295
291
 
309
305
 
310
306
            if self.filename in _fcntl_WriteLock._open_locks:
311
307
                raise AssertionError('file already locked: %r'
312
 
                                     % (self.filename,))
 
308
                    % (self.filename,))
313
309
 
314
310
            # See if we can open the file for writing. Another process might
315
311
            # have a read lock. We don't use self._open() because we don't want
317
313
            # done by _fcntl_ReadLock
318
314
            try:
319
315
                new_f = open(self.filename, 'rb+')
320
 
            except IOError as e:
 
316
            except IOError, e:
321
317
                if e.errno in (errno.EACCES, errno.EPERM):
322
318
                    raise errors.LockFailed(self.filename, str(e))
323
319
                raise
325
321
                # LOCK_NB will cause IOError to be raised if we can't grab a
326
322
                # lock right away.
327
323
                fcntl.lockf(new_f, fcntl.LOCK_EX | fcntl.LOCK_NB)
328
 
            except IOError as e:
 
324
            except IOError, e:
329
325
                # TODO: Raise a more specific error based on the type of error
330
326
                raise errors.LockContention(self.filename, e)
331
327
            _fcntl_WriteLock._open_locks.add(self.filename)
334
330
 
335
331
        def restore_read_lock(self):
336
332
            """Restore the original ReadLock."""
337
 
            # For fcntl, since we never released the read lock, just release
338
 
            # the write lock, and return the original lock.
 
333
            # For fcntl, since we never released the read lock, just release the
 
334
            # write lock, and return the original lock.
339
335
            fcntl.lockf(self.f, fcntl.LOCK_UN)
340
336
            self._clear_f()
341
337
            _fcntl_WriteLock._open_locks.remove(self.filename)
344
340
            self._read_lock = None
345
341
            return read_lock
346
342
 
 
343
 
347
344
    _lock_classes.append(('fcntl', _fcntl_WriteLock, _fcntl_ReadLock))
348
345
 
349
346
 
 
347
if have_pywin32 and sys.platform == 'win32':
 
348
    if os.path.supports_unicode_filenames:
 
349
        # for Windows NT/2K/XP/etc
 
350
        win32file_CreateFile = win32file.CreateFileW
 
351
    else:
 
352
        # for Windows 98
 
353
        win32file_CreateFile = win32file.CreateFile
 
354
 
 
355
    class _w32c_FileLock(_OSLock):
 
356
 
 
357
        def _open(self, filename, access, share, cflags, pymode):
 
358
            self.filename = osutils.realpath(filename)
 
359
            try:
 
360
                self._handle = win32file_CreateFile(filename, access, share,
 
361
                    None, win32file.OPEN_ALWAYS,
 
362
                    win32file.FILE_ATTRIBUTE_NORMAL, None)
 
363
            except pywintypes.error, e:
 
364
                if e.args[0] == winerror.ERROR_ACCESS_DENIED:
 
365
                    raise errors.LockFailed(filename, e)
 
366
                if e.args[0] == winerror.ERROR_SHARING_VIOLATION:
 
367
                    raise errors.LockContention(filename, e)
 
368
                raise
 
369
            fd = win32file._open_osfhandle(self._handle, cflags)
 
370
            self.f = os.fdopen(fd, pymode)
 
371
            return self.f
 
372
 
 
373
        def unlock(self):
 
374
            self._clear_f()
 
375
            self._handle = None
 
376
 
 
377
 
 
378
    class _w32c_ReadLock(_w32c_FileLock):
 
379
        def __init__(self, filename):
 
380
            super(_w32c_ReadLock, self).__init__()
 
381
            self._open(filename, win32file.GENERIC_READ,
 
382
                win32file.FILE_SHARE_READ, os.O_RDONLY, "rb")
 
383
 
 
384
        def temporary_write_lock(self):
 
385
            """Try to grab a write lock on the file.
 
386
 
 
387
            On platforms that support it, this will upgrade to a write lock
 
388
            without unlocking the file.
 
389
            Otherwise, this will release the read lock, and try to acquire a
 
390
            write lock.
 
391
 
 
392
            :return: A token which can be used to switch back to a read lock.
 
393
            """
 
394
            # I can't find a way to upgrade a read lock to a write lock without
 
395
            # unlocking first. So here, we do just that.
 
396
            self.unlock()
 
397
            try:
 
398
                wlock = _w32c_WriteLock(self.filename)
 
399
            except errors.LockError:
 
400
                return False, _w32c_ReadLock(self.filename)
 
401
            return True, wlock
 
402
 
 
403
 
 
404
    class _w32c_WriteLock(_w32c_FileLock):
 
405
        def __init__(self, filename):
 
406
            super(_w32c_WriteLock, self).__init__()
 
407
            self._open(filename,
 
408
                win32file.GENERIC_READ | win32file.GENERIC_WRITE, 0,
 
409
                os.O_RDWR, "rb+")
 
410
 
 
411
        def restore_read_lock(self):
 
412
            """Restore the original ReadLock."""
 
413
            # For win32 we had to completely let go of the original lock, so we
 
414
            # just unlock and create a new read lock.
 
415
            self.unlock()
 
416
            return _w32c_ReadLock(self.filename)
 
417
 
 
418
 
 
419
    _lock_classes.append(('pywin32', _w32c_WriteLock, _w32c_ReadLock))
 
420
 
 
421
 
350
422
if have_ctypes_win32:
351
 
    from ctypes.wintypes import DWORD, LPWSTR
352
 
    LPSECURITY_ATTRIBUTES = ctypes.c_void_p  # used as NULL no need to declare
353
 
    HANDLE = ctypes.c_int  # rather than unsigned as in ctypes.wintypes
354
 
    _function_name = "CreateFileW"
 
423
    from ctypes.wintypes import DWORD, LPCSTR, LPCWSTR
 
424
    LPSECURITY_ATTRIBUTES = ctypes.c_void_p # used as NULL no need to declare
 
425
    HANDLE = ctypes.c_int # rather than unsigned as in ctypes.wintypes
 
426
    if os.path.supports_unicode_filenames:
 
427
        _function_name = "CreateFileW"
 
428
        LPTSTR = LPCWSTR
 
429
    else:
 
430
        _function_name = "CreateFileA"
 
431
        class LPTSTR(LPCSTR):
 
432
            def __new__(cls, obj):
 
433
                return LPCSTR.__new__(cls, obj.encode("mbcs"))
355
434
 
356
435
    # CreateFile <http://msdn.microsoft.com/en-us/library/aa363858.aspx>
357
436
    _CreateFile = ctypes.WINFUNCTYPE(
358
 
        HANDLE,                # return value
359
 
        LPWSTR,                # lpFileName
360
 
        DWORD,                 # dwDesiredAccess
361
 
        DWORD,                 # dwShareMode
362
 
        LPSECURITY_ATTRIBUTES,  # lpSecurityAttributes
363
 
        DWORD,                 # dwCreationDisposition
364
 
        DWORD,                 # dwFlagsAndAttributes
365
 
        HANDLE                 # hTemplateFile
 
437
            HANDLE,                # return value
 
438
            LPTSTR,                # lpFileName
 
439
            DWORD,                 # dwDesiredAccess
 
440
            DWORD,                 # dwShareMode
 
441
            LPSECURITY_ATTRIBUTES, # lpSecurityAttributes
 
442
            DWORD,                 # dwCreationDisposition
 
443
            DWORD,                 # dwFlagsAndAttributes
 
444
            HANDLE                 # hTemplateFile
366
445
        )((_function_name, ctypes.windll.kernel32))
367
446
 
368
447
    INVALID_HANDLE_VALUE = -1
381
460
        def _open(self, filename, access, share, cflags, pymode):
382
461
            self.filename = osutils.realpath(filename)
383
462
            handle = _CreateFile(filename, access, share, None, OPEN_ALWAYS,
384
 
                                 FILE_ATTRIBUTE_NORMAL, 0)
 
463
                FILE_ATTRIBUTE_NORMAL, 0)
385
464
            if handle in (INVALID_HANDLE_VALUE, 0):
386
465
                e = ctypes.WinError()
387
466
                if e.args[0] == ERROR_ACCESS_DENIED:
396
475
        def unlock(self):
397
476
            self._clear_f()
398
477
 
 
478
 
399
479
    class _ctypes_ReadLock(_ctypes_FileLock):
400
480
        def __init__(self, filename):
401
481
            super(_ctypes_ReadLock, self).__init__()
402
482
            self._open(filename, GENERIC_READ, FILE_SHARE_READ, os.O_RDONLY,
403
 
                       "rb")
 
483
                "rb")
404
484
 
405
485
        def temporary_write_lock(self):
406
486
            """Try to grab a write lock on the file.
425
505
        def __init__(self, filename):
426
506
            super(_ctypes_WriteLock, self).__init__()
427
507
            self._open(filename, GENERIC_READ | GENERIC_WRITE, 0, os.O_RDWR,
428
 
                       "rb+")
 
508
                "rb+")
429
509
 
430
510
        def restore_read_lock(self):
431
511
            """Restore the original ReadLock."""
434
514
            self.unlock()
435
515
            return _ctypes_ReadLock(self.filename)
436
516
 
 
517
 
437
518
    _lock_classes.append(('ctypes', _ctypes_WriteLock, _ctypes_ReadLock))
438
519
 
439
520
 
440
521
if len(_lock_classes) == 0:
441
522
    raise NotImplementedError(
442
 
        "We must have one of fcntl or ctypes available"
 
523
        "We must have one of fcntl, pywin32, or ctypes available"
443
524
        " to support OS locking."
444
525
        )
445
526
 
468
549
            trace.note(gettext('{0!r} was {1} locked again'), self, type_name)
469
550
        self._prev_lock = lock_type
470
551
 
471
 
 
472
552
@contextlib.contextmanager
473
553
def write_locked(lockable):
474
554
    lockable.lock_write()