Coverage for drivers/flock.py : 65%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1#
2# Copyright (C) Citrix Systems Inc.
3#
4# This program is free software; you can redistribute it and/or modify
5# it under the terms of the GNU Lesser General Public License as published
6# by the Free Software Foundation; version 2.1 only.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU Lesser General Public License for more details.
12#
13# You should have received a copy of the GNU Lesser General Public License
14# along with this program; if not, write to the Free Software Foundation, Inc.,
15# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16#
18"""
19Fcntl-based Advisory Locking with a proper .trylock()
21Python's fcntl module is not good at locking. In particular, proper
22testing and trying of locks isn't well supported. Looks as if we've
23got to grow our own.
24"""
26import os
27import fcntl
28import struct
29import errno
32class Flock:
33 """A C flock struct."""
35 def __init__(self, l_type, l_whence=0, l_start=0, l_len=0, l_pid=0):
36 """See fcntl(2) for field details."""
37 self.fields = [l_type, l_whence, l_start, l_len, l_pid]
39 FORMAT = "hhqql"
40 # struct flock(2) format, tested with python2.4/i686 and
41 # python2.5/x86_64. http://docs.python.org/lib/posix-large-files.html
43 def fcntl(self, fd, cmd):
44 """Issues a system fcntl(fd, cmd, self). Updates self with what was
45 returned by the kernel. Otherwise raises IOError(errno)."""
47 st = struct.pack(self.FORMAT, * self.fields)
48 st = fcntl.fcntl(fd, cmd, st)
50 fields = struct.unpack(self.FORMAT, st)
51 self.__init__( * fields)
53 FIELDS = {'l_type': 0,
54 'l_whence': 1,
55 'l_start': 2,
56 'l_len': 3,
57 'l_pid': 4}
59 def __getattr__(self, name):
60 idx = self.FIELDS[name]
61 return self.fields[idx]
63 def __setattr__(self, name, value):
64 idx = self.FIELDS.get(name)
65 if idx is None: 65 ↛ 68line 65 didn't jump to line 68, because the condition on line 65 was never false
66 self.__dict__[name] = value
67 else:
68 self.fields[idx] = value
71class FcntlLockBase:
72 """Abstract base class for either reader or writer locks. A respective
73 definition of LOCK_TYPE (fcntl.{F_RDLCK|F_WRLCK}) determines the
74 type."""
76 LOCK_TYPE = None
78 if __debug__:
79 ERROR_ISLOCKED = "Attempt to acquire lock held."
80 ERROR_NOTLOCKED = "Attempt to unlock lock not held."
82 def __init__(self, fd):
83 """Creates a new, unheld lock."""
84 self.fd = fd
85 #
86 # Subtle: fcntl(2) permits re-locking it as often as you want
87 # once you hold it. This is slightly counterintuitive and we
88 # want clean code, so we add one bit of our own bookkeeping.
89 #
90 self._held = False
92 def lock(self):
93 """Blocking lock aquisition."""
94 assert not self._held, self.ERROR_ISLOCKED
95 Flock(self.LOCK_TYPE).fcntl(self.fd, fcntl.F_SETLKW)
96 self._held = True
98 def trylock(self):
99 """Non-blocking lock aquisition. Returns True on success, False
100 otherwise."""
101 if self._held: 101 ↛ 102line 101 didn't jump to line 102, because the condition on line 101 was never true
102 return False
103 try:
104 Flock(self.LOCK_TYPE).fcntl(self.fd, fcntl.F_SETLK)
105 except IOError as e:
106 if e.errno in [errno.EACCES, errno.EAGAIN]:
107 return False
108 raise
109 self._held = True
110 return True
112 def held(self):
113 """Returns True if @self holds the lock, False otherwise."""
114 return self._held
116 def unlock(self):
117 """Release a previously acquired lock."""
118 Flock(fcntl.F_UNLCK).fcntl(self.fd, fcntl.F_SETLK)
119 self._held = False
121 def test(self):
122 """Returns the PID of the process holding the lock or -1 if the lock
123 is not held."""
124 if self._held:
125 return os.getpid()
126 flock = Flock(self.LOCK_TYPE)
127 flock.fcntl(self.fd, fcntl.F_GETLK)
128 if flock.l_type == fcntl.F_UNLCK:
129 return -1
130 return flock.l_pid
133class WriteLock(FcntlLockBase):
134 """A simple global writer (i.e. exclusive) lock."""
135 LOCK_TYPE = fcntl.F_WRLCK
138class ReadLock(FcntlLockBase):
139 """A simple global reader (i.e. shared) lock."""
140 LOCK_TYPE = fcntl.F_RDLCK