Skip to content

Commit 1b3fedd

Browse files
committed
LockFile: id representing the instance that keeps the lock is unique now - locks will check whether the lock they wrote truly is theirs - in case threads are racing, this might not be the case. Now this issue will be detected and results in a proper failure
1 parent dcf2c3b commit 1b3fedd

File tree

1 file changed

+25
-11
lines changed

1 file changed

+25
-11
lines changed

lib/git/utils.py

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ def _lock_file_path(self):
119119
"""
120120
return "%s.lock" % (self._file_path)
121121

122+
def _get_id(self):
123+
"""Returns string id to be written into the lock file"""
124+
return "%i|%i" % (os.getpid(), hash(self))
125+
122126
def _has_lock(self):
123127
"""
124128
Return
@@ -133,13 +137,13 @@ def _has_lock(self):
133137
lock_file = self._lock_file_path()
134138
try:
135139
fp = open(lock_file, "rb")
136-
pid = int(fp.read())
140+
pid = fp.read()
137141
fp.close()
138142
except IOError:
139143
raise AssertionError("The lock file at %s could not be read" % lock_file)
140144

141-
if pid != os.getpid():
142-
raise AssertionError("We claim to own the lock at %s, but it was not owned by our process %i, but by %i" % (lock_file, os.getpid(), pid))
145+
if pid != self._get_id():
146+
raise AssertionError("We claim to own the lock at %s, but it was not owned by our process %r, but by %r" % (lock_file, self._get_id(), pid ))
143147

144148
return True
145149

@@ -152,14 +156,25 @@ def _obtain_lock_or_raise(self):
152156
"""
153157
if self._has_lock():
154158
return
155-
156159
lock_file = self._lock_file_path()
157-
if os.path.exists(lock_file):
160+
if os.path.isfile(lock_file):
158161
raise IOError("Lock for file %r did already exist, delete %r in case the lock is illegal" % (self._file_path, lock_file))
159-
162+
163+
my_id = self._get_id()
160164
fp = open(lock_file, "wb")
161-
fp.write(str(os.getpid()))
165+
fp.write(my_id)
166+
fp.close()
167+
168+
# verify its truly us who got the lock - if two threads are doing this within the
169+
# fraction of a millisecond, it is possible to actually trick the FS
170+
# and two threads write, but only one succeeds.
171+
fp = open(lock_file, 'rb')
172+
actual_id = fp.read()
162173
fp.close()
174+
if actual_id != my_id:
175+
msg = "Failed to obtain lock for file %r as the process identified by %r outraced this process or thread %r" % (self._file_path, actual_id, my_id)
176+
raise IOError(msg)
177+
# END verification
163178

164179
self._owns_lock = True
165180

@@ -175,11 +190,11 @@ def _release_lock(self):
175190
Release our lock if we have one
176191
"""
177192
if not self._has_lock():
178-
return
179-
193+
return
180194
os.remove(self._lock_file_path())
181195
self._owns_lock = False
182196

197+
183198
class BlockingLockFile(LockFile):
184199
"""The lock file will block until a lock could be obtained, or fail after
185200
a specified timeout"""
@@ -206,7 +221,7 @@ def _obtain_lock(self):
206221
maxtime = starttime + float(self._max_block_time)
207222
while True:
208223
try:
209-
self._obtain_lock_or_raise()
224+
super(BlockingLockFile, self)._obtain_lock()
210225
except IOError:
211226
curtime = time.time()
212227
if curtime >= maxtime:
@@ -219,7 +234,6 @@ def _obtain_lock(self):
219234
# END endless loop
220235

221236

222-
223237
class ConcurrentWriteOperation(LockFile):
224238
"""
225239
This class facilitates a safe write operation to a file on disk such that we:

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy