comparison lockfile-0.10.2/lockfile/pidlockfile.py @ 33:7ceb967147c3

start xena with no gui add library files
author jingchunzhu <jingchunzhu@gmail.com>
date Wed, 22 Jul 2015 13:24:44 -0700
parents
children
comparison
equal deleted inserted replaced
32:63b1ba1e3424 33:7ceb967147c3
1 # -*- coding: utf-8 -*-
2
3 # pidlockfile.py
4 #
5 # Copyright © 2008–2009 Ben Finney <ben+python@benfinney.id.au>
6 #
7 # This is free software: you may copy, modify, and/or distribute this work
8 # under the terms of the Python Software Foundation License, version 2 or
9 # later as published by the Python Software Foundation.
10 # No warranty expressed or implied. See the file LICENSE.PSF-2 for details.
11
12 """ Lockfile behaviour implemented via Unix PID files.
13 """
14
15 from __future__ import absolute_import
16
17 import os
18 import sys
19 import errno
20 import time
21
22 from . import (LockBase, AlreadyLocked, LockFailed, NotLocked, NotMyLock,
23 LockTimeout)
24
25
26 class PIDLockFile(LockBase):
27 """ Lockfile implemented as a Unix PID file.
28
29 The lock file is a normal file named by the attribute `path`.
30 A lock's PID file contains a single line of text, containing
31 the process ID (PID) of the process that acquired the lock.
32
33 >>> lock = PIDLockFile('somefile')
34 >>> lock = PIDLockFile('somefile')
35 """
36
37 def __init__(self, path, threaded=False, timeout=None):
38 # pid lockfiles don't support threaded operation, so always force
39 # False as the threaded arg.
40 LockBase.__init__(self, path, False, timeout)
41 dirname = os.path.dirname(self.lock_file)
42 basename = os.path.split(self.path)[-1]
43 self.unique_name = self.path
44
45 def read_pid(self):
46 """ Get the PID from the lock file.
47 """
48 return read_pid_from_pidfile(self.path)
49
50 def is_locked(self):
51 """ Test if the lock is currently held.
52
53 The lock is held if the PID file for this lock exists.
54
55 """
56 return os.path.exists(self.path)
57
58 def i_am_locking(self):
59 """ Test if the lock is held by the current process.
60
61 Returns ``True`` if the current process ID matches the
62 number stored in the PID file.
63 """
64 return self.is_locked() and os.getpid() == self.read_pid()
65
66 def acquire(self, timeout=None):
67 """ Acquire the lock.
68
69 Creates the PID file for this lock, or raises an error if
70 the lock could not be acquired.
71 """
72
73 timeout = timeout is not None and timeout or self.timeout
74 end_time = time.time()
75 if timeout is not None and timeout > 0:
76 end_time += timeout
77
78 while True:
79 try:
80 write_pid_to_pidfile(self.path)
81 except OSError as exc:
82 if exc.errno == errno.EEXIST:
83 # The lock creation failed. Maybe sleep a bit.
84 if timeout is not None and time.time() > end_time:
85 if timeout > 0:
86 raise LockTimeout("Timeout waiting to acquire"
87 " lock for %s" %
88 self.path)
89 else:
90 raise AlreadyLocked("%s is already locked" %
91 self.path)
92 time.sleep(timeout is not None and timeout/10 or 0.1)
93 else:
94 raise LockFailed("failed to create %s" % self.path)
95 else:
96 return
97
98 def release(self):
99 """ Release the lock.
100
101 Removes the PID file to release the lock, or raises an
102 error if the current process does not hold the lock.
103
104 """
105 if not self.is_locked():
106 raise NotLocked("%s is not locked" % self.path)
107 if not self.i_am_locking():
108 raise NotMyLock("%s is locked, but not by me" % self.path)
109 remove_existing_pidfile(self.path)
110
111 def break_lock(self):
112 """ Break an existing lock.
113
114 Removes the PID file if it already exists, otherwise does
115 nothing.
116
117 """
118 remove_existing_pidfile(self.path)
119
120 def read_pid_from_pidfile(pidfile_path):
121 """ Read the PID recorded in the named PID file.
122
123 Read and return the numeric PID recorded as text in the named
124 PID file. If the PID file cannot be read, or if the content is
125 not a valid PID, return ``None``.
126
127 """
128 pid = None
129 try:
130 pidfile = open(pidfile_path, 'r')
131 except IOError:
132 pass
133 else:
134 # According to the FHS 2.3 section on PID files in /var/run:
135 #
136 # The file must consist of the process identifier in
137 # ASCII-encoded decimal, followed by a newline character.
138 #
139 # Programs that read PID files should be somewhat flexible
140 # in what they accept; i.e., they should ignore extra
141 # whitespace, leading zeroes, absence of the trailing
142 # newline, or additional lines in the PID file.
143
144 line = pidfile.readline().strip()
145 try:
146 pid = int(line)
147 except ValueError:
148 pass
149 pidfile.close()
150
151 return pid
152
153
154 def write_pid_to_pidfile(pidfile_path):
155 """ Write the PID in the named PID file.
156
157 Get the numeric process ID (“PID”) of the current process
158 and write it to the named file as a line of text.
159
160 """
161 open_flags = (os.O_CREAT | os.O_EXCL | os.O_WRONLY)
162 open_mode = 0o644
163 pidfile_fd = os.open(pidfile_path, open_flags, open_mode)
164 pidfile = os.fdopen(pidfile_fd, 'w')
165
166 # According to the FHS 2.3 section on PID files in /var/run:
167 #
168 # The file must consist of the process identifier in
169 # ASCII-encoded decimal, followed by a newline character. For
170 # example, if crond was process number 25, /var/run/crond.pid
171 # would contain three characters: two, five, and newline.
172
173 pid = os.getpid()
174 line = "%(pid)d\n" % vars()
175 pidfile.write(line)
176 pidfile.close()
177
178
179 def remove_existing_pidfile(pidfile_path):
180 """ Remove the named PID file if it exists.
181
182 Removing a PID file that doesn't already exist puts us in the
183 desired state, so we ignore the condition if the file does not
184 exist.
185
186 """
187 try:
188 os.remove(pidfile_path)
189 except OSError as exc:
190 if exc.errno == errno.ENOENT:
191 pass
192 else:
193 raise