Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 # Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 """Locking, timeout, and other process management functions.""" | |
| 6 | |
| 7 import contextlib | |
| 8 import fcntl | |
| 9 import os | |
| 10 import sys | |
| 11 import tempfile | |
| 12 | |
| 13 | |
| 14 @contextlib.contextmanager | |
| 15 def _auto_closing_fd(*args, **kwargs): | |
| 16 """Opens a file, yields its fd, and closes it when done.""" | |
| 17 | |
| 18 fd = os.open(*args, **kwargs) | |
| 19 yield fd | |
| 20 os.close(fd) | |
| 21 | |
| 22 | |
| 23 @contextlib.contextmanager | |
| 24 def flock(lockfile, lockdir=None): | |
| 25 """Keeps a critical section from executing concurrently using a file lock. | |
|
tandrii(chromium)
2015/04/23 13:02:47
You should document that this lock only gives excl
| |
| 26 | |
| 27 Implementation based on http://goo.gl/dNf7fv (see John Mudd's comment) and | |
| 28 http://stackoverflow.com/a/18745264/3984761. This implementation creates the | |
| 29 lockfile if it doesn't exist and removes it when the critical section exits. | |
| 30 It yields True if the lock was acquired, and False if it wasn't. You must | |
| 31 check if the yielded value is True if you want to prevent concurrent critical | |
| 32 sections. | |
| 33 | |
| 34 Example usage: | |
| 35 | |
| 36 with daemon.flock('toaster') as acquired: | |
|
tandrii(chromium)
2015/04/23 13:02:47
IMO, it's more Pythonic to raise exception instead
agable
2015/04/27 18:48:33
So then the construct would be
try:
with daemon
tandrii(chromium)
2015/04/27 21:17:09
Yep, and you don't even need "as acq" part :)
ghost stip (do not use)
2015/04/27 22:03:47
Done.
| |
| 37 if acquired: | |
| 38 put_bread_in_toaster() | |
| 39 else: | |
| 40 print 'toaster is occupied!' | |
| 41 """ | |
| 42 | |
| 43 if sys.platform.startswith('win'): # pragma: no cover | |
| 44 raise NotImplementedError | |
| 45 | |
| 46 lockdir = lockdir or tempfile.gettempdir() | |
| 47 full_lockfile = os.path.join(lockdir, lockfile) | |
| 48 lock_acquired = False | |
| 49 | |
| 50 with _auto_closing_fd( | |
| 51 full_lockfile, os.O_CREAT | os.O_TRUNC | os.O_WRONLY) as fd: | |
| 52 try: | |
| 53 # Request exclusive (EX) non-blocking (NB) advisory lock. | |
| 54 fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) | |
| 55 | |
| 56 lock_acquired = True | |
| 57 except IOError: | |
| 58 # Could not obtain lock. | |
| 59 lock_acquired = False | |
| 60 | |
| 61 if lock_acquired: | |
| 62 try: | |
| 63 held_inode = os.fstat(fd).st_ino | |
| 64 file_inode = os.stat(full_lockfile).st_ino | |
| 65 | |
| 66 if held_inode != file_inode: | |
| 67 # The file was deleted under us, another process has created it again | |
| 68 # and may get a lock on it. That process doesn't know about the lock | |
| 69 # we have on the (now deleted) file, so we need to bail. | |
| 70 lock_acquired = False | |
| 71 except OSError: | |
| 72 # File has been deleted under us. We have to exit because another process | |
| 73 # might try to create it and obtain a lock, not knowing that we had a | |
| 74 # lock on the (now deleted) file. | |
| 75 lock_acquired = False | |
| 76 | |
| 77 yield lock_acquired | |
| 78 | |
| 79 try: | |
| 80 # The order of these two operations is very important. We need to delete | |
| 81 # the file before we release the lock. If we release the lock before we | |
| 82 # delete the file, we run the risk of another process obtaining a lock on | |
| 83 # the file we're about to delete. If the delete happens while the other | |
| 84 # critical section is running, a third process could create the file, get | |
| 85 # a lock on it, and run a second critical section simultaneously. Deleting | |
| 86 # before unlocking prevents this scenario. | |
| 87 os.unlink(full_lockfile) | |
| 88 fcntl.lockf(fd, fcntl.LOCK_UN) | |
| 89 except OSError: | |
| 90 # If the file was deleted for some other reason, don't sweat it. | |
| 91 pass | |
| 92 | |
| 93 | |
| 94 def add_timeout(cmd, timeout_secs): | |
| 95 """Adds a timeout to a command.""" | |
|
agable
2015/04/27 18:48:33
Note that it does so using the linux /bin/timeout
ghost stip (do not use)
2015/04/27 22:03:47
Done.
| |
| 96 | |
| 97 if sys.platform.startswith('win') or sys.platform.startswith('darwin'): | |
| 98 raise NotImplementedError # pragma: no cover | |
| 99 | |
| 100 return ['timeout', str(timeout_secs)] + cmd | |
| OLD | NEW |