~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lockdir.py

  • Committer: Martin Pool
  • Date: 2006-03-06 03:57:18 UTC
  • mto: This revision was merged to the branch mainline in revision 1593.
  • Revision ID: mbp@sourcefrog.net-20060306035718-a50bae3c32eec48a
Change LockDirs to format "lock-name/held/info"

For the moment, auto-create the top-level directory when it's needed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
129
129
# TODO: Some kind of callback run while polling a lock to show progress
130
130
# indicators.
131
131
 
132
 
# TODO: perhaps put everything (held, pending, releasing, broken) inside a
133
 
# top-level directory with the name of the lock, so that we can still see the
134
 
# lock actually exists -- the top-level directory can then be created only by
135
 
# the init method.
136
 
 
137
132
_DEFAULT_TIMEOUT_SECONDS = 300
138
133
_DEFAULT_POLL_SECONDS = 0.5
139
134
 
158
153
        self.path = path
159
154
        self._lock_held = False
160
155
        self._fake_read_lock = False
161
 
        self._info_path = path + self.__INFO_NAME
 
156
        self._held_dir = path + '/held'
 
157
        self._held_info_path = self._held_dir + self.__INFO_NAME
162
158
        self.nonce = rand_chars(20)
163
159
 
164
160
    def __repr__(self):
168
164
 
169
165
    is_held = property(lambda self: self._lock_held)
170
166
 
 
167
    def create(self):
 
168
        """Create the on-disk lock.
 
169
 
 
170
        This is typically only called when the object/directory containing the 
 
171
        directory is first created.  The lock is not held when it's created.
 
172
        """
 
173
        if self.transport.is_readonly():
 
174
            raise UnlockableTransport(self.transport)
 
175
        self.transport.mkdir(self.path)
 
176
 
 
177
    def _create_if_needed(self):
 
178
        # XXX: remove this in favour of creating the lock when the containing
 
179
        # directory is built
 
180
        if not self.transport.has(self.path):
 
181
            self.create()
 
182
 
171
183
    def attempt_lock(self):
172
184
        """Take the lock; fail if it's already held.
173
185
        
178
190
            raise LockContention(self)
179
191
        if self.transport.is_readonly():
180
192
            raise UnlockableTransport(self.transport)
 
193
        self._create_if_needed()
181
194
        try:
182
 
            tmpname = '%s.pending.%s.tmp' % (self.path, rand_chars(20))
 
195
            tmpname = '%s/pending.%s.tmp' % (self.path, rand_chars(20))
183
196
            self.transport.mkdir(tmpname)
184
197
            sio = StringIO()
185
198
            self._prepare_info(sio)
186
199
            sio.seek(0)
187
200
            self.transport.put(tmpname + self.__INFO_NAME, sio)
188
 
            # FIXME: this turns into os.rename on posix, but into a fancy rename 
189
 
            # on Windows that may overwrite existing directory trees.  
190
 
            # NB: posix rename will overwrite empty directories, but not 
191
 
            # non-empty directories.
192
 
            self.transport.move(tmpname, self.path)
 
201
            self.transport.rename(tmpname, self._held_dir)
193
202
            self._lock_held = True
194
203
            self.confirm()
195
204
            return
208
217
            raise LockNotHeld(self)
209
218
        # rename before deleting, because we can't atomically remove the whole
210
219
        # tree
211
 
        tmpname = '%s.releasing.%s.tmp' % (self.path, rand_chars(20))
212
 
        self.transport.rename(self.path, tmpname)
 
220
        tmpname = '%s/releasing.%s.tmp' % (self.path, rand_chars(20))
 
221
        self.transport.rename(self._held_dir, tmpname)
213
222
        self._lock_held = False
214
223
        self.transport.delete(tmpname + self.__INFO_NAME)
215
224
        self.transport.rmdir(tmpname)
240
249
            return
241
250
        if current_info != dead_holder_info:
242
251
            raise LockBreakMismatch(self, current_info, dead_holder_info)
243
 
        tmpname = '%s.broken.%s.tmp' % (self.path, rand_chars(20))
244
 
        self.transport.rename(self.path, tmpname)
 
252
        tmpname = '%s/broken.%s.tmp' % (self.path, rand_chars(20))
 
253
        self.transport.rename(self._held_dir, tmpname)
245
254
        # check that we actually broke the right lock, not someone else;
246
255
        # there's a small race window between checking it and doing the 
247
256
        # rename.
273
282
            raise LockBroken(self)
274
283
        
275
284
    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
        """
276
289
        return self._parse_info(self.transport.get(path))
277
290
 
278
291
    def peek(self):
283
296
        Otherwise returns None.
284
297
        """
285
298
        try:
286
 
            info = self._read_info_file(self._info_path)
 
299
            info = self._read_info_file(self._held_info_path)
287
300
            assert isinstance(info, dict), \
288
301
                    "bad parse result %r" % info
289
302
            return info