~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lockdir.py

  • Committer: Martin Pool
  • Date: 2006-02-22 07:06:12 UTC
  • mto: This revision was merged to the branch mainline in revision 1569.
  • Revision ID: mbp@sourcefrog.net-20060222070612-c108852197fc0ca4
LockDir review comment fixes

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
"""On-disk mutex protecting a resource
18
18
 
19
 
bzr objects are locked by the existence of a directory with a particular name
20
 
within the control directory.  We use this rather than OS internal locks (such
21
 
as flock etc) because they can be seen across all transports, including http.
 
19
bzr on-disk objects are locked by the existence of a directory with a
 
20
particular name within the control directory.  We use this rather than OS
 
21
internal locks (such as flock etc) because they can be seen across all
 
22
transports, including http.
22
23
 
23
24
Objects can be read if there is only physical read access; therefore 
24
25
readers can never be required to create a lock, though they will
117
118
# lock at the same time they should *both* get it.  But then that's unlikely
118
119
# to be a good idea.
119
120
 
120
 
# TODO: After renaming the directory, check the contents are what we
121
 
# expected.  It's possible that the rename failed but the transport lost
122
 
# the failure indication.
123
 
 
124
121
# TODO: Transport could offer a simpler put() method that avoids the
125
122
# rename-into-place for cases like creating the lock template, where there is
126
123
# no chance that the file already exists.
135
132
_DEFAULT_POLL_SECONDS = 0.5
136
133
 
137
134
class LockDir(object):
138
 
    """Write-lock guarding access to data.
139
 
    """
 
135
    """Write-lock guarding access to data."""
140
136
 
141
 
    INFO_NAME = '/info'
 
137
    __INFO_NAME = '/info'
142
138
 
143
139
    def __init__(self, transport, path):
144
140
        """Create a new LockDir object.
155
151
        self.transport = transport
156
152
        self.path = path
157
153
        self._lock_held = False
158
 
        self._info_path = path + self.INFO_NAME
 
154
        self._info_path = path + self.__INFO_NAME
159
155
        self.nonce = rand_chars(20)
160
156
 
161
157
    def __repr__(self):
166
162
    is_held = property(lambda self: self._lock_held)
167
163
 
168
164
    def attempt_lock(self):
169
 
        """Take the lock; fail if it's already held
 
165
        """Take the lock; fail if it's already held.
170
166
        
171
167
        If you wish to block until the lock can be obtained, call wait_lock()
172
168
        instead.
179
175
            sio = StringIO()
180
176
            self._prepare_info(sio)
181
177
            sio.seek(0)
182
 
            self.transport.put(tmpname + self.INFO_NAME, sio)
 
178
            self.transport.put(tmpname + self.__INFO_NAME, sio)
183
179
            # FIXME: this turns into os.rename on posix, but into a fancy rename 
184
180
            # on Windows that may overwrite existing directory trees.  
185
181
            # NB: posix rename will overwrite empty directories, but not 
186
182
            # non-empty directories.
187
183
            self.transport.move(tmpname, self.path)
188
184
            self._lock_held = True
 
185
            self.confirm()
189
186
            return
190
187
        except (DirectoryNotEmpty, FileExists), e:
191
188
            pass
202
199
        tmpname = '%s.releasing.%s.tmp' % (self.path, rand_chars(20))
203
200
        self.transport.rename(self.path, tmpname)
204
201
        self._lock_held = False
205
 
        self.transport.delete(tmpname + self.INFO_NAME)
 
202
        self.transport.delete(tmpname + self.__INFO_NAME)
206
203
        self.transport.rmdir(tmpname)
207
204
 
208
205
    def force_break(self, dead_holder_info):
236
233
        # check that we actually broke the right lock, not someone else;
237
234
        # there's a small race window between checking it and doing the 
238
235
        # rename.
239
 
        broken_info_path = tmpname + self.INFO_NAME
240
 
        broken_info = self._parse_info(self.transport.get(broken_info_path))
 
236
        broken_info_path = tmpname + self.__INFO_NAME
 
237
        broken_info = self._read_info_file(broken_info_path)
241
238
        if broken_info != dead_holder_info:
242
239
            raise LockBreakMismatch(self, broken_info, dead_holder_info)
243
240
        self.transport.delete(broken_info_path)
262
259
        if info.get('nonce') != self.nonce:
263
260
            # there is a lock, but not ours
264
261
            raise LockBroken(self)
 
262
        
 
263
    def _read_info_file(self, path):
 
264
        return self._parse_info(self.transport.get(path))
265
265
 
266
266
    def peek(self):
267
267
        """Check if the lock is held by anyone.
271
271
        Otherwise returns None.
272
272
        """
273
273
        try:
274
 
            info = self._parse_info(self.transport.get(self._info_path))
 
274
            info = self._read_info_file(self._info_path)
275
275
            assert isinstance(info, dict), \
276
276
                    "bad parse result %r" % info
277
277
            return info
320
320
                raise LockContention(self)
321
321
 
322
322
    def wait(self, timeout=20, poll=0.5):
323
 
        """Wait a certain period for a lock to be released.
324
 
        """
 
323
        """Wait a certain period for a lock to be released."""
325
324
        # XXX: the transport interface doesn't let us guard 
326
325
        # against operations there taking a long time.
327
326
        deadline = time.time() + timeout