~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lock.py

  • Committer: John Ferlito
  • Date: 2009-09-02 04:31:45 UTC
  • mto: (4665.7.1 serve-init)
  • mto: This revision was merged to the branch mainline in revision 4913.
  • Revision ID: johnf@inodes.org-20090902043145-gxdsfw03ilcwbyn5
Add a debian init script for bzr --serve

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007 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
 
40
import warnings
39
41
 
40
42
from bzrlib import (
 
43
    debug,
41
44
    errors,
42
45
    osutils,
43
46
    trace,
44
47
    )
 
48
from bzrlib.hooks import HookPoint, Hooks
 
49
 
 
50
 
 
51
class LockHooks(Hooks):
 
52
 
 
53
    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))
 
64
 
 
65
 
 
66
class Lock(object):
 
67
    """Base class for locks.
 
68
 
 
69
    :cvar hooks: Hook dictionary for operations on locks.
 
70
    """
 
71
 
 
72
    hooks = LockHooks()
 
73
 
 
74
 
 
75
class LockResult(object):
 
76
    """Result of an operation on a lock; passed to a hook"""
 
77
 
 
78
    def __init__(self, lock_url, details=None):
 
79
        """Create a lock result for lock with optional details about the lock."""
 
80
        self.lock_url = lock_url
 
81
        self.details = details
 
82
 
 
83
    def __eq__(self, other):
 
84
        return self.lock_url == other.lock_url and self.details == other.details
 
85
 
 
86
    def __repr__(self):
 
87
        return '%s(%s%s)' % (self.__class__.__name__,
 
88
                             self.lock_url, self.details)
 
89
 
 
90
 
 
91
def cant_unlock_not_held(locked_object):
 
92
    """An attempt to unlock failed because the object was not locked.
 
93
 
 
94
    This provides a policy point from which we can generate either a warning 
 
95
    or an exception.
 
96
    """
 
97
    # This is typically masking some other error and called from a finally
 
98
    # block, so it's useful to have the option not to generate a new error
 
99
    # here.  You can use -Werror to make it fatal.  It should possibly also
 
100
    # raise LockNotHeld.
 
101
    if 'unlock' in debug.debug_flags:
 
102
        warnings.warn("%r is already unlocked" % (locked_object,),
 
103
            stacklevel=3)
 
104
    else:
 
105
        raise errors.LockNotHeld(locked_object)
 
106
 
 
107
 
 
108
try:
 
109
    import fcntl
 
110
    have_fcntl = True
 
111
except ImportError:
 
112
    have_fcntl = False
 
113
 
 
114
have_pywin32 = False
 
115
have_ctypes_win32 = False
 
116
if sys.platform == 'win32':
 
117
    import msvcrt
 
118
    try:
 
119
        import win32file, pywintypes, winerror
 
120
        have_pywin32 = True
 
121
    except ImportError:
 
122
        pass
 
123
 
 
124
    try:
 
125
        import ctypes
 
126
        have_ctypes_win32 = True
 
127
    except ImportError:
 
128
        pass
45
129
 
46
130
 
47
131
class _OSLock(object):
83
167
        raise NotImplementedError()
84
168
 
85
169
 
86
 
try:
87
 
    import fcntl
88
 
    have_fcntl = True
89
 
except ImportError:
90
 
    have_fcntl = False
91
 
try:
92
 
    import win32con, win32file, pywintypes, winerror, msvcrt
93
 
    have_pywin32 = True
94
 
except ImportError:
95
 
    have_pywin32 = False
96
 
try:
97
 
    import ctypes, msvcrt
98
 
    have_ctypes = True
99
 
except ImportError:
100
 
    have_ctypes = False
101
 
 
102
 
 
103
170
_lock_classes = []
104
171
 
105
172
 
106
173
if have_fcntl:
107
 
    LOCK_SH = fcntl.LOCK_SH
108
 
    LOCK_NB = fcntl.LOCK_NB
109
 
    lock_EX = fcntl.LOCK_EX
110
 
 
111
174
 
112
175
    class _fcntl_FileLock(_OSLock):
113
176
 
127
190
            if self.filename in _fcntl_WriteLock._open_locks:
128
191
                self._clear_f()
129
192
                raise errors.LockContention(self.filename)
 
193
            if self.filename in _fcntl_ReadLock._open_locks:
 
194
                if 'strict_locks' in debug.debug_flags:
 
195
                    self._clear_f()
 
196
                    raise errors.LockContention(self.filename)
 
197
                else:
 
198
                    trace.mutter('Write lock taken w/ an open read lock on: %s'
 
199
                                 % (self.filename,))
130
200
 
131
201
            self._open(self.filename, 'rb+')
