~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lock.py

  • Committer: Vincent Ladeuil
  • Date: 2009-07-02 13:07:14 UTC
  • mto: (4524.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 4525.
  • Revision ID: v.ladeuil+lp@free.fr-20090702130714-hsyqfusi8vn3a11m
Use tree.has_changes() where appropriate (the test suite caught a
bug in has_changes() (not filtering out the root) in an impressive
number of tests)

* bzrlib/send.py:
(send): Use tree.has_changes() instead of tree.changes_from().

* bzrlib/reconfigure.py:
(Reconfigure._check): Use tree.has_changes() instead of
tree.changes_from().

* bzrlib/merge.py:
(Merger.ensure_revision_trees, Merger.compare_basis): Use
tree.has_changes() instead of tree.changes_from().

* bzrlib/builtins.py:
(cmd_remove_tree.run, cmd_push.run, cmd_merge.run): Use
tree.has_changes() instead of tree.changes_from().

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
 
18
18
"""Locking using OS file locks or file existence.
35
35
"""
36
36
 
37
37
import errno
 
38
import os
38
39
import sys
39
40
 
40
41
from bzrlib import (
42
43
    osutils,
43
44
    trace,
44
45
    )
45
 
from bzrlib.hooks import Hooks
 
46
from bzrlib.hooks import HookPoint, Hooks
46
47
 
47
48
 
48
49
class LockHooks(Hooks):
49
50
 
50
51
    def __init__(self):
51
52
        Hooks.__init__(self)
52
 
 
53
 
        # added in 1.8; called with a LockResult when a physical lock is
54
 
        # acquired
55
 
        self['lock_acquired'] = []
56
 
 
57
 
        # added in 1.8; called with a LockResult when a physical lock is
58
 
        # acquired
59
 
        self['lock_released'] = []
 
53
        self.create_hook(HookPoint('lock_acquired',
 
54
            "Called with a bzrlib.lock.LockResult when a physical lock is "
 
55
            "acquired.", (1, 8), None))
 
56
        self.create_hook(HookPoint('lock_released',
 
57
            "Called with a bzrlib.lock.LockResult when a physical lock is "
 
58
            "released.", (1, 8), None))
 
59
        self.create_hook(HookPoint('lock_broken',
 
60
            "Called with a bzrlib.lock.LockResult when a physical lock is "
 
61
            "broken.", (1, 15), None))
60
62
 
61
63
 
62
64
class Lock(object):
79
81
    def __eq__(self, other):
80
82
        return self.lock_url == other.lock_url and self.details == other.details
81
83
 
 
84
    def __repr__(self):
 
85
        return '%s(%s%s)' % (self.__class__.__name__,
 
86
                             self.lock_url, self.details)
 
87
 
82
88
 
83
89
try:
84
90
    import fcntl
91
97
if sys.platform == 'win32':
92
98
    import msvcrt
93
99
    try:
94
 
        import win32con, win32file, pywintypes, winerror
 
100
        import win32file, pywintypes, winerror
95
101
        have_pywin32 = True
96
102
    except ImportError:
97
103
        pass
146
152
 
147
153
 
148
154
if have_fcntl:
149
 
    LOCK_SH = fcntl.LOCK_SH
150
 
    LOCK_NB = fcntl.LOCK_NB
151
 
    lock_EX = fcntl.LOCK_EX
152
 
 
153
155
 
154
156
    class _fcntl_FileLock(_OSLock):
155
157
 
185
187
                    self.unlock()
186
188
                # we should be more precise about whats a locking
187
189
                # error and whats a random-other error
188
 
                raise errors.LockContention(e)
 
190
                raise errors.LockContention(self.filename, e)
189
191
 
190
192
        def unlock(self):
191
193
            _fcntl_WriteLock._open_locks.remove(self.filename)
209
211
            except IOError, e:
210
212
                # we should be more precise about whats a locking
211
213
                # error and whats a random-other error
212
 
                raise errors.LockContention(e)
 
214
                raise errors.LockContention(self.filename, e)
213
215
 
214
216
        def unlock(self):
215
217
            count = _fcntl_ReadLock._open_locks[self.filename]
277
279
                fcntl.lockf(new_f, fcntl.LOCK_EX | fcntl.LOCK_NB)
278
280
            except IOError, e:
279
281
                # TODO: Raise a more specific error based on the type of error
280
 
                raise errors.LockContention(e)
 
282
                raise errors.LockContention(self.filename, e)
281
283
            _fcntl_WriteLock._open_locks.add(self.filename)
282
284
 
283
285
            self.f = new_f
299
301
 
300
302
 
301
303
if have_pywin32 and sys.platform == 'win32':
302
 
    LOCK_SH = 0 # the default
303
 
    LOCK_EX = win32con.LOCKFILE_EXCLUSIVE_LOCK
304
 
    LOCK_NB = win32con.LOCKFILE_FAIL_IMMEDIATELY
305
 
 
306
304
 
307
305
    class _w32c_FileLock(_OSLock):
308
306
 
309
 
        def _lock(self, filename, openmode, lockmode):
310
 
            self._open(filename, openmode)
311
 
 
312
 
            self.hfile = msvcrt.get_osfhandle(self.f.fileno())
313
 
            overlapped = pywintypes.OVERLAPPED()
 
307
        def _open(self, filename, access, share, cflags, pymode):
 
308
            self.filename = osutils.realpath(filename)
314
309
            try:
315
 
                win32file.LockFileEx(self.hfile, lockmode, 0, 0x7fff0000,
316
 
                                     overlapped)
 
310
                self._handle = win32file.CreateFile(filename, access, share,
 
311
                    None, win32file.OPEN_ALWAYS,
 
312
                    win32file.FILE_ATTRIBUTE_NORMAL, None)
317
313
            except pywintypes.error, e:
318
 
                self._clear_f()
319
 
                if e.args[0] in (winerror.ERROR_LOCK_VIOLATION,):
320
 
                    raise errors.LockContention(filename)
321
 
                ## import pdb; pdb.set_trace()
 
314
                if e.args[0] == winerror.ERROR_ACCESS_DENIED:
 
315
                    raise errors.LockFailed(filename, e)
 
316
                if e.args[0] == winerror.ERROR_SHARING_VIOLATION:
 
317
                    raise errors.LockContention(filename, e)
322
318
                raise
323
 
            except Exception, e:
324
 
                self._clear_f()
325
 
                raise errors.LockContention(e)
 
319
            fd = win32file._open_osfhandle(self._handle, cflags)
 
320
            self.f = os.fdopen(fd, pymode)
 
321
            return self.f
326
322
 
327
323
        def unlock(self):
328
 
            overlapped = pywintypes.OVERLAPPED()
329
 
            try:
330
 
                win32file.UnlockFileEx(self.hfile, 0, 0x7fff0000, overlapped)
331
 
                self._clear_f()
332
 
            except Exception, e:
333
 
                raise errors.LockContention(e)
 
324
            self._clear_f()
 
325
            self._handle = None
334
326
 
335
327
 
336
328
    class _w32c_ReadLock(_w32c_FileLock):
337
329
        def __init__(self, filename):
338
330
            super(_w32c_ReadLock, self).__init__()
339
 
            self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
 
331
            self._open(filename, win32file.GENERIC_READ,
 
332
                win32file.FILE_SHARE_READ, os.O_RDONLY, "rb")
340
333
 
341
334
        def temporary_write_lock(self):
342
335
            """Try to grab a write lock on the file.
361
354
    class _w32c_WriteLock(_w32c_FileLock):
362
355
        def __init__(self, filename):
363
356
            super(_w32c_WriteLock, self).__init__()
364
 
            self._lock(filename, 'rb+', LOCK_EX + LOCK_NB)
 
357
            self._open(filename,
 
358
                win32file.GENERIC_READ | win32file.GENERIC_WRITE, 0,
 
359
                os.O_RDWR, "rb+")
365
360
 
366
361
        def restore_read_lock(self):
367
362
            """Restore the original ReadLock."""
375
370
 
376
371
 
377
372
if have_ctypes_win32:
378
 
    # These constants were copied from the win32con.py module.
379
 
    LOCKFILE_FAIL_IMMEDIATELY = 1
380
 
    LOCKFILE_EXCLUSIVE_LOCK = 2
381
 
    # Constant taken from winerror.py module
382
 
    ERROR_LOCK_VIOLATION = 33
383
 
 
384
 
    LOCK_SH = 0
385
 
    LOCK_EX = LOCKFILE_EXCLUSIVE_LOCK
386
 
    LOCK_NB = LOCKFILE_FAIL_IMMEDIATELY
387
 
    _LockFileEx = ctypes.windll.kernel32.LockFileEx
388
 
    _UnlockFileEx = ctypes.windll.kernel32.UnlockFileEx
389
 
    _GetLastError = ctypes.windll.kernel32.GetLastError
390
 
 
391
 
    ### Define the OVERLAPPED structure.
392
 
    #   http://msdn2.microsoft.com/en-us/library/ms684342.aspx
393
 
    # typedef struct _OVERLAPPED {
394
 
    #   ULONG_PTR Internal;
395
 
    #   ULONG_PTR InternalHigh;
396
 
    #   union {
397
 
    #     struct {
398
 
    #       DWORD Offset;
399
 
    #       DWORD OffsetHigh;
400
 
    #     };
401
 
    #     PVOID Pointer;
402
 
    #   };
403
 
    #   HANDLE hEvent;
404
 
    # } OVERLAPPED,
405
 
 
406
 
    class _inner_struct(ctypes.Structure):
407
 
        _fields_ = [('Offset', ctypes.c_uint), # DWORD
408
 
                    ('OffsetHigh', ctypes.c_uint), # DWORD
409
 
                   ]
410
 
 
411
 
    class _inner_union(ctypes.Union):
412
 
        _fields_  = [('anon_struct', _inner_struct), # struct
413
 
                     ('Pointer', ctypes.c_void_p), # PVOID
414
 
                    ]
415
 
 
416
 
    class OVERLAPPED(ctypes.Structure):
417
 
        _fields_ = [('Internal', ctypes.c_void_p), # ULONG_PTR
418
 
                    ('InternalHigh', ctypes.c_void_p), # ULONG_PTR
419
 
                    ('_inner_union', _inner_union),
420
 
                    ('hEvent', ctypes.c_void_p), # HANDLE
421
 
                   ]
 
373
    from ctypes.wintypes import DWORD, LPCSTR, LPCWSTR
 
374
    LPSECURITY_ATTRIBUTES = ctypes.c_void_p # used as NULL no need to declare
 
375
    HANDLE = ctypes.c_int # rather than unsigned as in ctypes.wintypes
 
376
    if os.path.supports_unicode_filenames:
 
377
        _function_name = "CreateFileW"
 
378
        LPTSTR = LPCWSTR
 
379
    else:
 
380
        _function_name = "CreateFileA"
 
381
        class LPTSTR(LPCSTR):
 
382
            def __new__(cls, obj):
 
383
                return LPCSTR.__new__(cls, obj.encode("mbcs"))
 
384
 
 
385
    # CreateFile <http://msdn.microsoft.com/en-us/library/aa363858.aspx>
 
386
    _CreateFile = ctypes.WINFUNCTYPE(
 
387
            HANDLE,                # return value
 
388
            LPTSTR,                # lpFileName
 
389
            DWORD,                 # dwDesiredAccess
 
390
            DWORD,                 # dwShareMode
 
391
            LPSECURITY_ATTRIBUTES, # lpSecurityAttributes
 
392
            DWORD,                 # dwCreationDisposition
 
393
            DWORD,                 # dwFlagsAndAttributes
 
394
            HANDLE                 # hTemplateFile
 
395
        )((_function_name, ctypes.windll.kernel32))
 
396
    
 
397
    INVALID_HANDLE_VALUE = -1
 
398
    
 
399
    GENERIC_READ = 0x80000000
 
400
    GENERIC_WRITE = 0x40000000
 
401
    FILE_SHARE_READ = 1
 
402
    OPEN_ALWAYS = 4
 
403
    FILE_ATTRIBUTE_NORMAL = 128
 
404
    
 
405
    ERROR_ACCESS_DENIED = 5
 
406
    ERROR_SHARING_VIOLATION = 32
422
407
 
423
408
    class _ctypes_FileLock(_OSLock):
424
409
 
425
 
        def _lock(self, filename, openmode, lockmode):
426
 
            self._open(filename, openmode)
427
 
 
428
 
            self.hfile = msvcrt.get_osfhandle(self.f.fileno())
429
 
            overlapped = OVERLAPPED()
430
 
            result = _LockFileEx(self.hfile, # HANDLE hFile
431
 
                                 lockmode,   # DWORD dwFlags
432
 
                                 0,          # DWORD dwReserved
433
 
                                 0x7fffffff, # DWORD nNumberOfBytesToLockLow
434
 
                                 0x00000000, # DWORD nNumberOfBytesToLockHigh
435
 
                                 ctypes.byref(overlapped), # lpOverlapped
436
 
                                )
437
 
            if result == 0:
438
 
                self._clear_f()
439
 
                last_err = _GetLastError()
440
 
                if last_err in (ERROR_LOCK_VIOLATION,):
441
 
                    raise errors.LockContention(filename)
442
 
                raise errors.LockContention('Unknown locking error: %s'
443
 
                                            % (last_err,))
 
410
        def _open(self, filename, access, share, cflags, pymode):
 
411
            self.filename = osutils.realpath(filename)
 
412
            handle = _CreateFile(filename, access, share, None, OPEN_ALWAYS,
 
413
                FILE_ATTRIBUTE_NORMAL, 0)
 
414
            if handle in (INVALID_HANDLE_VALUE, 0):
 
415
                e = ctypes.WinError()
 
416
                if e.args[0] == ERROR_ACCESS_DENIED:
 
417
                    raise errors.LockFailed(filename, e)
 
418
                if e.args[0] == ERROR_SHARING_VIOLATION:
 
419
                    raise errors.LockContention(filename, e)
 
420
                raise e
 
421
            fd = msvcrt.open_osfhandle(handle, cflags)
 
422
            self.f = os.fdopen(fd, pymode)
 
423
            return self.f
444
424
 
445
425
        def unlock(self):
446
 
            overlapped = OVERLAPPED()
447
 
            result = _UnlockFileEx(self.hfile, # HANDLE hFile
448
 
                                   0,          # DWORD dwReserved
449
 
                                   0x7fffffff, # DWORD nNumberOfBytesToLockLow
450
 
                                   0x00000000, # DWORD nNumberOfBytesToLockHigh
451
 
                                   ctypes.byref(overlapped), # lpOverlapped
452
 
                                  )
453
426
            self._clear_f()
454
 
            if result == 0:
455
 
                self._clear_f()
456
 
                last_err = _GetLastError()
457
 
                raise errors.LockContention('Unknown unlocking error: %s'
458
 
                                            % (last_err,))
459
427
 
460
428
 
461
429
    class _ctypes_ReadLock(_ctypes_FileLock):
462
430
        def __init__(self, filename):
463
431
            super(_ctypes_ReadLock, self).__init__()
464
 
            self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
 
432
            self._open(filename, GENERIC_READ, FILE_SHARE_READ, os.O_RDONLY,
 
433
                "rb")
465
434
 
466
435
        def temporary_write_lock(self):
467
436
            """Try to grab a write lock on the file.
485
454
    class _ctypes_WriteLock(_ctypes_FileLock):
486
455
        def __init__(self, filename):
487
456
            super(_ctypes_WriteLock, self).__init__()
488
 
            self._lock(filename, 'rb+', LOCK_EX + LOCK_NB)
 
457
            self._open(filename, GENERIC_READ | GENERIC_WRITE, 0, os.O_RDWR,
 
458
                "rb+")
489
459
 
490
460
        def restore_read_lock(self):
491
461
            """Restore the original ReadLock."""