Earlier there was discussion of using a combination of
extend and aside to enable multiple readers and one
writer.

Here is a class that manages it for you: meta_safe.
You access the storage object as an attribute with
meta_safe and have meta_safe manage the extend and
aisde isses.

It also has a simple cross-platform method of 2-level
locking based of of the assumption that mkdir is one
successful once. If you want to manage locks yourself,
you can eaisly turn off locking.

The locking I call gentleman's locking. It is safe to
call unlock even if you do not have the lock(it just
won't do anything). And, you only keep your lock for a
predermined time. If you want to keep your lock
longer, you have to reassert it, before the time runs
out.

I called it meta_safe since with the combination of
locking and extend and aside, it is safer to write to
metakit.

Here is an example of usage
#####
import time,meta_safe

dbfile='main.mk'
#w means write

#open for safe writing and create the view
s=meta_safe.safe(dbfile,'w',create='a[i:S]')
vw = s.db.view('a')
vw.append(i=time.asctime())
s.close()

#open for safe writing
s=meta_safe.safe(dbfile,'w')
vw = s.db.view('a')
vw.append(i=time.asctime())
vw.append(i=time.asctime())
vw.append(i=time.asctime())
s.close()

#open for reading
s=meta_safe.safe(dbfile)
vw=s.db.view('a')
print len(vw)
for item in vw:
    print item.i
s.unsafe_close()

#if you do not want locking set use_lock to 0
s=meta_safe.safe(dbfile,'w',create='a[i:S]',use_lock=0)

####meta_safe.py class####
import metakit,os,time
#Version 0.1
#John Nielsen

import metakit,os,time

class lock:
    '''cross-platform locking.
    Locking will raise exceptions.
    Unlocking won't. So, unlock all you want'''
    def __init__(self,*args,**kwds):
        if not len(args): raise 'need a lock name'
        #detemine how long to wait for locks(min)
        if 'wait' in kwds:
            self.wait=kwds['wait']*60
        else:
            self.wait=300
        name=args[0]
        self.d=name+'_lock/'
        self.d2=name+'_lock2/'
        self.locked={}
        lock_successful=0
        if self.locked.has_key(self.d2):
            try:
                #you got a lock, it is yours?
                if os.stat(self.d2)[8]==\
                   self.locked.get(self.d2):
                    #try to extend lock before loss
                    #ATOMIC operation, extend may
                    #fail presence of self.d will
                    #prevent loss of lock
                    #just by the act of extending
                    os.rmdir(self.d2)
                    os.mkdir(self.d2)
                    os.rmdir(self.d)
                    os.mkdir(self.d)
                    self.locked[self.d2]=\
                        os.stat(self.d2)[8]
                    lock_successful=1
                if self.locked.has_key(self.d2):
                    del(self.locked[self.d2])
                result='Fail: lost lock'
            except:
                if self.locked.has_key(self.d2):
                    del(self.locked[self.d2])
                result='Fail: lost lock'
        else:
            for t in range(0,self.wait): #try 10 times

                #not locked yet, try to lock it
                try:
                    os.mkdir(self.d) #ATOMIC  
                    os.mkdir(self.d2) #ATOMIC 
                    self.locked[self.d2]=\
                        os.stat(self.d2)[8]
                    lock_successful=1
                    break
                except Exception,error:
                    result=error
                    print 'locking??',error
                    #mkdir probably failed
                    #lock already there,
                    #try to delete old lock
                    m_dir2=0;m_dir=0
                    if os.path.exists(self.d):
                        try:
                            m_dir2=os.stat(self.d2)[8]
                        except:
                            pass
                        try:
                            m_dir=os.stat(self.d)[8]
                        except:
                            pass
                        cur_tm=int(time.time())
                        #presence of either directory
                        #can stop you from taking lock
                        if cur_tm>m_dir+self.wait and\
                           cur_tm>m_dir2+self.wait:
                            #delete old locks
                            #ATOMIC here
                            os.rmdir(self.d2)
                            os.mkdir(self.d2) 
                            os.rmdir(self.d)
                            os.mkdir(self.d)
                            self.locked[self.d2]=\
                                os.stat(self.d2)[8]
                            lock_successful=1 
                            break
                time.sleep(1)
        #made it thru the loop, so we got no lock
        if not lock_successful:
            raise result

    def unlock(self):
        '''does not raise an exception,
        safe to unlock as often as you want
        it may just do nothing'''
        if self.locked.has_key(self.d2):
            #we're the ones that unlocked it,
            #if time matched
            if self.locked[self.d2]==\
               os.stat(self.d2)[8]:
                try:
                    del(self.locked[self.d2])
                    os.rmdir(self.d2)
                    os.rmdir(self.d)
                    return 1
                except:
                    return 0
            else:
                del(self.locked[self.d2])
            return 0
        else:
            return 0

class safe:
    '''manage aside and extend automatically to allow
    multiple safe readers and one writer'''
    def __init__(self,*args,**kwds):
        self.use_lock=1
        self.l=None#locking placeholders
        if 'lock' in kwds:
            self.use_lock=kwds['lock']
        if 'store' in kwds:
            #must end in / or \
            if kwds['store_dir'][-1] in ('/','\\'):
                self.store_dir=kwds['store_dir']
            else:
                raise 'error: need / or \ to end
store'
        else:
            self.store=''
        if len(args)==1:
            #read-only
            self.flag='r'
            self.dbfile=self.store+args[0]
        elif len(args)>1:
            self.dbfile=args[0]
            self.flag=args[1]
            if self.flag not in ('r','w'):
                raise 'error: use r or w'
        self.dbaside=self.dbfile+'a'
        if 'create' in kwds:
            if self.use_lock:
                l=lock(self.dbfile)##########LOCKING
                db = metakit.storage(self.dbfile, 1)
                vw=db.getas(kwds['create'])
                db.commit()
                del db
            if self.use_lock:
                l.unlock() ############UNLOCKING
        self.db=self.dba=None
        ##  
        if self.flag=='r':
            self.db = metakit.storage(self.dbfile, 0)
            if os.path.exists(self.dbaside):
                self.dba =\
                    metakit.storage(self.dbaside, 0)
        elif self.flag=='w':
            if self.use_lock:
                self.l=lock(self.dbfile)######LOCKING
            self.db = metakit.storage(self.dbfile, 0)
            self.dba = metakit.storage(self.dbaside,
2)

        self.db.aside(self.dba)
        vw=self.db.view('a')
    def close(self):
        if self.flag=='w':
            self.db.commit()
            self.dba.commit()
            ############UNLOCKING
            if self.use_lock:
                if self.l: self.l.unlock()
        del self.db
        del self.dba

    def unsafe_close(self):
        l=lock(self.dbfile)##########LOCKING
        #synchronize aside and main
        db = metakit.storage(self.dbfile, 1)
        dba = metakit.storage(self.dbaside, 1)
        db.aside(dba)
        db.commit(1)
        del db
        del dba
        l.unlock()

if __name__ == "__main__":
    print 'testing'
    dbfile='main.mk'

    #create is only needed if the view doesn't exist
    s=safe(dbfile,'w',create='a[i:S]',lock=1)
    vw = s.db.view('a')
    vw.append(i=time.asctime())
    vw.append(i=time.asctime())
    vw.append(i=time.asctime())
    s.close()

    s=safe(dbfile)
    vw=s.db.view('a')
    print len(vw)
    for item in vw:
        print item.i
    s.unsafe_close()

__________________________________
Do you Yahoo!?
Free Pop-Up Blocker - Get it now
http://companion.yahoo.com/
_____________________________________________
Metakit mailing list  -  [EMAIL PROTECTED]
http://www.equi4.com/mailman/listinfo/metakit

Reply via email to