132
202
            # reserve a slot for this lock - even if the lockf call fails,
143
213
                    self.unlock()
144
214
                # we should be more precise about whats a locking
145
215
                # error and whats a random-other error
146
 
                raise errors.LockContention(e)
 
216
                raise errors.LockContention(self.filename, e)
147
217
 
148
218
        def unlock(self):
149
219
            _fcntl_WriteLock._open_locks.remove(self.filename)
157
227
        def __init__(self, filename):
158
228
            super(_fcntl_ReadLock, self).__init__()
159
229
            self.filename = osutils.realpath(filename)
 
230
            if self.filename in _fcntl_WriteLock._open_locks:
 
231
                if 'strict_locks' in debug.debug_flags:
 
232
                    # We raise before calling _open so we don't need to
 
233
                    # _clear_f
 
234
                    raise errors.LockContention(self.filename)
 
235
                else:
 
236
                    trace.mutter('Read lock taken w/ an open write lock on: %s'
 
237
                                 % (self.filename,))
160
238
            _fcntl_ReadLock._open_locks.setdefault(self.filename, 0)
161
239
            _fcntl_ReadLock._open_locks[self.filename] += 1
162
240
            self._open(filename, 'rb')
167
245
            except IOError, e:
168
246
                # we should be more precise about whats a locking
169
247
                # error and whats a random-other error
170
 
                raise errors.LockContention(e)
 
248
                raise errors.LockContention(self.filename, e)
171
249
 
172
250
        def unlock(self):
173
251
            count = _fcntl_ReadLock._open_locks[self.filename]
235
313
                fcntl.lockf(new_f, fcntl.LOCK_EX | fcntl.LOCK_NB)
236
314
            except IOError, e:
237
315
                # TODO: Raise a more specific error based on the type of error
238
 
                raise errors.LockContention(e)
 
316
                raise errors.LockContention(self.filename, e)
239
317
            _fcntl_WriteLock._open_locks.add(self.filename)
240
318
 
241
319
            self.f = new_f
257
335
 
258
336
 
259
337
if have_pywin32 and sys.platform == 'win32':
260
 
    LOCK_SH = 0 # the default
261
 
    LOCK_EX = win32con.LOCKFILE_EXCLUSIVE_LOCK
262
 
    LOCK_NB = win32con.LOCKFILE_FAIL_IMMEDIATELY
263
 
 
 
338
    if os.path.supports_unicode_filenames:
 
339
        # for Windows NT/2K/XP/etc
 
340
        win32file_CreateFile = win32file.CreateFileW
 
341
    else:
 
342
        # for Windows 98
 
343
        win32file_CreateFile = win32file.CreateFile
264
344
 
265
345
    class _w32c_FileLock(_OSLock):
266
346
 
267
 
        def _lock(self, filename, openmode, lockmode):
268
 
            self._open(filename, openmode)
269
 
 
270
 
            self.hfile = msvcrt.get_osfhandle(self.f.fileno())
271
 
            overlapped = pywintypes.OVERLAPPED()
 
347
        def _open(self, filename, access, share, cflags, pymode):
 
348
            self.filename = osutils.realpath(filename)
272
349
            try:
273
 
                win32file.LockFileEx(self.hfile, lockmode, 0, 0x7fff0000,
274
 
                                     overlapped)
 
350
                self._handle = win32file_CreateFile(filename, access, share,
 
351
                    None, win32file.OPEN_ALWAYS,
 
352
                    win32file.FILE_ATTRIBUTE_NORMAL, None)
275
353
            except pywintypes.error, e:
276
 
                self._clear_f()
277
 
                if e.args[0] in (winerror.ERROR_LOCK_VIOLATION,):
278
 
                    raise errors.LockContention(filename)
279
 
                ## import pdb; pdb.set_trace()
 
354
                if e.args[0] == winerror.ERROR_ACCESS_DENIED:
 
355
                    raise errors.LockFailed(filename, e)
 
356
                if e.args[0] == winerror.ERROR_SHARING_VIOLATION:
 
357
                    raise errors.LockContention(filename, e)
280
358
                raise
281
 
            except Exception, e:
282
 
                self._clear_f()
283
 
                raise errors.LockContention(e)
 
359
            fd = win32file._open_osfhandle(self._handle, cflags)
 
360
            self.f = os.fdopen(fd, pymode)
 
361
            return self.f
284
362
 
285
363
        def unlock(self):
286
 
            overlapped = pywintypes.OVERLAPPED()
287
 
            try:
288
 
                win32file.UnlockFileEx(self.hfile, 0, 0x7fff0000, overlapped)
289
 
                self._clear_f()
290
 
            except Exception, e:
291
 
                raise errors.LockContention(e)
 
364
            self._clear_f()
 
