1cef7893435aa41160dd1255c43cb8498279738ccChris Craik# Copyright 2016 The Chromium Authors. All rights reserved. 2cef7893435aa41160dd1255c43cb8498279738ccChris Craik# Use of this source code is governed by a BSD-style license that can be 3cef7893435aa41160dd1255c43cb8498279738ccChris Craik# found in the LICENSE file. 4cef7893435aa41160dd1255c43cb8498279738ccChris Craik 5cef7893435aa41160dd1255c43cb8498279738ccChris Craikimport contextlib 6cef7893435aa41160dd1255c43cb8498279738ccChris Craikimport os 7cef7893435aa41160dd1255c43cb8498279738ccChris Craik 8cef7893435aa41160dd1255c43cb8498279738ccChris CraikLOCK_EX = None # Exclusive lock 9cef7893435aa41160dd1255c43cb8498279738ccChris CraikLOCK_SH = None # Shared lock 10cef7893435aa41160dd1255c43cb8498279738ccChris CraikLOCK_NB = None # Non-blocking (LockException is raised if resource is locked) 11cef7893435aa41160dd1255c43cb8498279738ccChris Craik 12cef7893435aa41160dd1255c43cb8498279738ccChris Craik 13cef7893435aa41160dd1255c43cb8498279738ccChris Craikclass LockException(Exception): 14cef7893435aa41160dd1255c43cb8498279738ccChris Craik pass 15cef7893435aa41160dd1255c43cb8498279738ccChris Craik 16cef7893435aa41160dd1255c43cb8498279738ccChris Craik 17cef7893435aa41160dd1255c43cb8498279738ccChris Craikif os.name == 'nt': 18cef7893435aa41160dd1255c43cb8498279738ccChris Craik import win32con # pylint: disable=import-error 19cef7893435aa41160dd1255c43cb8498279738ccChris Craik import win32file # pylint: disable=import-error 20cef7893435aa41160dd1255c43cb8498279738ccChris Craik import pywintypes # pylint: disable=import-error 21cef7893435aa41160dd1255c43cb8498279738ccChris Craik LOCK_EX = win32con.LOCKFILE_EXCLUSIVE_LOCK 22cef7893435aa41160dd1255c43cb8498279738ccChris Craik LOCK_SH = 0 # the default 23cef7893435aa41160dd1255c43cb8498279738ccChris Craik LOCK_NB = win32con.LOCKFILE_FAIL_IMMEDIATELY 24cef7893435aa41160dd1255c43cb8498279738ccChris Craik _OVERLAPPED = pywintypes.OVERLAPPED() 25cef7893435aa41160dd1255c43cb8498279738ccChris Craikelif os.name == 'posix': 26cef7893435aa41160dd1255c43cb8498279738ccChris Craik import fcntl # pylint: disable=import-error 27cef7893435aa41160dd1255c43cb8498279738ccChris Craik LOCK_EX = fcntl.LOCK_EX 28cef7893435aa41160dd1255c43cb8498279738ccChris Craik LOCK_SH = fcntl.LOCK_SH 29cef7893435aa41160dd1255c43cb8498279738ccChris Craik LOCK_NB = fcntl.LOCK_NB 30cef7893435aa41160dd1255c43cb8498279738ccChris Craik 31cef7893435aa41160dd1255c43cb8498279738ccChris Craik 32cef7893435aa41160dd1255c43cb8498279738ccChris Craik@contextlib.contextmanager 33cef7893435aa41160dd1255c43cb8498279738ccChris Craikdef FileLock(target_file, flags): 34cef7893435aa41160dd1255c43cb8498279738ccChris Craik """ Lock the target file. Similar to AcquireFileLock but allow user to write: 35cef7893435aa41160dd1255c43cb8498279738ccChris Craik with FileLock(f, LOCK_EX): 36cef7893435aa41160dd1255c43cb8498279738ccChris Craik ...do stuff on file f without worrying about race condition 37cef7893435aa41160dd1255c43cb8498279738ccChris Craik Args: see AcquireFileLock's documentation. 38cef7893435aa41160dd1255c43cb8498279738ccChris Craik """ 39cef7893435aa41160dd1255c43cb8498279738ccChris Craik AcquireFileLock(target_file, flags) 40cef7893435aa41160dd1255c43cb8498279738ccChris Craik try: 41cef7893435aa41160dd1255c43cb8498279738ccChris Craik yield 42cef7893435aa41160dd1255c43cb8498279738ccChris Craik finally: 43cef7893435aa41160dd1255c43cb8498279738ccChris Craik ReleaseFileLock(target_file) 44cef7893435aa41160dd1255c43cb8498279738ccChris Craik 45cef7893435aa41160dd1255c43cb8498279738ccChris Craik 46cef7893435aa41160dd1255c43cb8498279738ccChris Craikdef AcquireFileLock(target_file, flags): 47cef7893435aa41160dd1255c43cb8498279738ccChris Craik """ Lock the target file. Note that if |target_file| is closed, the lock is 48cef7893435aa41160dd1255c43cb8498279738ccChris Craik automatically released. 49cef7893435aa41160dd1255c43cb8498279738ccChris Craik Args: 50cef7893435aa41160dd1255c43cb8498279738ccChris Craik target_file: file handle of the file to acquire lock. 51cef7893435aa41160dd1255c43cb8498279738ccChris Craik flags: can be any of the type LOCK_EX, LOCK_SH, LOCK_NB, or a bitwise 52cef7893435aa41160dd1255c43cb8498279738ccChris Craik OR combination of flags. 53cef7893435aa41160dd1255c43cb8498279738ccChris Craik """ 54cef7893435aa41160dd1255c43cb8498279738ccChris Craik assert flags in ( 55cef7893435aa41160dd1255c43cb8498279738ccChris Craik LOCK_EX, LOCK_SH, LOCK_NB, LOCK_EX | LOCK_NB, LOCK_SH | LOCK_NB) 56cef7893435aa41160dd1255c43cb8498279738ccChris Craik if os.name == 'nt': 57cef7893435aa41160dd1255c43cb8498279738ccChris Craik _LockImplWin(target_file, flags) 58cef7893435aa41160dd1255c43cb8498279738ccChris Craik elif os.name == 'posix': 59cef7893435aa41160dd1255c43cb8498279738ccChris Craik _LockImplPosix(target_file, flags) 60cef7893435aa41160dd1255c43cb8498279738ccChris Craik else: 61cef7893435aa41160dd1255c43cb8498279738ccChris Craik raise NotImplementedError('%s is not supported' % os.name) 62cef7893435aa41160dd1255c43cb8498279738ccChris Craik 63cef7893435aa41160dd1255c43cb8498279738ccChris Craik 64cef7893435aa41160dd1255c43cb8498279738ccChris Craikdef ReleaseFileLock(target_file): 65cef7893435aa41160dd1255c43cb8498279738ccChris Craik """ Unlock the target file. 66cef7893435aa41160dd1255c43cb8498279738ccChris Craik Args: 67cef7893435aa41160dd1255c43cb8498279738ccChris Craik target_file: file handle of the file to release the lock. 68cef7893435aa41160dd1255c43cb8498279738ccChris Craik """ 69cef7893435aa41160dd1255c43cb8498279738ccChris Craik if os.name == 'nt': 70cef7893435aa41160dd1255c43cb8498279738ccChris Craik _UnlockImplWin(target_file) 71cef7893435aa41160dd1255c43cb8498279738ccChris Craik elif os.name == 'posix': 72cef7893435aa41160dd1255c43cb8498279738ccChris Craik _UnlockImplPosix(target_file) 73cef7893435aa41160dd1255c43cb8498279738ccChris Craik else: 74cef7893435aa41160dd1255c43cb8498279738ccChris Craik raise NotImplementedError('%s is not supported' % os.name) 75cef7893435aa41160dd1255c43cb8498279738ccChris Craik 76cef7893435aa41160dd1255c43cb8498279738ccChris Craik# These implementations are based on 77cef7893435aa41160dd1255c43cb8498279738ccChris Craik# http://code.activestate.com/recipes/65203/ 78cef7893435aa41160dd1255c43cb8498279738ccChris Craik 79cef7893435aa41160dd1255c43cb8498279738ccChris Craikdef _LockImplWin(target_file, flags): 80cef7893435aa41160dd1255c43cb8498279738ccChris Craik hfile = win32file._get_osfhandle(target_file.fileno()) 81cef7893435aa41160dd1255c43cb8498279738ccChris Craik try: 82cef7893435aa41160dd1255c43cb8498279738ccChris Craik win32file.LockFileEx(hfile, flags, 0, -0x10000, _OVERLAPPED) 83cef7893435aa41160dd1255c43cb8498279738ccChris Craik except pywintypes.error, exc_value: 84cef7893435aa41160dd1255c43cb8498279738ccChris Craik if exc_value[0] == 33: 85cef7893435aa41160dd1255c43cb8498279738ccChris Craik raise LockException('Error trying acquiring lock of %s: %s' % 86cef7893435aa41160dd1255c43cb8498279738ccChris Craik (target_file.name, exc_value[2])) 87cef7893435aa41160dd1255c43cb8498279738ccChris Craik else: 88cef7893435aa41160dd1255c43cb8498279738ccChris Craik raise 89cef7893435aa41160dd1255c43cb8498279738ccChris Craik 90cef7893435aa41160dd1255c43cb8498279738ccChris Craik 91cef7893435aa41160dd1255c43cb8498279738ccChris Craikdef _UnlockImplWin(target_file): 92cef7893435aa41160dd1255c43cb8498279738ccChris Craik hfile = win32file._get_osfhandle(target_file.fileno()) 93cef7893435aa41160dd1255c43cb8498279738ccChris Craik try: 94cef7893435aa41160dd1255c43cb8498279738ccChris Craik win32file.UnlockFileEx(hfile, 0, -0x10000, _OVERLAPPED) 95cef7893435aa41160dd1255c43cb8498279738ccChris Craik except pywintypes.error, exc_value: 96cef7893435aa41160dd1255c43cb8498279738ccChris Craik if exc_value[0] == 158: 97cef7893435aa41160dd1255c43cb8498279738ccChris Craik # error: (158, 'UnlockFileEx', 'The segment is already unlocked.') 98cef7893435aa41160dd1255c43cb8498279738ccChris Craik # To match the 'posix' implementation, silently ignore this error 99cef7893435aa41160dd1255c43cb8498279738ccChris Craik pass 100cef7893435aa41160dd1255c43cb8498279738ccChris Craik else: 101cef7893435aa41160dd1255c43cb8498279738ccChris Craik # Q: Are there exceptions/codes we should be dealing with here? 102cef7893435aa41160dd1255c43cb8498279738ccChris Craik raise 103cef7893435aa41160dd1255c43cb8498279738ccChris Craik 104cef7893435aa41160dd1255c43cb8498279738ccChris Craik 105cef7893435aa41160dd1255c43cb8498279738ccChris Craikdef _LockImplPosix(target_file, flags): 106cef7893435aa41160dd1255c43cb8498279738ccChris Craik try: 107cef7893435aa41160dd1255c43cb8498279738ccChris Craik fcntl.flock(target_file.fileno(), flags) 108cef7893435aa41160dd1255c43cb8498279738ccChris Craik except IOError, exc_value: 109cef7893435aa41160dd1255c43cb8498279738ccChris Craik if exc_value[0] == 11 or exc_value[0] == 35: 110cef7893435aa41160dd1255c43cb8498279738ccChris Craik raise LockException('Error trying acquiring lock of %s: %s' % 111cef7893435aa41160dd1255c43cb8498279738ccChris Craik (target_file.name, exc_value[1])) 112cef7893435aa41160dd1255c43cb8498279738ccChris Craik else: 113cef7893435aa41160dd1255c43cb8498279738ccChris Craik raise 114cef7893435aa41160dd1255c43cb8498279738ccChris Craik 115cef7893435aa41160dd1255c43cb8498279738ccChris Craik 116cef7893435aa41160dd1255c43cb8498279738ccChris Craikdef _UnlockImplPosix(target_file): 117cef7893435aa41160dd1255c43cb8498279738ccChris Craik fcntl.flock(target_file.fileno(), fcntl.LOCK_UN) 118