/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: Marius Kruger
  • Date: 2010-07-10 21:28:56 UTC
  • mto: (5384.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 5385.
  • Revision ID: marius.kruger@enerweb.co.za-20100710212856-uq4ji3go0u5se7hx
* Update documentation
* add NEWS

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
 
17
18
"""Locking using OS file locks or file existence.
18
19
 
19
20
Note: This method of locking is generally deprecated in favour of LockDir, but
33
34
unlock() method.
34
35
"""
35
36
 
36
 
from __future__ import absolute_import
37
 
 
38
 
import contextlib
39
37
import errno
40
38
import os
41
39
import sys
42
40
import warnings
43
41
 
44
 
from . import (
 
42
from bzrlib import (
45
43
    debug,
46
44
    errors,
47
45
    osutils,
48
46
    trace,
49
47
    )
50
 
from .hooks import Hooks
51
 
from .i18n import gettext
 
48
from bzrlib.hooks import HookPoint, Hooks
52
49
 
53
50
 
54
51
class LockHooks(Hooks):
55
52
 
56
53
    def __init__(self):
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))
 
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))
70
64
 
71
65
 
72
66
class Lock(object):
91
85
 
92
86
    def __repr__(self):
93
87
        return '%s(%s, %s)' % (self.__class__.__name__,
94
 
                               self.lock_url, self.details)
 
88
                             self.lock_url, self.details)
95
89
 
96
90
 
97
91
class LogicalLockResult(object):
100
94
    :ivar unlock: A callable which will unlock the lock.
101
95
    """
102
96
 
103
 
    def __init__(self, unlock, token=None):
 
97
    def __init__(self, unlock):
104
98
        self.unlock = unlock
105
 
        self.token = token
106
99
 
107
100
    def __repr__(self):
108
101
        return "LogicalLockResult(%s)" % (self.unlock)
109
102
 
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
121
103
 
122
104
 
123
105
def cant_unlock_not_held(locked_object):
124
106
    """An attempt to unlock failed because the object was not locked.
125
107
 
126
 
    This provides a policy point from which we can generate either a warning or
127
 
    an exception.
 
108
    This provides a policy point from which we can generate either a warning 
 
109
    or an exception.
128
110
    """
129
111
    # This is typically masking some other error and called from a finally
130
112
    # block, so it's useful to have the option not to generate a new error
132
114
    # raise LockNotHeld.
133
115
    if 'unlock' in debug.debug_flags:
134
116
        warnings.warn("%r is already unlocked" % (locked_object,),
135
 
                      stacklevel=3)
 
117
            stacklevel=3)
136
118
    else:
137
119
        raise errors.LockNotHeld(locked_object)
138
120
 
143
125
except ImportError:
144
126
    have_fcntl = False
145
127
 
 
128
have_pywin32 = False
146
129
have_ctypes_win32 = False
147
130
if sys.platform == 'win32':
148
131
    import msvcrt
149
132
    try:
 
133
        import win32file, pywintypes, winerror
 
134
        have_pywin32 = True
 
135
    except ImportError:
 
136
        pass
 
137
 
 
138
    try:
150
139
        import ctypes
151
140
        have_ctypes_win32 = True
152
141
    except ImportError:
164
153
        try:
165
154
            self.f = open(self.filename, filemode)
166
155
            return self.f
167
 
        except IOError as e:
 
156
        except IOError, e:
168
157
            if e.errno in (errno.EACCES, errno.EPERM):
169
158
                raise errors.LockFailed(self.filename, str(e))
170
159
            if e.errno != errno.ENOENT:
182
171
            self.f.close()
183
172
            self.f = None
184
173
 
 
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
 
185
180
    def unlock(self):
186
181
        raise NotImplementedError()
187
182
 
197
192
            fcntl.lockf(self.f, fcntl.LOCK_UN)
198
193
            self._clear_f()
199
194
 
 
195
 
200
196
    class _fcntl_WriteLock(_fcntl_FileLock):
201
197
 
202
198
        _open_locks = set()
225
221
                # LOCK_NB will cause IOError to be raised if we can't grab a
226
222
                # lock right away.
227
223
                fcntl.lockf(self.f, fcntl.LOCK_EX | fcntl.LOCK_NB)
228
 
            except IOError as e:
 
224
            except IOError, e:
229
225
                if e.errno in (errno.EAGAIN, errno.EACCES):
230
226
                    # We couldn't grab the lock
231
227
                    self.unlock()
237
233
            _fcntl_WriteLock._open_locks.remove(self.filename)
238
234
            self._unlock()
239
235
 
 
236
 
240
237
    class _fcntl_ReadLock(_fcntl_FileLock):
241
238
 
242
239
        _open_locks = {}
259
256
                # LOCK_NB will cause IOError to be raised if we can't grab a
260
257
                # lock right away.
261
258
                fcntl.lockf(self.f, fcntl.LOCK_SH | fcntl.LOCK_NB)
262
 
            except IOError as e:
 
259
            except IOError, e:
263
260
                # we should be more precise about whats a locking
264
261
                # error and whats a random-other error
265
262
                raise errors.LockContention(self.filename, e)
284
281
            """
285
282
            if self.filename in _fcntl_WriteLock._open_locks:
286
283
                raise AssertionError('file already locked: %r'
287
 
                                     % (self.filename,))
 
284
                    % (self.filename,))
288
285
            try:
289
286
                wlock = _fcntl_TemporaryWriteLock(self)