365
            self._handle = None
292
366
 
293
367
 
294
368
    class _w32c_ReadLock(_w32c_FileLock):
295
369
        def __init__(self, filename):
296
370
            super(_w32c_ReadLock, self).__init__()
297
 
            self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
 
371
            self._open(filename, win32file.GENERIC_READ,
 
372
                win32file.FILE_SHARE_READ, os.O_RDONLY, "rb")
298
373
 
299
374
        def temporary_write_lock(self):
300
375
            """Try to grab a write lock on the file.
319
394
    class _w32c_WriteLock(_w32c_FileLock):
320
395
        def __init__(self, filename):
321
396
            super(_w32c_WriteLock, self).__init__()
322
 
            self._lock(filename, 'rb+', LOCK_EX + LOCK_NB)
 
397
            self._open(filename,
 
398
                win32file.GENERIC_READ | win32file.GENERIC_WRITE, 0,
 
399
                os.O_RDWR, "rb+")
323
400
 
324
401
        def restore_read_lock(self):
325
402
            """Restore the original ReadLock."""
332
409
    _lock_classes.append(('pywin32', _w32c_WriteLock, _w32c_ReadLock))
333
410
 
334
411
 
335
 
if have_ctypes and sys.platform == 'win32':
336
 
    # These constants were copied from the win32con.py module.
337
 
    LOCKFILE_FAIL_IMMEDIATELY = 1
338
 
    LOCKFILE_EXCLUSIVE_LOCK = 2
339
 
    # Constant taken from winerror.py module
340
 
    ERROR_LOCK_VIOLATION = 33
341
 
 
342
 
    LOCK_SH = 0
343
 
    LOCK_EX = LOCKFILE_EXCLUSIVE_LOCK
344
 
    LOCK_NB = LOCKFILE_FAIL_IMMEDIATELY
345
 
    _LockFileEx = ctypes.windll.kernel32.LockFileEx
346
 
    _UnlockFileEx = ctypes.windll.kernel32.UnlockFileEx
347
 
    _GetLastError = ctypes.windll.kernel32.GetLastError
348
 
 
349
 
    ### Define the OVERLAPPED structure.
350
 
    #   http://msdn2.microsoft.com/en-us/library/ms684342.aspx
351
 
    # typedef struct _OVERLAPPED {
352
 
    #   ULONG_PTR Internal;
353
 
    #   ULONG_PTR InternalHigh;
354
 
    #   union {
355
 
    #     struct {
356
 
    #       DWORD Offset;
357
 
    #       DWORD OffsetHigh;
358
 
    #     };
359
 
    #     PVOID Pointer;
360
 
    #   };
361
 
    #   HANDLE hEvent;
362
 
    # } OVERLAPPED,
363
 
 
364
 
    class _inner_struct(ctypes.Structure):
365
 
        _fields_ = [('Offset', ctypes.c_uint), # DWORD
366
 
                    ('OffsetHigh', ctypes.c_uint), # DWORD
367
 
                   ]
368
 
 
369
 
    class _inner_union(ctypes.Union):
370
 
        _fields_  = [('anon_struct', _inner_struct), # struct
371
 
                     ('Pointer', ctypes.c_void_p), # PVOID
372
 
                    ]
373
 
 
374
 
    class OVERLAPPED(ctypes.Structure):
375
 
        _fields_ = [('Internal', ctypes.c_void_p), # ULONG_PTR
376
 
                    ('InternalHigh', ctypes.c_void_p), # ULONG_PTR
377
 
                    ('_inner_union', _inner_union),
378
 
                    ('hEvent', ctypes.c_void_p), # HANDLE
379
 
                   ]
 
412
if have_ctypes_win32:
 
413
    from ctypes.wintypes import DWORD, LPCSTR, LPCWSTR
 
414
    LPSECURITY_ATTRIBUTES = ctypes.c_void_p # used as NULL no need to declare
 
415
    HANDLE = ctypes.c_int # rather than unsigned as in ctypes.wintypes
 
416
    if os.path.supports_unicode_filenames:
 
417
        _function_name = "CreateFileW"
 
418
        LPTSTR = LPCWSTR
 
419
    else:
 
420
        _function_name = "CreateFileA"
 
421
        class LPTSTR(LPCSTR):
 
422
            def __new__(cls, obj):
 
423
                return LPCSTR.__new__(cls, obj.encode("mbcs"))
 
424
 
 
425
    # CreateFile <http://msdn.microsoft.com/en-us/library/aa363858.aspx>
 
426
    _CreateFile = ctypes.WINFUNCTYPE(
 
427
            HANDLE,                # return value
 
428
            LPTSTR,                # lpFileName
 
429
            DWORD,                 # dwDesiredAccess
 
430
            DWORD,                 # dwShareMode
 
431
            LPSECURITY_ATTRIBUTES, # lpSecurityAttributes
 
432
            DWORD,                 # dwCreationDisposition
 
433
            DWORD,                 # dwFlagsAndAttributes
 
434
            HANDLE                 # hTemplateFile
 
435
        )((_function_name, ctypes.windll.kernel32))
 
436
 
 
437
    INVALID_HANDLE_VALUE = -1
 
438
 
 
439
    GENERIC_READ = 0x80000000
 
440
    GENERIC_WRITE = 0x40000000
 
441
    FILE_SHARE_READ = 1
 
442
    OPEN_ALWAYS = 4
 
443
    FILE_ATTRIBUTE_NORMAL = 128
 
444
 
 
445
    ERROR_ACCESS_DENIED = 5
 
446
    ERROR_SHARING_VIOLATION = 32
380
447
 
381
448
    class _ctypes_FileLock(_OSLock):
382
449
 
383
 
        def _lock(self, filename, openmode, lockmode):
384
 
            self._open(filename, openmode)
385
 
 
386
 
            self.hfile = msvcrt.get_osfhandle(self.f.fileno())
387
 
            overlapped = OVERLAPPED()
388
 
            result = _LockFileEx(self.hfile, # HANDLE hFile
389
 
                                 lockmode,   # DWORD dwFlags
390
 
                                 0,          # DWORD dwReserved
391
 
                                 0x7fffffff, # DWORD nNumberOfBytesToLockLow
392
 
                                 0x00000000, # DWORD nNumberOfBytesToLockHigh
393
 
                                 ctypes.byref(overlapped), # lpOverlapped
394
 
                                )
395
 
            if result == 0:
396
 
                self._clear_f()
397
 
                last_err = _GetLastError()
398
 
                if last_err in (ERROR_LOCK_VIOLATION,):
399
 
                    raise errors.LockContention(filename)
400
 
                raise errors.LockContention('Unknown locking error: %s'
401
 
                                            % (last_err,))
 
450
        def _open(self, filename, access, share, cflags, pymode):
 
451
            self.filename = osutils.realpath(filename)
 
452
            handle = _CreateFile(filename, access, share, None, OPEN_ALWAYS,
 
453
                FILE_ATTRIBUTE_NORMAL, 0)
 
454
            if handle in (INVALID_HANDLE_VALUE, 0):
 
455
                e = ctypes.WinError()
 
456
                if e.args[0] == ERROR_ACCESS_DENIED:
 
457
                    raise errors.LockFailed(filename, e)
 
458
                if e.args[0] == ERROR_SHARING_VIOLATION:
 
459
                    raise errors.LockContention(filename, e)
 
460
                raise e
 
461
            fd = msvcrt.open_osfhandle(handle, cflags)
 
462
            self.f = os.fdopen(fd, pymode)
 
463
            return self.f
402
464
 
403
465
        def unlock(self):
404
 
            overlapped = OVERLAPPED()
405
 
            result = _UnlockFileEx(self.hfile, # HANDLE hFile
406
 
                                   0,          # DWORD dwReserved
407
 
                                   0x7fffffff, # DWORD nNumberOfBytesToLockLow
408
 
                                   0x00000000, # DWORD nNumberOfBytesToLockHigh
409
 
                                   ctypes.byref(overlapped), # lpOverlapped
410
 
                                  )
411
466
            self._clear_f()
412
 
            if result == 0:
413
 
                self._clear_f()
414
 
                last_err = _GetLastError()
415
 
                raise errors.LockContention('Unknown unlocking error: %s'
416
 
                                            % (last_err,))
417
467
 
418
468
 
419
469
    class _ctypes_ReadLock(_ctypes_FileLock):
420
470
        def __init__(self, filename):
421
471
            super(_ctypes_ReadLock, self).__init__()
422
 
            self._lock(filename, 'rb', LOCK_SH + LOCK_NB)
 
472
            self._open(filename, GENERIC_READ, FILE_SHARE_READ, os.O_RDONLY,
 
473
                "rb")
423
474
 
424
475
        def temporary_write_lock(self):
425
476
            """Try to grab a write lock on the file.
443
494
    class _ctypes_WriteLock(_ctypes_FileLock):
444
495
        def __init__(self, filename):
445
496
            super(_ctypes_WriteLock, self).__init__()
446
 
            self._lock(filename, 'rb+', LOCK_EX + LOCK_NB)
 
497
            self._open(filename, GENERIC_READ | GENERIC_WRITE, 0, os.O_RDWR,
 
498
                "rb+")
447
499
 
448
500
        def restore_read_lock(self):
449
501
            """Restore the original ReadLock."""