@@ -119,6 +119,10 @@ def _lock_file_path(self):
119119"""
120120return "%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+
122126def _has_lock (self ):
123127"""
124128Return
@@ -133,13 +137,13 @@ def _has_lock(self):
133137lock_file = self ._lock_file_path ()
134138try :
135139fp = open (lock_file ,"rb" )
136- pid = int ( fp .read () )
140+ pid = fp .read ()
137141fp .close ()
138142except IOError :
139143raise 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
144148return True
145149
@@ -152,14 +156,25 @@ def _obtain_lock_or_raise(self):
152156"""
153157if self ._has_lock ():
154158return
155-
156159lock_file = self ._lock_file_path ()
157- if os .path .exists (lock_file ):
160+ if os .path .isfile (lock_file ):
158161raise 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 ()
160164fp = 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 ()
162173fp .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
164179self ._owns_lock = True
165180
@@ -175,11 +190,11 @@ def _release_lock(self):
175190Release our lock if we have one
176191"""
177192if not self ._has_lock ():
178- return
179-
193+ return
180194os .remove (self ._lock_file_path ())
181195self ._owns_lock = False
182196
197+
183198class BlockingLockFile (LockFile ):
184199"""The lock file will block until a lock could be obtained, or fail after
185200a specified timeout"""
@@ -206,7 +221,7 @@ def _obtain_lock(self):
206221maxtime = starttime + float (self ._max_block_time )
207222while True :
208223try :
209- self . _obtain_lock_or_raise ()
224+ super ( BlockingLockFile , self ). _obtain_lock ()
210225except IOError :
211226curtime = time .time ()
212227if curtime >= maxtime :
@@ -219,7 +234,6 @@ def _obtain_lock(self):
219234# END endless loop
220235
221236
222-
223237class ConcurrentWriteOperation (LockFile ):
224238"""
225239This class facilitates a safe write operation to a file on disk such that we: