203
204
self.transport.delete(tmpname + self.INFO_NAME)
204
205
self.transport.rmdir(tmpname)
206
def force_break(self):
207
def force_break(self, dead_holder_info):
207
208
"""Release a lock held by another process.
209
210
WARNING: This should only be used when the other process is dead; if
210
211
it still thinks it has the lock there will be two concurrent writers.
211
212
In general the user's approval should be sought for lock breaks.
214
dead_holder_info must be the result of a previous LockDir.peek() call;
215
this is used to check that it's still held by the same process that
216
the user decided was dead. If this is not the current holder,
217
LockBreakMismatch is raised.
213
219
After the lock is broken it will not be held by any process.
214
220
It is possible that another process may sneak in and take the
215
221
lock before the breaking process acquires it.
223
if not isinstance(dead_holder_info, dict):
224
raise ValueError("dead_holder_info: %r" % dead_holder_info)
217
225
if self._lock_held:
218
226
raise AssertionError("can't break own lock: %r" % self)
219
lock_info = self.peek()
220
if lock_info is None:
227
current_info = self.peek()
228
if current_info is None:
221
229
# must have been recently released
231
if current_info != dead_holder_info:
232
raise LockBreakMismatch(self, current_info, dead_holder_info)
223
233
tmpname = '%s.broken.%s.tmp' % (self.path, rand_chars(20))
224
234
self.transport.rename(self.path, tmpname)
225
self.transport.delete(tmpname + self.INFO_NAME)
235
# check that we actually broke the right lock, not someone else;
236
# there's a small race window between checking it and doing the
238
broken_info_path = tmpname + self.INFO_NAME
239
broken_info = self._parse_info(self.transport.get(broken_info_path))
240
if broken_info != dead_holder_info:
241
raise LockBreakMismatch(self, broken_info, dead_holder_info)
242
self.transport.delete(broken_info_path)
226
243
self.transport.rmdir(tmpname)
228
245
def confirm(self):
256
273
info = self._parse_info(self.transport.get(self._info_path))
257
assert isinstance(info, Stanza), \
274
assert isinstance(info, dict), \
258
275
"bad parse result %r" % info
259
return info.as_dict()
260
277
except NoSuchFile, e:
275
292
RioWriter(outf).write_stanza(s)
277
294
def _parse_info(self, info_file):
278
return read_stanza(info_file.readlines())
295
return read_stanza(info_file.readlines()).as_dict()
280
297
def wait_lock(self, timeout=_DEFAULT_TIMEOUT_SECONDS,
281
298
poll=_DEFAULT_POLL_SECONDS):