Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(302)

Side by Side Diff: infra/libs/service_utils/daemon.py

Issue 1096683003: Add flock and timeout to infra/libs. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | infra/libs/service_utils/test/daemon_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
OLDNEW
« no previous file with comments | « no previous file | infra/libs/service_utils/test/daemon_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698