290
287
            except errors.LockError:
292
289
                return False, self
293
290
            return True, wlock
294
291
 
 
292
 
295
293
    class _fcntl_TemporaryWriteLock(_OSLock):
296
294
        """A token used when grabbing a temporary_write_lock.
297
295
 
311
309
 
312
310
            if self.filename in _fcntl_WriteLock._open_locks:
313
311
                raise AssertionError('file already locked: %r'
314
 
                                     % (self.filename,))
 
312
                    % (self.filename,))
315
313
 
316
314
            # See if we can open the file for writing. Another process might
317
315
            # have a read lock. We don't use self._open() because we don't want
319
317
            # done by _fcntl_ReadLock
320
318
            try:
321
319
                new_f = open(self.filename, 'rb+')
322
 
            except IOError as e:
 
320
            except IOError, e:
323
321
                if e.errno in (errno.EACCES, errno.EPERM):
324
322
                    raise errors.LockFailed(self.filename, str(e))
325
323
                raise
327
325
                # LOCK_NB will cause IOError to be raised if we can't grab a
328
326
                # lock right away.
329
327
                fcntl.lockf(new_f, fcntl.LOCK_EX | fcntl.LOCK_NB)
330
 
            except IOError as e:
 
328
            except IOError, e:
331
329
                # TODO: Raise a more specific error based on the type of error
332
330
                raise errors.LockContention(self.filename, e)
333
331
            _fcntl_WriteLock._open_locks.add(self.filename)
336
334
 
337
335
        def restore_read_lock(self):
338
336
            """Restore the original ReadLock."""
339
 
            # For fcntl, since we never released the read lock, just release
340
 
            # the write lock, and return the original lock.
 
337
            # For fcntl, since we never released the read lock, just release the
 
338
            # write lock, and return the original lock.
341
339
            fcntl.lockf(self.f, fcntl.LOCK_UN)
342
340
            self._clear_f()
343
341
            _fcntl_WriteLock._open_locks.remove(self.filename)
346
344
            self._read_lock = None
347
345
            return read_lock
348
346
 
 
347
 
349
348
    _lock_classes.append(('fcntl', _fcntl_WriteLock, _fcntl_ReadLock))
350
349
 
351
350
 
 
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
 
352
426
if have_ctypes_win32:
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"
 
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"))
357
438
 
358
439
    # CreateFile <http://msdn.microsoft.com/en-us/library/aa363858.aspx>
359
440
    _CreateFile = ctypes.WINFUNCTYPE(
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
 
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
368
449
        )((_function_name, ctypes.windll.kernel32))
369
450
 
370
451
    INVALID_HANDLE_VALUE = -1
383
464
        def _open(self, filename, access, share, cflags, pymode):
384
465
            self.filename = osutils.realpath(filename)
385
466
            handle = _CreateFile(filename, access, share, None, OPEN_ALWAYS,
386
 
                                 FILE_ATTRIBUTE_NORMAL, 0)
 
467
                FILE_ATTRIBUTE_NORMAL, 0)
387
468
            if handle in (INVALID_HANDLE_VALUE, 0):
388
469
                e = ctypes.WinError()
389
470
                if e.args[0] == ERROR_ACCESS_DENIED:
398
479
        def unlock(self):
399
480
            self._clear_f()
400
481
 
 
482
 
401
483
    class _ctypes_ReadLock(_ctypes_FileLock):
402
484
        def __init__(self, filename):
403
485
            super(_ctypes_ReadLock, self).__init__()
404
486
            self._open(filename, GENERIC_READ, FILE_SHARE_READ, os.O_RDONLY,
405
 
                       "rb")
 
487
                "rb")
406
488
 
407
489
        def temporary_write_lock(self):
408
490
            """Try to grab a write lock on the file.
427
509
        def __init__(self, filename):
428
510
            super(_ctypes_WriteLock, self).__init__()
429
511
            self._open(filename, GENERIC_READ | GENERIC_WRITE, 0, os.O_RDWR,
430
 
                       "rb+")
 
512
                "rb+")
431
513
 
432
514
        def restore_read_lock(self):
433
515
            """Restore the original ReadLock."""
436
518
            self.unlock()
437
519
            return _ctypes_ReadLock(self.filename)
438
520
 
 
521
 
439
522
    _lock_classes.append(('ctypes', _ctypes_WriteLock, _ctypes_ReadLock))
440
523
 
441
524
 
442
525
if len(_lock_classes) == 0:
443
526
    raise NotImplementedError(
444
 
        "We must have one of fcntl or ctypes available"
 
527
        "We must have one of fcntl, pywin32, or ctypes available"
445
528
        " to support OS locking."
446
529
        )
447
530
 
458
541
    locked the same way), and -Drelock is set, then this will trace.note a
459
542
    message about it.
460
543
    """
461
 
 
 
544
    
462
545
    _prev_lock = None
463
546
 
464
547
    def _note_lock(self, lock_type):
467
550
                type_name = 'read'
468
551
            else:
469
552
                type_name = 'write'
470
 
            trace.note(gettext('{0!r} was {1} locked again'), self, type_name)
 
553
            trace.note('%r was %s locked again', self, type_name)
471
554
        self._prev_lock = lock_type
472
555
 
473
 
 
474
 
@contextlib.contextmanager
475
 
def write_locked(lockable):
476
 
    lockable.lock_write()
477
 
    try:
478
 
        yield lockable
479
 
    finally:
480
 
        lockable.unlock()