blob: 629e7a4fe322efdaea4fc422926aff9b91d42a93 [file] [log] [blame]
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Locking, timeout, and other process management functions."""
import contextlib
import ctypes
import ctypes.wintypes
import os
import tempfile
GENERIC_WRITE = 0x40000000
CREATE_ALWAYS = 2
FILE_FLAG_DELETE_ON_CLOSE = 0x04000000
INVALID_HANDLE_VALUE = ctypes.wintypes.HANDLE(-1)
ERROR_SHARING_VIOLATION = 32
CreateFile = ctypes.windll.kernel32.CreateFileW
CreateFile.argtypes = [
ctypes.wintypes.LPWSTR,
ctypes.wintypes.DWORD,
ctypes.wintypes.DWORD,
ctypes.wintypes.LPVOID,
ctypes.wintypes.DWORD,
ctypes.wintypes.DWORD,
ctypes.wintypes.HANDLE
]
CreateFile.restype = ctypes.wintypes.HANDLE
GetLastError = ctypes.windll.kernel32.GetLastError
GetLastError.restype = ctypes.wintypes.DWORD
CloseHandle = ctypes.windll.kernel32.CloseHandle
CloseHandle.argtypes = [ctypes.wintypes.HANDLE]
CloseHandle.restype = ctypes.wintypes.BOOL
class LockAlreadyLocked(RuntimeError):
"""Exception used when a lock couldn't be acquired."""
pass
class WindowsError(Exception):
"""Error code returned from a windows API call.
Values are documented here:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms681381(v=vs.85).aspx
"""
pass
@contextlib.contextmanager
def flock(lockfile, lockdir=None):
"""Keeps a critical section from executing concurrently using a file lock.
Example usage:
try:
with daemon.flock('toaster'):
put_bread_in_toaster()
except daemon.LockAlreadyLocked:
print 'toaster is occupied!'
"""
lockdir = lockdir or tempfile.gettempdir()
full_lockfile = os.path.join(lockdir, lockfile)
handle = ctypes.wintypes.HANDLE(CreateFile(
full_lockfile,
GENERIC_WRITE,
0, # shareMode - this is the important argument for exclusive locking.
None,
CREATE_ALWAYS,
FILE_FLAG_DELETE_ON_CLOSE,
0))
if handle.value == INVALID_HANDLE_VALUE.value:
errno = GetLastError()
if errno == ERROR_SHARING_VIOLATION:
raise LockAlreadyLocked()
raise WindowsError(errno)
try:
yield
finally:
CloseHandle(handle)
def add_timeout(_cmd, _timeout_secs): # pragma: no cover
raise NotImplementedError
def close_all_fds(_keep_fds): # pragma: no cover
raise NotImplementedError
def become_daemon(_keep_fds=None): # pragma: no cover
raise NotImplementedError