diff options
Diffstat (limited to 'src/cmd/fossil/9fid.c')
-rw-r--r-- | src/cmd/fossil/9fid.c | 304 |
1 files changed, 304 insertions, 0 deletions
diff --git a/src/cmd/fossil/9fid.c b/src/cmd/fossil/9fid.c new file mode 100644 index 00000000..ea7de5ab --- /dev/null +++ b/src/cmd/fossil/9fid.c @@ -0,0 +1,304 @@ +#include "stdinc.h" + +#include "9.h" + +static struct { + VtLock* lock; + + Fid* free; + int nfree; + int inuse; +} fbox; + +static void +fidLock(Fid* fid, int flags) +{ + if(flags & FidFWlock){ + vtLock(fid->lock); + fid->flags = flags; + } + else + vtRLock(fid->lock); + + /* + * Callers of file* routines are expected to lock fsys->fs->elk + * before making any calls in order to make sure the epoch doesn't + * change underfoot. With the exception of Tversion and Tattach, + * that implies all 9P functions need to lock on entry and unlock + * on exit. Fortunately, the general case is the 9P functions do + * fidGet on entry and fidPut on exit, so this is a convenient place + * to do the locking. + * No fsys->fs->elk lock is required if the fid is being created + * (Tauth, Tattach and Twalk). FidFCreate is always accompanied by + * FidFWlock so the setting and testing of FidFCreate here and in + * fidUnlock below is always done under fid->lock. + * A side effect is that fidFree is called with the fid locked, and + * must call fidUnlock only after it has disposed of any File + * resources still held. + */ + if(!(flags & FidFCreate)) + fsysFsRlock(fid->fsys); +} + +static void +fidUnlock(Fid* fid) +{ + if(!(fid->flags & FidFCreate)) + fsysFsRUnlock(fid->fsys); + if(fid->flags & FidFWlock){ + fid->flags = 0; + vtUnlock(fid->lock); + return; + } + vtRUnlock(fid->lock); +} + +static Fid* +fidAlloc(void) +{ + Fid *fid; + + vtLock(fbox.lock); + if(fbox.nfree > 0){ + fid = fbox.free; + fbox.free = fid->hash; + fbox.nfree--; + } + else{ + fid = vtMemAllocZ(sizeof(Fid)); + fid->lock = vtLockAlloc(); + fid->alock = vtLockAlloc(); + } + fbox.inuse++; + vtUnlock(fbox.lock); + + fid->con = nil; + fid->fidno = NOFID; + fid->ref = 0; + fid->flags = 0; + fid->open = FidOCreate; + assert(fid->fsys == nil); + assert(fid->file == nil); + fid->qid = (Qid){0, 0, 0}; + assert(fid->uid == nil); + assert(fid->uname == nil); + assert(fid->db == nil); + assert(fid->excl == nil); + assert(fid->rpc == nil); + assert(fid->cuname == nil); + fid->hash = fid->next = fid->prev = nil; + + return fid; +} + +static void +fidFree(Fid* fid) +{ + if(fid->file != nil){ + fileDecRef(fid->file); + fid->file = nil; + } + if(fid->db != nil){ + dirBufFree(fid->db); + fid->db = nil; + } + fidUnlock(fid); + + if(fid->uid != nil){ + vtMemFree(fid->uid); + fid->uid = nil; + } + if(fid->uname != nil){ + vtMemFree(fid->uname); + fid->uname = nil; + } + if(fid->excl != nil) + exclFree(fid); + if(fid->rpc != nil){ + close(fid->rpc->afd); + auth_freerpc(fid->rpc); + fid->rpc = nil; + } + if(fid->fsys != nil){ + fsysPut(fid->fsys); + fid->fsys = nil; + } + if(fid->cuname != nil){ + vtMemFree(fid->cuname); + fid->cuname = nil; + } + + vtLock(fbox.lock); + fbox.inuse--; + if(fbox.nfree < 10){ + fid->hash = fbox.free; + fbox.free = fid; + fbox.nfree++; + } + else{ + vtLockFree(fid->alock); + vtLockFree(fid->lock); + vtMemFree(fid); + } + vtUnlock(fbox.lock); +} + +static void +fidUnHash(Fid* fid) +{ + Fid *fp, **hash; + + assert(fid->ref == 0); + + hash = &fid->con->fidhash[fid->fidno % NFidHash]; + for(fp = *hash; fp != nil; fp = fp->hash){ + if(fp == fid){ + *hash = fp->hash; + break; + } + hash = &fp->hash; + } + assert(fp == fid); + + if(fid->prev != nil) + fid->prev->next = fid->next; + else + fid->con->fhead = fid->next; + if(fid->next != nil) + fid->next->prev = fid->prev; + else + fid->con->ftail = fid->prev; + fid->prev = fid->next = nil; + + fid->con->nfid--; +} + +Fid* +fidGet(Con* con, u32int fidno, int flags) +{ + Fid *fid, **hash; + + if(fidno == NOFID) + return nil; + + hash = &con->fidhash[fidno % NFidHash]; + vtLock(con->fidlock); + for(fid = *hash; fid != nil; fid = fid->hash){ + if(fid->fidno != fidno) + continue; + + /* + * Already in use is an error + * when called from attach, clone or walk. + */ + if(flags & FidFCreate){ + vtUnlock(con->fidlock); + vtSetError("%s: fid 0x%ud in use", argv0, fidno); + return nil; + } + fid->ref++; + vtUnlock(con->fidlock); + + fidLock(fid, flags); + if((fid->open & FidOCreate) || fid->fidno == NOFID){ + fidPut(fid); + vtSetError("%s: fid invalid", argv0); + return nil; + } + return fid; + } + + if((flags & FidFCreate) && (fid = fidAlloc()) != nil){ + assert(flags & FidFWlock); + fid->con = con; + fid->fidno = fidno; + fid->ref = 1; + + fid->hash = *hash; + *hash = fid; + if(con->ftail != nil){ + fid->prev = con->ftail; + con->ftail->next = fid; + } + else{ + con->fhead = fid; + fid->prev = nil; + } + con->ftail = fid; + fid->next = nil; + + con->nfid++; + vtUnlock(con->fidlock); + + /* + * The FidOCreate flag is used to prevent any + * accidental access to the Fid between unlocking the + * hash and acquiring the Fid lock for return. + */ + fidLock(fid, flags); + fid->open &= ~FidOCreate; + return fid; + } + vtUnlock(con->fidlock); + + vtSetError("%s: fid not found", argv0); + return nil; +} + +void +fidPut(Fid* fid) +{ + vtLock(fid->con->fidlock); + assert(fid->ref > 0); + fid->ref--; + vtUnlock(fid->con->fidlock); + + if(fid->ref == 0 && fid->fidno == NOFID){ + fidFree(fid); + return; + } + fidUnlock(fid); +} + +void +fidClunk(Fid* fid) +{ + assert(fid->flags & FidFWlock); + + vtLock(fid->con->fidlock); + assert(fid->ref > 0); + fid->ref--; + fidUnHash(fid); + fid->fidno = NOFID; + vtUnlock(fid->con->fidlock); + + if(fid->ref > 0){ + /* not reached - fidUnHash requires ref == 0 */ + fidUnlock(fid); + return; + } + fidFree(fid); +} + +void +fidClunkAll(Con* con) +{ + Fid *fid; + u32int fidno; + + vtLock(con->fidlock); + while(con->fhead != nil){ + fidno = con->fhead->fidno; + vtUnlock(con->fidlock); + if((fid = fidGet(con, fidno, FidFWlock)) != nil) + fidClunk(fid); + vtLock(con->fidlock); + } + vtUnlock(con->fidlock); +} + +void +fidInit(void) +{ + fbox.lock = vtLockAlloc(); +} |