Seeking advice on locking iterators

Gonçalo Rodrigues op73418 at mail.telepac.pt
Fri Nov 15 10:12:15 EST 2002


On Thu, 14 Nov 2002 19:29:49 +0100, Ype Kingma <ykingma at accessforall.nl>
wrote:

>Gonçalo Rodrigues schreef:
>
>> Hi,
>> 
>> My problem is the following: I have a top object Application, roughly
>> corresponding to the main thread, that spawns some child threads -
>> objects on their own sake, deriving from the Thread class in the
>> threading module. They communicate when they need to via queues in the
>> usual way. So far so nice.
>> 
>> The problem is that those same child objects expose some public
>> attributes (or properties, or methods, does not matter) that the
>> Application object may need to change/call and doing the change via the
>> queue mechanism is conceptually wrong (and a real PITA). The solution is
>> obvious: wrap the attributes with locks around it. What is not so
>> obvious is when these attributes are complex objects themselves, in
>> particular they are iterables. In order to call iter() on these objects
>> safely I coded the following helper class:
>> 
>> #WARNING: Brittle - use this at your own risk.
>> class TIter(object):
>>     """The TIter helper class, wrapping an iterator for thread safe
>> acess."""
>> 
>>     def __init__(self, lock, iterator):
>>         super(TIter, self).__init__(lock, iterator)
>>         self.__lock = lock
>>         self.__iterator = iter(iterator)
>>         #Acquire lock => You cannot change the underlying structure
>>         #while iter not exhausted.
>>         self.__lock.acquire()
>>         self.__acquired = 1
>> 
>>     #Iterator protocol.
>>     def __iter__(self):
>>         return self
>> 
>>     def next(self):
>>         try:
>>             ret = self.__iterator.next()
>>         except StopIteration:
>>             self.__lock.release()
>>             self.__acquired = 0
>>             raise StopIteration
>>         else:
>>             return ret
>> 
>>     def __del__(self):
>>         if self.__acquired:
>>             self.__lock.release()
>> 
>> The idea is that for the wrapping of a given attribute one has an
>> associated lock (a reentrant lock, usually) that is passed to the ITer
>> constructor. I have to ensure two things, though:
>> 
>> * The lock is acquired while the iterator is alive.
>> * The lock is released when the iterator is disposed of.
>> 
>> So, finally, my question is: Is the above enough to ensure this? Can I
>
>In general, no. Some reasons:
>One can never be sure from the code shown when the __del__ method will be 
>called, since this depends on when the last reference to the iterator goes 
>out of scope.
>Although CPython more or less guarantees that the __del__ method will be 
>called quite soon after the last reference goes out of scope, the language 
>by itself does not make such a guarantee. In Jython, you are at the mercy
>of a separate a garbage collecting thread in the JVM to call the __del__ 
>method, ie. it might take more than a few million CPU cycles before the 
>__del__ method is called.
>
>> be sure that __del__ is called if the iterator is garbage collected
>> before being exhausted? Or is there a better (e.g. safer) mechanism for
>> this?
>
>
>One safe way is:
>
>yourObject.lock()
>try:
>    # use an iterator on yourObject.
>finally:
>    yourObject.unlock()
>    # and don't use the iterator again.
>
>Ie. I don't see the point of locking the iterator, as you seem to want to
>lock an object, ie. one the attributes you mentioned above.
>

I want to lock the iterator because I want the wrapped object to have
the same interface as the object it wraps.

>The lock() and unlock() methods need to be added to the class of yourObject 
>for this to work.

Anyway I think I have found a solution: The problem lies in the fact
that the object I am iterating over and the iterator itself can be
different objects => while the iterator acquires the lock it may get
garbage collected before it releases it. Therefore I just wrap the
object in a way that the wrapper is its own iterator and the problem
disappears.
>
>Have fun,

I am. Python *is* fun.

>Ype

With my best regards,
G. Rodrigues



More information about the Python-list mailing list