/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 breezy/lock.py

  • Committer: Jelmer Vernooij
  • Date: 2020-05-06 02:13:25 UTC
  • mfrom: (7490.7.21 work)
  • mto: This revision was merged to the branch mainline in revision 7501.
  • Revision ID: jelmer@jelmer.uk-20200506021325-awbmmqu1zyorz7sj
Merge 3.1 branch.

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
 
 
38
36
import contextlib
39
37
import errno
40
38
import os
50
48
from .hooks import Hooks
51
49
from .i18n import gettext
52
50
 
 
51
 
53
52
class LockHooks(Hooks):
54
53
 
55
54
    def __init__(self):
56
55
        Hooks.__init__(self, "breezy.lock", "Lock.hooks")
57
 
        self.add_hook('lock_acquired',
 
56
        self.add_hook(
 
57
            'lock_acquired',
58
58
            "Called with a breezy.lock.LockResult when a physical lock is "
59
59
            "acquired.", (1, 8))
60
 
        self.add_hook('lock_released',
 
60
        self.add_hook(
 
61
            'lock_released',
61
62
            "Called with a breezy.lock.LockResult when a physical lock is "
62
63
            "released.", (1, 8))
63
 
        self.add_hook('lock_broken',
 
64
        self.add_hook(
 
65
            'lock_broken',
64
66
            "Called with a breezy.lock.LockResult when a physical lock is "
65
67
            "broken.", (1, 15))
66
68
 
87
89
 
88
90
    def __repr__(self):
89
91
        return '%s(%s, %s)' % (self.__class__.__name__,
90
 
                             self.lock_url, self.details)
 
92
                               self.lock_url, self.details)
91
93
 
92
94
 
93
95
class LogicalLockResult(object):
110
112
        # If there was an error raised, prefer the original one
111
113
        try:
112
114
            self.unlock()
113
 
        except:
 
115
        except BaseException:
114
116
            if exc_type is None:
115
117
                raise
116
118
        return False
119
121
def cant_unlock_not_held(locked_object):
120
122
    """An attempt to unlock failed because the object was not locked.
121
123
 
122
 
    This provides a policy point from which we can generate either a warning 
123
 
    or an exception.
 
124
    This provides a policy point from which we can generate either a warning or
 
125
    an exception.
124
126
    """
125
127
    # This is typically masking some other error and called from a finally
126
128
    # block, so it's useful to have the option not to generate a new error
128
130
    # raise LockNotHeld.
129
131
    if 'unlock' in debug.debug_flags:
130
132
        warnings.warn("%r is already unlocked" % (locked_object,),
131
 
            stacklevel=3)
 
133
                      stacklevel=3)
132
134
    else:
133
135
        raise errors.LockNotHeld(locked_object)
134
136
 
139
141
except ImportError:
140
142
    have_fcntl = False
141
143
 
142
 
have_pywin32 = False
143
144
have_ctypes_win32 = False
144
145
if sys.platform == 'win32':
145
146
    import msvcrt
146
147
    try:
147
 
        import win32file, pywintypes, winerror
148
 
        have_pywin32 = True
149
 
    except ImportError:
150
 
        pass
151
 
 
152
 
    try:
153
148
        import ctypes
154
149
        have_ctypes_win32 = True
155
150
    except ImportError:
200
195
            fcntl.lockf(self.f, fcntl.LOCK_UN)
201
196
            self._clear_f()
202
197
 
203
 
 
204
198
    class _fcntl_WriteLock(_fcntl_FileLock):
205
199
 
206
200
        _open_locks = set()
241
235
            _fcntl_WriteLock._open_locks.remove(self.filename)
242
236
            self._unlock()
243
237
 
244
 
 
245
238
    class _fcntl_ReadLock(_fcntl_FileLock):
246
239
 
247
240
        _open_locks = {}
289
282
            """
290
283
            if self.filename in _fcntl_WriteLock._open_locks:
291
284
                raise AssertionError('file already locked: %r'
292
 
                    % (self.filename,))
 
285
                                     % (self.filename,))
293
286
            try:
294
287
                wlock = _fcntl_TemporaryWriteLock(self)
295
288
            except errors.LockError:
297
290
                return False, self
298
291
            return True, wlock
299
292
 
300
 
 
301
293
    class _fcntl_TemporaryWriteLock(_OSLock):
302
294
        """A token used when grabbing a temporary_write_lock.
303
295
 
317
309
 
318
310
            if self.filename in _fcntl_WriteLock._open_locks:
319
311
                raise AssertionError('file already locked: %r'
320
 
                    % (self.filename,))
 
312
                                     % (self.filename,))
