/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

Merge bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
96
96
 
97
97
import os
98
98
import time
99
 
from StringIO import StringIO
 
99
from cStringIO import StringIO
100
100
 
101
101
import bzrlib.config
102
102
from bzrlib.errors import (
111
111
        ResourceBusy,
112
112
        UnlockableTransport,
113
113
        )
114
 
from bzrlib.trace import mutter
 
114
from bzrlib.trace import mutter, note
115
115
from bzrlib.transport import Transport
116
 
from bzrlib.osutils import rand_chars
117
 
from bzrlib.rio import RioWriter, read_stanza, Stanza
 
116
from bzrlib.osutils import rand_chars, format_delta
 
117
from bzrlib.rio import read_stanza, Stanza
 
118
 
118
119
 
119
120
# XXX: At the moment there is no consideration of thread safety on LockDir
120
121
# objects.  This should perhaps be updated - e.g. if two threads try to take a
130
131
# TODO: Make sure to pass the right file and directory mode bits to all
131
132
# files/dirs created.
132
133
 
 
134
 
133
135
_DEFAULT_TIMEOUT_SECONDS = 300
134
 
_DEFAULT_POLL_SECONDS = 0.5
 
136
_DEFAULT_POLL_SECONDS = 1.0
 
137
 
135
138
 
136
139
class LockDir(object):
137
140
    """Write-lock guarding access to data."""
160
163
        self._dir_modebits = dir_modebits
161
164
        self.nonce = rand_chars(20)
162
165
 
 
166
        self._report_function = note
 
167
 
163
168
    def __repr__(self):
164
169
        return '%s(%s%s)' % (self.__class__.__name__,
165
170
                             self.transport.base,
200
205
                # After creating the lock directory, try again
201
206
                self.transport.mkdir(tmpname)
202
207
 
203
 
            sio = StringIO()
204
 
            self._prepare_info(sio)
205
 
            sio.seek(0)
206
 
            # append will create a new file; we use append rather than put
207
 
            # because we don't want to write to a temporary file and rename
208
 
            # into place, because that's going to happen to the whole
209
 
            # directory
210
 
            self.transport.append_file(tmpname + self.__INFO_NAME, sio)
 
208
            info_bytes = self._prepare_info()
 
209
            # We use put_file_non_atomic because we just created a new unique
 
210
            # directory so we don't have to worry about files existing there.
 
211
            # We'll rename the whole directory into place to get atomic
 
212
            # properties
 
213
            self.transport.put_bytes_non_atomic(tmpname + self.__INFO_NAME,
 
214
                                                info_bytes)
211
215
 
212
216
            self.transport.rename(tmpname, self._held_dir)
213
217
            self._lock_held = True
244
248
        self._check_not_locked()
245
249
        holder_info = self.peek()
246
250
        if holder_info is not None:
247
 
            if bzrlib.ui.ui_factory.get_boolean(
248
 
                "Break lock %s held by %s@%s [process #%s]" % (
249
 
                    self.transport,
250
 
                    holder_info["user"],
251
 
                    holder_info["hostname"],
252
 
                    holder_info["pid"])):
 
251
            lock_info = '\n'.join(self._format_lock_info(holder_info))
 
252
            if bzrlib.ui.ui_factory.get_boolean("Break %s" % lock_info):
253
253
                self.force_break(holder_info)
254
254
        
255
255
    def force_break(self, dead_holder_info):
336
336
        except NoSuchFile, e:
337
337
            return None
338
338
 
339
 
    def _prepare_info(self, outf):
 
339
    def _prepare_info(self):
340
340
        """Write information about a pending lock to a temporary file.
341
341
        """
342
342
        import socket
348
348
                   nonce=self.nonce,
349
349
                   user=config.user_email(),
350
350
                   )
351
 
        RioWriter(outf).write_stanza(s)
 
351
        return s.to_string()
352
352
 
353
353
    def _parse_info(self, info_file):
354
354
        return read_stanza(info_file.readlines()).as_dict()
355
355
 
356
 
    def wait_lock(self, timeout=_DEFAULT_TIMEOUT_SECONDS,
357
 
                  poll=_DEFAULT_POLL_SECONDS):
 
356
    def wait_lock(self, timeout=None, poll=None):
358
357
        """Wait a certain period for a lock.
359
358
 
360
359
        If the lock can be acquired within the bounded time, it
363
362
        approximately `timeout` seconds.  (It may be a bit more if
364
363
        a transport operation takes a long time to complete.)
365
364
        """
 
365
        if timeout is None:
 
366
            timeout = _DEFAULT_TIMEOUT_SECONDS
 
367
        if poll is None:
 
368
            poll = _DEFAULT_POLL_SECONDS
 
369
 
366
370
        # XXX: the transport interface doesn't let us guard 
367
371
        # against operations there taking a long time.
368
372
        deadline = time.time() + timeout
 
373
        deadline_str = None
 
374
        last_info = None
369
375
        while True:
370
376
            try:
371
377
                self.attempt_lock()
372
378
                return
373
379
            except LockContention:
374
380
                pass
 
381
            new_info = self.peek()
 
382
            mutter('last_info: %s, new info: %s', last_info, new_info)
 
383
            if new_info is not None and new_info != last_info:
 
384
                if last_info is None:
 
385
                    start = 'Unable to obtain'
 
386
                else:
 
387
                    start = 'Lock owner changed for'
 
388
                last_info = new_info
 
389
                formatted_info = self._format_lock_info(new_info)
 
390
                if deadline_str is None:
 
391
                    deadline_str = time.strftime('%H:%M:%S',
 
392
                                                 time.localtime(deadline))
 
393
                self._report_function('%s %s\n'
 
394
                                      '%s\n' # held by
 
395
                                      '%s\n' # locked ... ago
 
396
                                      'Will continue to try until %s\n',
 
397
                                      start,
 
398
                                      formatted_info[0],
 
399
                                      formatted_info[1],
 
400
                                      formatted_info[2],
 
401
                                      deadline_str)
 
402
 
375
403
            if time.time() + poll < deadline:
376
404
                time.sleep(poll)
377
405
            else:
379
407
 
380
408
    def lock_write(self):
381
409
        """Wait for and acquire the lock."""
382
 
        self.attempt_lock()
 
410
        self.wait_lock()
383
411
 
384
412
    def lock_read(self):
385
413
        """Compatibility-mode shared lock.
409
437
            else:
410
438
                raise LockContention(self)
411
439
 
 
440
    def _format_lock_info(self, info):
 
441
        """Turn the contents of peek() into something for the user"""
 
442
        lock_url = self.transport.abspath(self.path)
 
443
        delta = time.time() - int(info['start_time'])
 
444
        return [
 
445
            'lock %s' % (lock_url,),
 
446
            'held by %(user)s on host %(hostname)s [process #%(pid)s]' % info,
 
447
            'locked %s' % (format_delta(delta),),
 
448
            ]
 
449