/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: 2018-02-18 21:42:57 UTC
  • mto: This revision was merged to the branch mainline in revision 6859.
  • Revision ID: jelmer@jelmer.uk-20180218214257-jpevutp1wa30tz3v
Update TODO to reference Breezy, not Bazaar.

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
48
50
from .hooks import Hooks
49
51
from .i18n import gettext
50
52
 
51
 
 
52
53
class LockHooks(Hooks):
53
54
 
54
55
    def __init__(self):
55
56
        Hooks.__init__(self, "breezy.lock", "Lock.hooks")
56
 
        self.add_hook(
57
 
            'lock_acquired',
 
57
        self.add_hook('lock_acquired',
58
58
            "Called with a breezy.lock.LockResult when a physical lock is "
59
59
            "acquired.", (1, 8))
60
 
        self.add_hook(
61
 
            'lock_released',
 
60
        self.add_hook('lock_released',
62
61
            "Called with a breezy.lock.LockResult when a physical lock is "
63
62
            "released.", (1, 8))
64
 
        self.add_hook(
65
 
            'lock_broken',
 
63
        self.add_hook('lock_broken',
66
64
            "Called with a breezy.lock.LockResult when a physical lock is "
67
65
            "broken.", (1, 15))
68
66
 
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):
112
110
        # If there was an error raised, prefer the original one
113
111
        try:
114
112
            self.unlock()
115
 
        except BaseException:
 
113
        except:
116
114
            if exc_type is None:
117
115
                raise
118
116
        return False
121
119
def cant_unlock_not_held(locked_object):
122
120
    """An attempt to unlock failed because the object was not locked.
123
121
 
124
 
    This provides a policy point from which we can generate either a warning or
125
 
    an exception.
 
122
    This provides a policy point from which we can generate either a warning 
 
123
    or an exception.
126
124
    """
127
125
    # This is typically masking some other error and called from a finally
128
126
    # block, so it's useful to have the option not to generate a new error
130
128
    # raise LockNotHeld.
131
129
    if 'unlock' in debug.debug_flags:
132
130
        warnings.warn("%r is already unlocked" % (locked_object,),
133
 
                      stacklevel=3)
 
131
            stacklevel=3)
134
132
    else:
135
133
        raise errors.LockNotHeld(locked_object)
136
134
 
141
139
except ImportError:
142
140
    have_fcntl = False
143
141
 
 
142
have_pywin32 = False
144
143
have_ctypes_win32 = False
145
144
if sys.platform == 'win32':
146
145
    import msvcrt
147
146
    try:
 
147
        import win32file, pywintypes, winerror
 
148
        have_pywin32 = True
 
149
    except ImportError:
 
150
        pass
 
151
 
 
152
    try:
148
153
        import ctypes
149
154
        have_ctypes_win32 = True
150
155
    except ImportError:
195
200
            fcntl.lockf(self.f, fcntl.LOCK_UN)
196
201
            self._clear_f()
197
202
 
 
203
 
198
204
    class _fcntl_WriteLock(_fcntl_FileLock):
199
205
 
200
206
        _open_locks = set()
235
241
            _fcntl_WriteLock._open_locks.remove(self.filename)
236
242
            self._unlock()
237
243
 
 
244
 
238
245
    class _fcntl_ReadLock(_fcntl_FileLock):
239
246
 
240
247
        _open_locks = {}
282
289
            """
283
290
            if self.filename in _fcntl_WriteLock._open_locks:
284
291
                raise AssertionError('file already locked: %r'
285
 
                                     % (self.filename,))
 
292
                    % (self.filename,))
286
293
            try:
287
294
                wlock = _fcntl_TemporaryWriteLock(self)
288
295
            except errors.LockError:
290
297
                return False, self
291
298
            return True, wlock
292
299
 
 
300
 
293
301
    class _fcntl_TemporaryWriteLock(_OSLock):
294
302
        """A token used when grabbing a temporary_write_lock.
295
303
 
309
317
 
310
318
            if self.filename in _fcntl_WriteLock._open_locks:
311
319
                raise AssertionError('file already locked: %r'
312
 
                                     % (self.filename,))
 
320
                    % (self.filename,))
313
321
 
314
322
            # See if we can open the file for writing. Another process might
315
323
            # have a read lock. We don't use self._open() because we don't want
334
342
 
335
343
        def restore_read_lock(self):
336
344
            """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.
 
345
            # For fcntl, since we never released the read lock, just release the
 
346
            # write lock, and return the original lock.
339
347
            fcntl.lockf(self.f, fcntl.LOCK_UN)
340
348
            self._clear_f()
341
349
            _fcntl_WriteLock._open_locks.remove(self.filename)
344
352
            self._read_lock = None
345
353
            return read_lock
346
354
 
 
355
 
347
356
    _lock_classes.append(('fcntl', _fcntl_WriteLock, _fcntl_ReadLock))
348
357
 
349
358
 
 
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
 
350
429
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
 
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
354
433
    _function_name = "CreateFileW"
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
            LPWSTR,                # 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()