Re: Transaction Set clean does not actually clean

Mitko Haralanov <[email protected]> Fri, 7 Dec 2007 13:45:16 -0800
Newsgroups gmane.linux.redhat.rpm.python
Organization QLogic Corporation
Message-ID <[email protected]>
On Fri, 7 Dec 2007 15:12:31 -0500
Jeff Johnson <[email protected]> wrote:

> Calling closeDB() (or doing any operation on a rpmdb) explicitly is not
> recommended. Delete the transaction set and create a new transaction
> instead. The reason is that there are global aspects to concurrent
> access of an rpmdb that few application programs are prepared to
> implement properly.

I tried that, however, this creates problems with multi-threaded
applications. If one of the threads deletes the TransactionSet
instance, then the other threads throw exceptions.

Lock and unlocking the TransactionSet instance doesn't really help
since holding a lock does not guarantee that the instance will exist.

What I did try is to create a wrapper class around the TransactionSet
instance (pasted below) that will try to do the right thing.
Unfortunately, this does not work either in cases where the
TransactionSet operation returns a match object (for example), since
working with the match object does depend on the TransactionSet
instance to still be around.

class Transaction:
    def __init__ (self, db=None):
        self.ts = None
        self.ts_lock = Lock ()
        self.db = db
        self.func = None

    def __call__ (self, *args):
        return False
    
    # This method will acquire the lock, record the TransactionSet
    # method being called by the caller and return the pointer to
    # the call_ts_func () function, which will do all the work.
    def __getattr__ (self, name):
        self.ts_lock.acquire ()
        self.func = name
        return self.call_ts_func

    # This method serves as a wrapper around the actual TransactionSet
    # method being called. It will open the database, call the
    # TransactionSet method, close the databse, and then release the
    # lock acquired in the __getattr__ () function.
    def call_ts_func (self, *args):
        if not self.ts:
            if self.db: rpm.addMacro ("_dbpath", self.db)
            self.ts = rpm.TransactionSet ()
            self.ts.openDB ()
            if self.db: rpm.delMacro ("_dbpath")

        func = self.ts.__getattribute__ (self.func)
        rs = func (*args)

        self.func = None
        self.ts.clean ()
        self.ts.closeDB ()
        del self.ts
        self.ts = None
        self.ts_lock.release ()
        return rs


-- 
Mitko Haralanov					 [email protected]
Senior Software Engineer			     650.934.8064
System Interconnect Group		    http://www.qlogic.com

==========================================
We need to teach Linus about "taste" in drivers. His core code taste is
impeccable, but I'm not fond of his driver taste ;)

	- Alan Cox on linux-kernel