321
313
 
322
314
            # See if we can open the file for writing. Another process might
323
315
            # have a read lock. We don't use self._open() because we don't want
342
334
 
343
335
        def restore_read_lock(self):
344
336
            """Restore the original ReadLock."""
345
 
            # For fcntl, since we never released the read lock, just release the
346
 
            # write lock, and return the original lock.
 
337
            # For fcntl, since we never released the read lock, just release
 
338
            # the write lock, and return the original lock.
347
339
            fcntl.lockf(self.f, fcntl.LOCK_UN)
348
340
            self._clear_f()
349
341
            _fcntl_WriteLock._open_locks.remove(self.filename)
352
344
            self._read_lock = None
353
345
            return read_lock
354
346
 
355
 
 
356
347
    _lock_classes.append(('fcntl', _fcntl_WriteLock, _fcntl_ReadLock))
357
348
 
358
349
 
359
 
if have_pywin32 and sys.platform == 'win32':
360
 
    win32file_CreateFile = win32file.CreateFileW
361
 
 
362
 
    class _w32c_FileLock(_OSLock):
363
 
 
364
 
        def _open(self, filename, access, share, cflags, pymode):
365
 
            self.filename = osutils.realpath(filename)
366
 
            try:
367
 
                self._handle = win32file_CreateFile(filename, access, share,
368
 
                    None, win32file.OPEN_ALWAYS,
369
 
                    win32file.FILE_ATTRIBUTE_NORMAL, None)
370
 
            except pywintypes.error as e:
371
 
                if e.args[0] == winerror.ERROR_ACCESS_DENIED:
372
 
                    raise errors.LockFailed(filename, e)
373
 
                if e.args[0] == winerror.ERROR_SHARING_VIOLATION:
374
 
                    raise errors.LockContention(filename, e)
375
 
                raise
376
 
            fd = win32file._open_osfhandle(self._handle, cflags)
377
 
            self.f = os.fdopen(fd, pymode)
378
 
            return self.f
379
 
 
380
 
        def unlock(self):
381
 
            self._clear_f()
382
 
            self._handle = None
383
 
 
384
 
 
385
 
    class _w32c_ReadLock(_w32c_FileLock):
386
 
        def __init__(self, filename):
387
 
            super(_w32c_ReadLock, self).__init__()
388
 
            self._open(filename, win32file.GENERIC_READ,
389
 
                win32file.FILE_SHARE_READ, os.O_RDONLY, "rb")
390
 
 
391
 
        def temporary_write_lock(self):
392
 
            """Try to grab a write lock on the file.
393
 
 
394
 
            On platforms that support it, this will upgrade to a write lock
395
 
            without unlocking the file.
396
 
            Otherwise, this will release the read lock, and try to acquire a
397
 
            write lock.
398
 
 
399
 
            :return: A token which can be used to switch back to a read lock.
400
 
            """
401
 
            # I can't find a way to upgrade a read lock to a write lock without
402
 
            # unlocking first. So here, we do just that.
403
 
            self.unlock()
404
 
            try:
405
 
                wlock = _w32c_WriteLock(self.filename)
406
 
            except errors.LockError:
407
 
                return False, _w32c_ReadLock(self.filename)
408
 
            return True, wlock
409
 
 
410
 
 
411
 
    class _w32c_WriteLock(_w32c_FileLock):
412
 
        def __init__(self, filename):
413
 
            super(_w32c_WriteLock, self).__init__()
414
 
            self._open(filename,
415
 
                win32file.GENERIC_READ | win32file.GENERIC_WRITE, 0,
416
 
                os.O_RDWR, "rb+")
