~ubuntu-branches/ubuntu/natty/moin/natty-updates

« back to all changes in this revision

Viewing changes to MoinMoin/util/lock.py

  • Committer: Bazaar Package Importer
  • Author(s): Jonas Smedegaard
  • Date: 2008-06-22 21:17:13 UTC
  • mfrom: (0.9.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20080622211713-fpo2zrq3s5dfecxg
Tags: 1.7.0-3
Simplify /etc/moin/wikilist format: "USER URL" (drop unneeded middle
CONFIG_DIR that was wrongly advertised as DATA_DIR).  Make
moin-mass-migrate handle both formats and warn about deprecation of
the old one.

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
"""
3
3
    MoinMoin - locking functions
4
4
 
5
 
    @copyright: 2005 by Florian Festi, Nir Soffer
 
5
    @copyright: 2005 Florian Festi, Nir Soffer
6
6
    @license: GNU GPL, see COPYING for details.
7
7
"""
8
8
 
9
9
import os, sys, tempfile, time, errno
10
10
 
11
11
 
12
 
# Temporary debugging aid, to be replaced with system wide debuging
13
 
# in release 3000.
14
 
import sys
15
 
def log(msg):
16
 
    sys.stderr.write('[%s] lock: %s' % (time.asctime(), msg))
17
 
 
18
 
 
19
12
class Timer:
20
13
    """ Simple count down timer
21
 
    
 
14
 
22
15
    Useful for code that needs to complete a task within some timeout.
23
16
    """
24
17
    defaultSleep = 0.25
25
18
    maxSleep = 0.25
26
 
    
 
19
 
27
20
    def __init__(self, timeout):
28
21
        self.setTimeout(timeout)
29
22
        self._start = None
35
28
            self._sleep = self.defaultSleep
36
29
        else:
37
30
            self._sleep = min(timeout / 10.0, self.maxSleep)
38
 
        
 
31
 
39
32
    def start(self):
40
33
        """ Start the countdown """
41
34
        if self.timeout is None:
53
46
    def sleep(self):
54
47
        """ Sleep without sleeping over timeout """
55
48
        if self._stop is not None:
56
 
            timeLeft = max(self._stop - time.time(), 0)            
 
49
            timeLeft = max(self._stop - time.time(), 0)
57
50
            sleep = min(self._sleep, timeLeft)
58
51
        else:
59
52
            sleep = self._sleep
62
55
    def elapsed(self):
63
56
        return time.time() - self._start
64
57
 
65
 
    
 
58
 
66
59
class ExclusiveLock:
67
60
    """ Exclusive lock
68
 
    
 
61
 
69
62
    Uses a directory as portable lock method. On all platforms,
70
63
    creating a directory will fail if the directory exists.
71
64
 
77
70
    """
78
71
    fileName = '' # The directory is the lockDir
79
72
    timerClass = Timer
80
 
    
 
73
 
81
74
    def __init__(self, dir, timeout=None):
82
75
        """ Init a write lock
83
 
        
 
76
 
84
77
        @param dir: the lock directory. Since this lock uses a empty
85
78
            filename, the dir is the lockDir.
86
79
        @param timeout: while trying to acquire, the lock will expire
96
89
            self.lockDir = os.path.join(dir, self.fileName)
97
90
            self._makeDir()
98
91
        else:
99
 
            self.lockDir = dir            
 
92
            self.lockDir = dir
100
93
        self._locked = False
101
94
 
102
95
    def acquire(self, timeout=None):
103
 
        """ Try to acquire a lock. 
104
 
        
 
96
        """ Try to acquire a lock.
 
97
 
105
98
        Try to create the lock directory. If it fails because another
106
99
        lock exists, try to expire the other lock. Repeat after little
107
100
        sleep until timeout passed.
139
132
        return os.path.exists(self.lockDir)
140
133
 
141
134
    def isExpired(self):
142
 
        """ Return True if too old or missing; False otherwise 
143
 
        
 
135
        """ Return True if too old or missing; False otherwise
 
136
 
144
137
        TODO: Since stat returns times using whole seconds, this is
145
138
        quite broken. Maybe use OS specific calls like Carbon.File on
146
139
        Mac OS X?
165
158
        return False
166
159
 
167
160
    # Private -------------------------------------------------------
168
 
    
 
161
 
169
162
    def _makeDir(self):
170
163
        """ Make sure directory exists """
171
164
        try:
180
173
        try:
181
174
            os.rmdir(self.lockDir)
182
175
        except OSError, err:
183
 
            if err.errno != errno.ENOENT: 
 
176
            if err.errno != errno.ENOENT:
184
177
                raise
185
178
 
186
179
 
187
180
class WriteLock(ExclusiveLock):
188
181
    """ Exclusive Read/Write Lock
189
 
    
 
182
 
190
183
    When a resource is locked with this lock, clients can't read
191
184
    or write the resource.
192
 
    
 
185
 
193
186
    This super-exclusive lock can't be acquired if there are any other
194
187
    locks, either WriteLock or ReadLocks. When trying to acquire, this
195
188
    lock will try to expire all existing ReadLocks.
196
189
    """
197
190
    fileName = 'write_lock'
198
 
    
 
191
 
199
192
    def __init__(self, dir, timeout=None, readlocktimeout=None):
200
193
        """ Init a write lock
201
 
        
 
194
 
202
195
        @param dir: the lock directory. Every resource should have one
203
196
            lock directory, which may contain read or write locks.
204
197
        @param timeout: while trying to acquire, the lock will expire
211
204
            self.readlocktimeout = timeout
212
205
        else:
213
206
            self.readlocktimeout = readlocktimeout
214
 
    
 
207
 
215
208
    def acquire(self, timeout=None):
216
209
        """ Acquire an exclusive write lock
217
 
        
 
210
 
218
211
        Try to acquire an exclusive lock, then try to expire existing
219
212
        read locks. If timeout has not passed, the lock is acquired.
220
213
        Otherwise, the exclusive lock is released and the lock is not
221
214
        acquired.
222
 
        
 
215
 
223
216
        Return True if lock acquired, False otherwise.
224
 
        """        
 
217
        """
225
218
        if self._locked:
226
219
            raise RuntimeError("lock already locked")
227
220
        result = False
242
235
                else:
243
236
                    self.release()
244
237
        return False
245
 
        
 
238
 
246
239
    # Private -------------------------------------------------------
247
 
    
 
240
 
248
241
    def _expireReadLocks(self):
249
242
        """ Expire old read locks """
250
243
        readLockFileName = ReadLock.fileName
255
248
            ExclusiveLock(LockDir, self.readlocktimeout).expire()
256
249
 
257
250
    def _haveReadLocks(self):
258
 
        """ Return True if read locks exists; False otherwise """            
 
251
        """ Return True if read locks exists; False otherwise """
259
252
        readLockFileName = ReadLock.fileName
260
253
        for name in os.listdir(self.dir):
261
254
            if name.startswith(readLockFileName):
264
257
 
265
258
class ReadLock(ExclusiveLock):
266
259
    """ Read lock
267
 
    
 
260
 
268
261
    The purpose of this lock is to mark the resource as read only.
269
262
    Multiple ReadLocks can be acquired for same resource, but no
270
263
    WriteLock can be acquired until all ReadLocks are released.
271
 
    
 
264
 
272
265
    Allows only one lock per instance.
273
266
    """
274
267
    fileName = 'read_lock_'
275
 
            
 
268
 
276
269
    def __init__(self, dir, timeout=None):
277
270
        """ Init a read lock
278
 
        
 
271
 
279
272
        @param dir: the lock directory. Every resource should have one
280
273
            lock directory, which may contain read or write locks.
281
274
        @param timeout: while trying to acquire, the lock will expire
287
280
 
288
281
    def acquire(self, timeout=None):
289
282
        """ Try to acquire a 'read' lock
290
 
        
 
283
 
291
284
        To prevent race conditions, acquire first an exclusive lock,
292
285
        then acquire a read lock. Finally release the exclusive lock so
293
 
        other can have read lock, too. 
 
286
        other can have read lock, too.
294
287
        """
295
288
        if self._locked:
296
289
            raise RuntimeError("lock already locked")
301
294
                # log('acquired read lock: %s\n' % self.lockDir)
302
295
                return True
303
296
            finally:
304
 
                self.writeLock.release()       
 
297
                self.writeLock.release()
305
298
        return False
306
299
 
307
300
 
308
301
class LazyReadLock(ReadLock):
309
302
    """ Lazy Read lock
310
 
    
 
303
 
311
304
    See ReadLock, but we do an optimization here:
312
305
    If (and ONLY if) the resource protected by this lock is updated in a POSIX
313
306
    style "write new content to tmpfile, rename tmpfile -> origfile", then reading
361
354
 
362
355
class LazyWriteLock(WriteLock):
363
356
    """ Lazy Write lock
364
 
    
 
357
 
365
358
    See WriteLock and LazyReadLock docs.
366
359
    """
367
360
    def __init__(self, dir, timeout=None):