~justin-fathomdb/nova/justinsb-openstack-api-volumes

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/python/lockfile.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: twisted.test.test_lockfile -*-
 
2
# Copyright (c) 2005 Divmod, Inc.
 
3
# Copyright (c) 2008 Twisted Matrix Laboratories.
 
4
# See LICENSE for details.
 
5
 
 
6
"""
 
7
Filesystem-based interprocess mutex.
 
8
"""
 
9
 
 
10
__metaclass__ = type
 
11
 
 
12
import errno, os
 
13
 
 
14
from time import time as _uniquefloat
 
15
 
 
16
def unique():
 
17
    return str(long(_uniquefloat() * 1000))
 
18
 
 
19
from os import rename
 
20
try:
 
21
    from os import kill
 
22
    from os import symlink
 
23
    from os import readlink
 
24
    from os import remove as rmlink
 
25
    _windows = False
 
26
except:
 
27
    _windows = True
 
28
 
 
29
    try:
 
30
        from win32api import OpenProcess
 
31
        import pywintypes
 
32
    except ImportError:
 
33
        kill = None
 
34
    else:
 
35
        ERROR_ACCESS_DENIED = 5
 
36
        ERROR_INVALID_PARAMETER = 87
 
37
 
 
38
        def kill(pid, signal):
 
39
            try:
 
40
                OpenProcess(0, 0, pid)
 
41
            except pywintypes.error, e:
 
42
                if e.args[0] == ERROR_ACCESS_DENIED:
 
43
                    return
 
44
                elif e.args[0] == ERROR_INVALID_PARAMETER:
 
45
                    raise OSError(errno.ESRCH, None)
 
46
                raise
 
47
            else:
 
48
                raise RuntimeError("OpenProcess is required to fail.")
 
49
 
 
50
    _open = file
 
51
 
 
52
    # XXX Implement an atomic thingamajig for win32
 
53
    def symlink(value, filename):
 
54
        newlinkname = filename+"."+unique()+'.newlink'
 
55
        newvalname = os.path.join(newlinkname,"symlink")
 
56
        os.mkdir(newlinkname)
 
57
        f = _open(newvalname,'wcb')
 
58
        f.write(value)
 
59
        f.flush()
 
60
        f.close()
 
61
        try:
 
62
            rename(newlinkname, filename)
 
63
        except:
 
64
            os.remove(newvalname)
 
65
            os.rmdir(newlinkname)
 
66
            raise
 
67
 
 
68
    def readlink(filename):
 
69
        try:
 
70
            fObj = _open(os.path.join(filename,'symlink'), 'rb')
 
71
        except IOError, e:
 
72
            if e.errno == errno.ENOENT or e.errno == errno.EIO:
 
73
                raise OSError(e.errno, None)
 
74
            raise
 
75
        else:
 
76
            result = fObj.read()
 
77
            fObj.close()
 
78
            return result
 
79
 
 
80
    def rmlink(filename):
 
81
        os.remove(os.path.join(filename, 'symlink'))
 
82
        os.rmdir(filename)
 
83
 
 
84
 
 
85
 
 
86
class FilesystemLock:
 
87
    """
 
88
    A mutex.
 
89
 
 
90
    This relies on the filesystem property that creating
 
91
    a symlink is an atomic operation and that it will
 
92
    fail if the symlink already exists.  Deleting the
 
93
    symlink will release the lock.
 
94
 
 
95
    @ivar name: The name of the file associated with this lock.
 
96
 
 
97
    @ivar clean: Indicates whether this lock was released cleanly by its
 
98
        last owner.  Only meaningful after C{lock} has been called and
 
99
        returns True.
 
100
 
 
101
    @ivar locked: Indicates whether the lock is currently held by this
 
102
        object.
 
103
    """
 
104
 
 
105
    clean = None
 
106
    locked = False
 
107
 
 
108
    def __init__(self, name):
 
109
        self.name = name
 
110
 
 
111
 
 
112
    def lock(self):
 
113
        """
 
114
        Acquire this lock.
 
115
 
 
116
        @rtype: C{bool}
 
117
        @return: True if the lock is acquired, false otherwise.
 
118
 
 
119
        @raise: Any exception os.symlink() may raise, other than
 
120
        EEXIST.
 
121
        """
 
122
        clean = True
 
123
        while True:
 
124
            try:
 
125
                symlink(str(os.getpid()), self.name)
 
126
            except OSError, e:
 
127
                if _windows and e.errno in (errno.EACCES, errno.EIO):
 
128
                    # The lock is in the middle of being deleted because we're
 
129
                    # on Windows where lock removal isn't atomic.  Give up, we
 
130
                    # don't know how long this is going to take.
 
131
                    return False
 
132
                if e.errno == errno.EEXIST:
 
133
                    try:
 
134
                        pid = readlink(self.name)
 
135
                    except OSError, e:
 
136
                        if e.errno == errno.ENOENT:
 
137
                            # The lock has vanished, try to claim it in the
 
138
                            # next iteration through the loop.
 
139
                            continue
 
140
                        raise
 
141
                    except IOError, e:
 
142
                        if _windows and e.errno == errno.EACCES:
 
143
                            # The lock is in the middle of being
 
144
                            # deleted because we're on Windows where
 
145
                            # lock removal isn't atomic.  Give up, we
 
146
                            # don't know how long this is going to
 
147
                            # take.
 
148
                            return False
 
149
                        raise
 
150
                    try:
 
151
                        if kill is not None:
 
152
                            kill(int(pid), 0)
 
153
                    except OSError, e:
 
154
                        if e.errno == errno.ESRCH:
 
155
                            # The owner has vanished, try to claim it in the next
 
156
                            # iteration through the loop.
 
157
                            try:
 
158
                                rmlink(self.name)
 
159
                            except OSError, e:
 
160
                                if e.errno == errno.ENOENT:
 
161
                                    # Another process cleaned up the lock.
 
162
                                    # Race them to acquire it in the next
 
163
                                    # iteration through the loop.
 
164
                                    continue
 
165
                                raise
 
166
                            clean = False
 
167
                            continue
 
168
                        raise
 
169
                    return False
 
170
                raise
 
171
            self.locked = True
 
172
            self.clean = clean
 
173
            return True
 
174
 
 
175
 
 
176
    def unlock(self):
 
177
        """
 
178
        Release this lock.
 
179
 
 
180
        This deletes the directory with the given name.
 
181
 
 
182
        @raise: Any exception os.readlink() may raise, or
 
183
        ValueError if the lock is not owned by this process.
 
184
        """
 
185
        pid = readlink(self.name)
 
186
        if int(pid) != os.getpid():
 
187
            raise ValueError("Lock %r not owned by this process" % (self.name,))
 
188
        rmlink(self.name)
 
189
        self.locked = False
 
190
 
 
191
 
 
192
def isLocked(name):
 
193
    """Determine if the lock of the given name is held or not.
 
194
 
 
195
    @type name: C{str}
 
196
    @param name: The filesystem path to the lock to test
 
197
 
 
198
    @rtype: C{bool}
 
199
    @return: True if the lock is held, False otherwise.
 
200
    """
 
201
    l = FilesystemLock(name)
 
202
    result = None
 
203
    try:
 
204
        result = l.lock()
 
205
    finally:
 
206
        if result:
 
207
            l.unlock()
 
208
    return not result
 
209
 
 
210
 
 
211
__all__ = ['FilesystemLock', 'isLocked']
 
212