~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lockdir.py

(mbp) merge bzr.dev to 0.8, prepare for release

Show diffs side-by-side

added added

removed removed

Lines of Context:
109
109
        LockError,
110
110
        LockNotHeld,
111
111
        NoSuchFile,
 
112
        PathError,
112
113
        ResourceBusy,
113
114
        UnlockableTransport,
114
115
        )
 
116
from bzrlib.trace import mutter
115
117
from bzrlib.transport import Transport
116
118
from bzrlib.osutils import rand_chars
117
119
from bzrlib.rio import RioWriter, read_stanza, Stanza
121
123
# lock at the same time they should *both* get it.  But then that's unlikely
122
124
# to be a good idea.
123
125
 
124
 
# TODO: Transport could offer a simpler put() method that avoids the
125
 
# rename-into-place for cases like creating the lock template, where there is
126
 
# no chance that the file already exists.
127
 
 
128
126
# TODO: Perhaps store some kind of note like the bzr command line in the lock
129
127
# info?
130
128
 
171
169
 
172
170
    is_held = property(lambda self: self._lock_held)
173
171
 
174
 
    def create(self):
 
172
    def create(self, mode=None):
175
173
        """Create the on-disk lock.
176
174
 
177
175
        This is typically only called when the object/directory containing the 
179
177
        """
180
178
        if self.transport.is_readonly():
181
179
            raise UnlockableTransport(self.transport)
182
 
        self.transport.mkdir(self.path)
 
180
        self.transport.mkdir(self.path, mode=mode)
183
181
 
184
182
    def attempt_lock(self):
185
183
        """Take the lock; fail if it's already held.
197
195
            sio = StringIO()
198
196
            self._prepare_info(sio)
199
197
            sio.seek(0)
200
 
            self.transport.put(tmpname + self.__INFO_NAME, sio)
 
198
            # append will create a new file; we use append rather than put
 
199
            # because we don't want to write to a temporary file and rename
 
200
            # into place, because that's going to happen to the whole
 
201
            # directory
 
202
            self.transport.append(tmpname + self.__INFO_NAME, sio)
201
203
            self.transport.rename(tmpname, self._held_dir)
202
204
            self._lock_held = True
203
205
            self.confirm()
204
 
            return
205
 
        except (DirectoryNotEmpty, FileExists, ResourceBusy), e:
206
 
            pass
207
 
        # fall through to here on contention
208
 
        raise LockContention(self)
 
206
        except (PathError, DirectoryNotEmpty, FileExists, ResourceBusy), e:
 
207
            mutter("contention on %r: %s", self, e)
 
208
            raise LockContention(self)
209
209
 
210
210
    def unlock(self):
211
211
        """Release a held lock
218
218
        # rename before deleting, because we can't atomically remove the whole
219
219
        # tree
220
220
        tmpname = '%s/releasing.%s.tmp' % (self.path, rand_chars(20))
 
221
        # gotta own it to unlock
 
222
        self.confirm()
221
223
        self.transport.rename(self._held_dir, tmpname)
222
224
        self._lock_held = False
223
225
        self.transport.delete(tmpname + self.__INFO_NAME)
224
226
        self.transport.rmdir(tmpname)
225
227
 
 
228
    def break_lock(self):
 
229
        """Break a lock not held by this instance of LockDir.
 
230
 
 
231
        This is a UI centric function: it uses the bzrlib.ui.ui_factory to
 
232
        prompt for input if a lock is detected and there is any doubt about
 
233
        it possibly being still active.
 
234
        """
 
235
        self._check_not_locked()
 
236
        holder_info = self.peek()
 
237
        if holder_info is not None:
 
238
            if bzrlib.ui.ui_factory.get_boolean(
 
239
                "Break lock %s held by %s@%s [process #%s]" % (
 
240
                    self.transport,
 
241
                    holder_info["user"],
 
242
                    holder_info["hostname"],
 
243
                    holder_info["pid"])):
 
244
                self.force_break(holder_info)
 
245
        
226
246
    def force_break(self, dead_holder_info):
227
247
        """Release a lock held by another process.
228
248
 
241
261
        """
242
262
        if not isinstance(dead_holder_info, dict):
243
263
            raise ValueError("dead_holder_info: %r" % dead_holder_info)
244
 
        if self._lock_held:
245
 
            raise AssertionError("can't break own lock: %r" % self)
 
264
        self._check_not_locked()
246
265
        current_info = self.peek()
247
266
        if current_info is None:
248
267
            # must have been recently released
261
280
        self.transport.delete(broken_info_path)
262
281
        self.transport.rmdir(tmpname)
263
282
 
 
283
    def _check_not_locked(self):
 
284
        """If the lock is held by this instance, raise an error."""
 
285
        if self._lock_held:
 
286
            raise AssertionError("can't break own lock: %r" % self)
 
287
 
264
288
    def confirm(self):
265
289
        """Make sure that the lock is still held by this locker.
266
290