/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/lockdir.py

  • Committer: Robert Collins
  • Date: 2006-03-03 02:09:49 UTC
  • mto: (1594.2.4 integration)
  • mto: This revision was merged to the branch mainline in revision 1596.
  • Revision ID: robertc@robertcollins.net-20060303020949-0ddc6f33d0a43943
Smoke test for RevisionStore factories creating revision stores.

Show diffs side-by-side

added added

removed removed

Lines of Context:
87
87
>>> # typically will be obtained from a BzrDir, Branch, etc
88
88
>>> t = MemoryTransport()
89
89
>>> l = LockDir(t, 'sample-lock')
90
 
>>> l.create()
91
90
>>> l.wait_lock()
92
91
>>> # do something here
93
92
>>> l.unlock()
96
95
 
97
96
import os
98
97
import time
99
 
from warnings import warn
100
98
from StringIO import StringIO
101
99
 
102
100
import bzrlib.config
109
107
        LockError,
110
108
        LockNotHeld,
111
109
        NoSuchFile,
112
 
        ResourceBusy,
113
110
        UnlockableTransport,
114
111
        )
115
112
from bzrlib.transport import Transport
131
128
# TODO: Some kind of callback run while polling a lock to show progress
132
129
# indicators.
133
130
 
134
 
# TODO: Make sure to pass the right file and directory mode bits to all
135
 
# files/dirs created.
136
 
 
137
131
_DEFAULT_TIMEOUT_SECONDS = 300
138
132
_DEFAULT_POLL_SECONDS = 0.5
139
133
 
142
136
 
143
137
    __INFO_NAME = '/info'
144
138
 
145
 
    def __init__(self, transport, path, file_modebits=0644, dir_modebits=0755):
 
139
    def __init__(self, transport, path):
146
140
        """Create a new LockDir object.
147
141
 
148
142
        The LockDir is initially unlocked - this just creates the object.
157
151
        self.transport = transport
158
152
        self.path = path
159
153
        self._lock_held = False
160
 
        self._fake_read_lock = False
161
 
        self._held_dir = path + '/held'
162
 
        self._held_info_path = self._held_dir + self.__INFO_NAME
163
 
        self._file_modebits = file_modebits
164
 
        self._dir_modebits = dir_modebits
 
154
        self._info_path = path + self.__INFO_NAME
165
155
        self.nonce = rand_chars(20)
166
156
 
167
157
    def __repr__(self):
171
161
 
172
162
    is_held = property(lambda self: self._lock_held)
173
163
 
174
 
    def create(self):
175
 
        """Create the on-disk lock.
176
 
 
177
 
        This is typically only called when the object/directory containing the 
178
 
        directory is first created.  The lock is not held when it's created.
179
 
        """
180
 
        if self.transport.is_readonly():
181
 
            raise UnlockableTransport(self.transport)
182
 
        self.transport.mkdir(self.path)
183
 
 
184
164
    def attempt_lock(self):
185
165
        """Take the lock; fail if it's already held.
186
166
        
187
167
        If you wish to block until the lock can be obtained, call wait_lock()
188
168
        instead.
189
169
        """
190
 
        if self._fake_read_lock:
191
 
            raise LockContention(self)
192
170
        if self.transport.is_readonly():
193
171
            raise UnlockableTransport(self.transport)
194
172
        try:
195
 
            tmpname = '%s/pending.%s.tmp' % (self.path, rand_chars(20))
 
173
            tmpname = '%s.pending.%s.tmp' % (self.path, rand_chars(20))
196
174
            self.transport.mkdir(tmpname)
197
175
            sio = StringIO()
198
176
            self._prepare_info(sio)
199
177
            sio.seek(0)
200
178
            self.transport.put(tmpname + self.__INFO_NAME, sio)
201
 
            self.transport.rename(tmpname, self._held_dir)
 
179
            # FIXME: this turns into os.rename on posix, but into a fancy rename 
 
180
            # on Windows that may overwrite existing directory trees.  
 
181
            # NB: posix rename will overwrite empty directories, but not 
 
182
            # non-empty directories.
 
183
            self.transport.move(tmpname, self.path)
202
184
            self._lock_held = True
203
185
            self.confirm()
204
186
            return
205
 
        except (DirectoryNotEmpty, FileExists, ResourceBusy), e:
 
187
        except (DirectoryNotEmpty, FileExists), e:
206
188
            pass
207
189
        # fall through to here on contention
208
190
        raise LockContention(self)
210
192
    def unlock(self):
211
193
        """Release a held lock
212
194
        """
213
 
        if self._fake_read_lock:
214
 
            self._fake_read_lock = False
215
 
            return
216
195
        if not self._lock_held:
217
196
            raise LockNotHeld(self)
218
197
        # rename before deleting, because we can't atomically remove the whole
219
198
        # tree
220
 
        tmpname = '%s/releasing.%s.tmp' % (self.path, rand_chars(20))
221
 
        self.transport.rename(self._held_dir, tmpname)
 
199
        tmpname = '%s.releasing.%s.tmp' % (self.path, rand_chars(20))
 
200
        self.transport.rename(self.path, tmpname)
222
201
        self._lock_held = False
223
202
        self.transport.delete(tmpname + self.__INFO_NAME)
224
203
        self.transport.rmdir(tmpname)
249
228
            return
250
229
        if current_info != dead_holder_info:
251
230
            raise LockBreakMismatch(self, current_info, dead_holder_info)
252
 
        tmpname = '%s/broken.%s.tmp' % (self.path, rand_chars(20))
253
 
        self.transport.rename(self._held_dir, tmpname)
 
231
        tmpname = '%s.broken.%s.tmp' % (self.path, rand_chars(20))
 
232
        self.transport.rename(self.path, tmpname)
254
233
        # check that we actually broke the right lock, not someone else;
255
234
        # there's a small race window between checking it and doing the 
256
235
        # rename.
282
261
            raise LockBroken(self)
283
262
        
284
263
    def _read_info_file(self, path):
285
 
        """Read one given info file.
286
 
 
287
 
        peek() reads the info file of the lock holder, if any.
288
 
        """
289
264
        return self._parse_info(self.transport.get(path))
290
265
 
291
266
    def peek(self):
296
271
        Otherwise returns None.
297
272
        """
298
273
        try:
299
 
            info = self._read_info_file(self._held_info_path)
 
274
            info = self._read_info_file(self._info_path)
300
275
            assert isinstance(info, dict), \
301
276
                    "bad parse result %r" % info
302
277
            return info
344
319
            else:
345
320
                raise LockContention(self)
346
321
 
347
 
    def lock_write(self):
348
 
        """Wait for and acquire the lock."""
349
 
        self.attempt_lock()
350
 
 
351
 
    def lock_read(self):
352
 
        """Compatability-mode shared lock.
353
 
 
354
 
        LockDir doesn't support shared read-only locks, so this 
355
 
        just pretends that the lock is taken but really does nothing.
356
 
        """
357
 
        # At the moment Branches are commonly locked for read, but 
358
 
        # we can't rely on that remotely.  Once this is cleaned up,
359
 
        # reenable this warning to prevent it coming back in 
360
 
        # -- mbp 20060303
361
 
        ## warn("LockDir.lock_read falls back to write lock")
362
 
        if self._lock_held or self._fake_read_lock:
363
 
            raise LockContention(self)
364
 
        self._fake_read_lock = True
365
 
 
366
322
    def wait(self, timeout=20, poll=0.5):
367
323
        """Wait a certain period for a lock to be released."""
368
324
        # XXX: the transport interface doesn't let us guard