~ubuntu-branches/ubuntu/trusty/spyder/trusty-proposed

« back to all changes in this revision

Viewing changes to spyderlib/utils/external/lockfile.py

  • Committer: Package Import Robot
  • Author(s): Picca Frédéric-Emmanuel
  • Date: 2013-05-08 21:12:32 UTC
  • mfrom: (1.2.1) (18.1.5 experimental)
  • Revision ID: package-import@ubuntu.com-20130508211232-r867h9xoenknvuxf
Tags: 2.2.0+dfsg-1
* Imported Upstream version 2.2.0+dfsg
* Depends on ipython-qtconsole (< 1.0)
* Removed the obsolte DM-Upload-Allowed

Show diffs side-by-side

added added

removed removed

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