> On Fri, Jul 06, 2007 at 02:46:38PM +0200, bernhard.maeder@zkb.ch wrote:
> > I think I found the cause of my deadlock, which is (if I'm right that is),
> > located in main.py:912:
> >
> >         val = cache.get(id, cls)
> >         if val is None:
> >             try:
> >                 val = cls(_SO_fetch_no_create=1)
> >
> > We reach this place e.g. by resolving a foreign key through
> > _SO_foreignKey() and get().
> >
> > If I read the code correctly, the cache's lock is acquired in cache.get()
> > (but not released, which would be done in cache.finishPut(cls)), if the
> > object doesn't already exist. cls(_SO_fetch_no_create=1) then acquires the
> > class' lock through the threadSafeMethod() wrapper in declarative.py.
> >
> > On the other hand, if I'm creating an object of the same type, at exactly
> > the time between the cache.get() and cls() calls, I'll be able to acquire
> > the class' lock and then pend on the cache's lock, through threadSafe
> > __init__(), then _create(), then _SO_finishCreate(), cacheSet.created(),
> > CacheFactory.created() and finally CashFactory.cull().
> >
> > This leaves us with the first thread having acquired the cache's lock while
> > pending for the class' lock, and the second thread having acquired the
> > class' lock and pending for the cache's lock -> deadlock. The situation can
> > only occur, if the creation of the object triggers a cache.cull(). In any
> > other case, the cache isn't locked and thus there are no problems.
> >
> > Does this all make sense?
>    Absolutely. Thank you for the excellent analysis! But could the cache
> release the lock before the new row (SQLObject) is put to the cache? I
> doubt it.

I'm not (yet) an expert on sqlobject's inner goings, but I'd say the same. Otherwise it would be possible for some other thread to start fetching the same object again.

My hacky solution is to not acquire the class's lock in __init__(), when in the process of fetching an instance:

def threadSafeMethod(lock):
    def decorator(fn):
        def _wrapper(self, *args, **kwargs):
            if '_SO_fetch_no_create' in kwargs:
                return fn(self, *args, **kwargs)


If I see that correctly, the class lock isn't really needed in __init__() while fetching. But there should certainly be nicer way to do that.




Diese Mitteilung ist nur fuer die Empfaengerin / den Empfaenger bestimmt.

Fuer den Fall, dass sie von nichtberechtigten Personen empfangen wird, bitten wir diese hoeflich, die Mitteilung an die ZKB zurueckzusenden und anschliessend die Mitteilung mit allen Anhaengen sowie allfaellige Kopien zu vernichten bzw. zu loeschen. Der Gebrauch der Information ist verboten.

This message is intended only for the named recipient and may contain confidential or privileged information.

If you have received it in error, please advise the sender by return e-mail and delete this message and any attachments. Any unauthorised use or dissemination of this information is strictly prohibited.