/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-04-05 19:11:34 UTC
  • mto: (7490.7.16 work)
  • mto: This revision was merged to the branch mainline in revision 7501.
  • Revision ID: jelmer@jelmer.uk-20200405191134-0aebh8ikiwygxma5
Populate the .gitignore file.

Show diffs side-by-side

added added

removed removed

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