417
 
 
418
 
        def restore_read_lock(self):
419
 
            """Restore the original ReadLock."""
420
 
            # For win32 we had to completely let go of the original lock, so we
421
 
            # just unlock and create a new read lock.
422
 
            self.unlock()
423
 
            return _w32c_ReadLock(self.filename)
424
 
 
425
 
 
426
 
    _lock_classes.append(('pywin32', _w32c_WriteLock, _w32c_ReadLock))
427
 
 
428
 
 
429
350
if have_ctypes_win32:
430
 
    from ctypes.wintypes import DWORD, LPCSTR, LPCWSTR
431
 
    LPSECURITY_ATTRIBUTES = ctypes.c_void_p # used as NULL no need to declare
432
 
    HANDLE = ctypes.c_int # rather than unsigned as in ctypes.wintypes
 
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
433
354
    _function_name = "CreateFileW"
434
355
 
435
356
    # CreateFile <http://msdn.microsoft.com/en-us/library/aa363858.aspx>
436
357
    _CreateFile = ctypes.WINFUNCTYPE(
437
 
            HANDLE,                # return value
438
 
            LPWSTR,                # lpFileName
439
 
            DWORD,                 # dwDesiredAccess
440
 
            DWORD,                 # dwShareMode
441
 
            LPSECURITY_ATTRIBUTES, # lpSecurityAttributes
442
 
            DWORD,                 # dwCreationDisposition
443
 
            DWORD,                 # dwFlagsAndAttributes
444
 
            HANDLE                 # hTemplateFile
 
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
445
366
        )((_function_name, ctypes.windll.kernel32))
446
367
 
447
368
    INVALID_HANDLE_VALUE = -1
460
381
        def _open(self, filename, access, share, cflags, pymode):
461
382
            self.filename = osutils.realpath(filename)
462
383
            handle = _CreateFile(filename, access, share, None, OPEN_ALWAYS,
463
 
                FILE_ATTRIBUTE_NORMAL, 0)
 
384
                                 FILE_ATTRIBUTE_NORMAL, 0)
464
385
            if handle in (INVALID_HANDLE_VALUE, 0):
465
386
                e = ctypes.WinError()
466
387
                if e.args[0] == ERROR_ACCESS_DENIED:
475
396
        def unlock(self):
476
397
            self._clear_f()
477
398
 
478
 
 
479
399
    class _ctypes_ReadLock(_ctypes_FileLock):
480
400
        def __init__(self, filename):
481
401
            super(_ctypes_ReadLock, self).__init__()
482
402
            self._open(filename, GENERIC_READ, FILE_SHARE_READ, os.O_RDONLY,
483
 
                "rb")
 
403
                       "rb")
484
404
 
485
405
        def temporary_write_lock(self):
486
406
            """Try to grab a write lock on the file.
505
425
        def __init__(self, filename):
506
426
            super(_ctypes_WriteLock, self).__init__()
507
427
            self._open(filename, GENERIC_READ | GENERIC_WRITE, 0, os.O_RDWR,
508
 
                "rb+")
 
428
                       "rb+")
509
429
 
510
430
        def restore_read_lock(self):
511
431
            """Restore the original ReadLock."""
514
434
            self.unlock()
515
435
            return _ctypes_ReadLock(self.filename)
516
436
 
517
 
 
518
437
    _lock_classes.append(('ctypes', _ctypes_WriteLock, _ctypes_ReadLock))
519
438
 
520
439
 
521
440
if len(_lock_classes) == 0:
522
441
    raise NotImplementedError(
523
 
        "We must have one of fcntl, pywin32, or ctypes available"
 
442
        "We must have one of fcntl or ctypes available"
524
443
        " to support OS locking."
525
444
        )
526
445
 
549
468
            trace.note(gettext('{0!r} was {1} locked again'), self, type_name)
550
469
        self._prev_lock = lock_type
551
470
 
 
471
 
552
472
@contextlib.contextmanager
553
473
def write_locked(lockable):
554
474
    lockable.lock_write()