4 # Copyright (C) 2006, 2007, 2010, 2011 Google Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 """Utility functions for file-based locks.
30 from ganeti import errors
31 from ganeti.utils import retry
35 """Locks a file using POSIX locks.
38 @param fd: the file descriptor we need to lock
42 fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
44 if err.errno == errno.EAGAIN:
45 raise errors.LockError("File already locked")
49 class FileLock(object):
50 """Utility class for file locks.
53 def __init__(self, fd, filename):
54 """Constructor for FileLock.
57 @param fd: File object
59 @param filename: Path of the file opened at I{fd}
63 self.filename = filename
66 def Open(cls, filename):
67 """Creates and opens a file to be used as a file-based lock.
69 @type filename: string
70 @param filename: path to the file to be locked
73 # Using "os.open" is necessary to allow both opening existing file
74 # read/write and creating if not existing. Vanilla "open" will truncate an
75 # existing file -or- allow creating if not existing.
76 return cls(os.fdopen(os.open(filename, os.O_RDWR | os.O_CREAT, 0664), "w+"),
83 """Close the file and release the lock.
86 if hasattr(self, "fd") and self.fd:
90 def _flock(self, flag, blocking, timeout, errmsg):
91 """Wrapper for fcntl.flock.
94 @param flag: operation flag
96 @param blocking: whether the operation should be done in blocking mode.
97 @type timeout: None or float
98 @param timeout: for how long the operation should be retried (implies
101 @param errmsg: error message in case operation fails.
104 assert self.fd, "Lock was closed"
105 assert timeout is None or timeout >= 0, \
106 "If specified, timeout must be positive"
107 assert not (flag & fcntl.LOCK_NB), "LOCK_NB must not be set"
109 # When a timeout is used, LOCK_NB must always be set
110 if not (timeout is None and blocking):
111 flag |= fcntl.LOCK_NB
114 self._Lock(self.fd, flag, timeout)
117 retry.Retry(self._Lock, (0.1, 1.2, 1.0), timeout,
118 args=(self.fd, flag, timeout))
119 except retry.RetryTimeout:
120 raise errors.LockError(errmsg)
123 def _Lock(fd, flag, timeout):
125 fcntl.flock(fd, flag)
127 if timeout is not None and err.errno == errno.EAGAIN:
128 raise retry.RetryAgain()
130 logging.exception("fcntl.flock failed")
133 def Exclusive(self, blocking=False, timeout=None):
134 """Locks the file in exclusive mode.
136 @type blocking: boolean
137 @param blocking: whether to block and wait until we
138 can lock the file or return immediately
139 @type timeout: int or None
140 @param timeout: if not None, the duration to wait for the lock
144 self._flock(fcntl.LOCK_EX, blocking, timeout,
145 "Failed to lock %s in exclusive mode" % self.filename)
147 def Shared(self, blocking=False, timeout=None):
148 """Locks the file in shared mode.
150 @type blocking: boolean
151 @param blocking: whether to block and wait until we
152 can lock the file or return immediately
153 @type timeout: int or None
154 @param timeout: if not None, the duration to wait for the lock
158 self._flock(fcntl.LOCK_SH, blocking, timeout,
159 "Failed to lock %s in shared mode" % self.filename)
161 def Unlock(self, blocking=True, timeout=None):
164 According to C{flock(2)}, unlocking can also be a nonblocking
167 To make a non-blocking request, include LOCK_NB with any of the above
170 @type blocking: boolean
171 @param blocking: whether to block and wait until we
172 can lock the file or return immediately
173 @type timeout: int or None
174 @param timeout: if not None, the duration to wait for the lock
178 self._flock(fcntl.LOCK_UN, blocking, timeout,
179 "Failed to unlock %s" % self.filename)