/* * the file is locked already * f->msource is unlocked */ static File * dirLookup(File *f, char *elem) { int i; MetaBlock mb; MetaEntry me; Block *b; Source *meta; File *ff; uint32_t bo, nb; meta = f->msource; b = nil; if(!sourceLock(meta, -1)) return nil; nb = (sourceGetSize(meta)+meta->dsize-1)/meta->dsize; for(bo=0; bo<nb; bo++){ b = sourceBlock(meta, bo, OReadOnly); if(b == nil) goto Err; if(!mbUnpack(&mb, b->data, meta->dsize)) goto Err; if(mbSearch(&mb, elem, &i, &me)){ ff = fileAlloc(f->fs); if(!deUnpack(&ff->dir, &me)){ fileFree(ff); goto Err; } sourceUnlock(meta); blockPut(b); ff->boff = bo; ff->mode = f->mode; ff->issnapshot = f->issnapshot; return ff; } blockPut(b); b = nil; } vtSetError(ENoFile); /* fall through */ Err: sourceUnlock(meta); blockPut(b); return nil; }
int fileGetDir(File *f, DirEntry *dir) { if(!fileRLock(f)) return 0; fileMetaLock(f); deCopy(dir, &f->dir); fileMetaUnlock(f); if(!fileIsDir(f)){ if(!sourceLock(f->source, OReadOnly)){ fileRUnlock(f); return 0; } dir->size = sourceGetSize(f->source); sourceUnlock(f->source); } fileRUnlock(f); return 1; }
int fileSetSize(File *f, uvlong size) { int r; if(!fileLock(f)) return 0; r = 0; if(f->dir.mode & ModeDir){ vtSetError(ENotFile); goto Err; } if(f->source->mode != OReadWrite){ vtSetError(EReadOnly); goto Err; } if(!sourceLock(f->source, -1)) goto Err; r = sourceSetSize(f->source, size); sourceUnlock(f->source); Err: fileUnlock(f); return r; }
int fileWrite(File *f, void *buf, int cnt, vlong offset, char *uid) { Source *s; ulong bn; int off, dsize, n; Block *b; uchar *p; vlong eof; if(0)fprint(2, "fileWrite: %s %d, %lld\n", f->dir.elem, cnt, offset); if(!fileLock(f)) return -1; s = nil; if(f->dir.mode & ModeDir){ vtSetError(ENotFile); goto Err; } if(f->source->mode != OReadWrite){ vtSetError(EReadOnly); goto Err; } if(offset < 0){ vtSetError(EBadOffset); goto Err; } fileWAccess(f, uid); if(!sourceLock(f->source, -1)) goto Err; s = f->source; dsize = s->dsize; eof = sourceGetSize(s); if(f->dir.mode & ModeAppend) offset = eof; bn = offset/dsize; off = offset%dsize; p = buf; while(cnt > 0){ n = cnt; if(n > dsize-off) n = dsize-off; b = sourceBlock(s, bn, n<dsize?OReadWrite:OOverWrite); if(b == nil){ if(offset > eof) sourceSetSize(s, offset); goto Err; } memmove(b->data+off, p, n); off = 0; cnt -= n; p += n; offset += n; bn++; blockDirty(b); blockPut(b); } if(offset > eof && !sourceSetSize(s, offset)) goto Err; sourceUnlock(s); fileUnlock(f); return p-(uchar*)buf; Err: if(s) sourceUnlock(s); fileUnlock(f); return -1; }
int fileRead(File *f, void *buf, int cnt, vlong offset) { Source *s; uvlong size; u32int bn; int off, dsize, n, nn; Block *b; uchar *p; if(0)fprint(2, "fileRead: %s %d, %lld\n", f->dir.elem, cnt, offset); if(!fileRLock(f)) return -1; if(offset < 0){ vtSetError(EBadOffset); goto Err1; } fileRAccess(f); if(!sourceLock(f->source, OReadOnly)) goto Err1; s = f->source; dsize = s->dsize; size = sourceGetSize(s); if(offset >= size) offset = size; if(cnt > size-offset) cnt = size-offset; bn = offset/dsize; off = offset%dsize; p = buf; while(cnt > 0){ b = sourceBlock(s, bn, OReadOnly); if(b == nil) goto Err; n = cnt; if(n > dsize-off) n = dsize-off; nn = dsize-off; if(nn > n) nn = n; memmove(p, b->data+off, nn); memset(p+nn, 0, nn-n); off = 0; bn++; cnt -= n; p += n; blockPut(b); } sourceUnlock(s); fileRUnlock(f); return p-(uchar*)buf; Err: sourceUnlock(s); Err1: fileRUnlock(f); return -1; }
File * fileCreate(File *f, char *elem, ulong mode, char *uid) { File *ff; DirEntry *dir; Source *pr, *r, *mr; int isdir; if(!fileLock(f)) return nil; r = nil; mr = nil; for(ff = f->down; ff; ff=ff->next){ if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){ ff = nil; vtSetError(EExists); goto Err1; } } ff = dirLookup(f, elem); if(ff != nil){ vtSetError(EExists); goto Err1; } pr = f->source; if(pr->mode != OReadWrite){ vtSetError(EReadOnly); goto Err1; } if(!sourceLock2(f->source, f->msource, -1)) goto Err1; ff = fileAlloc(f->fs); isdir = mode & ModeDir; r = sourceCreate(pr, pr->dsize, isdir, 0); if(r == nil) goto Err; if(isdir){ mr = sourceCreate(pr, pr->dsize, 0, r->offset); if(mr == nil) goto Err; } dir = &ff->dir; dir->elem = vtStrDup(elem); dir->entry = r->offset; dir->gen = r->gen; if(isdir){ dir->mentry = mr->offset; dir->mgen = mr->gen; } dir->size = 0; if(!fsNextQid(f->fs, &dir->qid)) goto Err; dir->uid = vtStrDup(uid); dir->gid = vtStrDup(f->dir.gid); dir->mid = vtStrDup(uid); dir->mtime = time(0L); dir->mcount = 0; dir->ctime = dir->mtime; dir->atime = dir->mtime; dir->mode = mode; ff->boff = fileMetaAlloc(f, dir, 0); if(ff->boff == NilBlock) goto Err; sourceUnlock(f->source); sourceUnlock(f->msource); ff->source = r; r->file = ff; /* point back */ ff->msource = mr; if(mode&ModeTemporary){ if(!sourceLock2(r, mr, -1)) goto Err1; fileSetTmp(ff, 1); sourceUnlock(r); if(mr) sourceUnlock(mr); } /* committed */ /* link in and up parent ref count */ ff->next = f->down; f->down = ff; ff->up = f; fileIncRef(f); fileWAccess(f, uid); fileUnlock(f); return ff; Err: sourceUnlock(f->source); sourceUnlock(f->msource); Err1: if(r){ sourceLock(r, -1); sourceRemove(r); } if(mr){ sourceLock(mr, -1); sourceRemove(mr); } if(ff) fileDecRef(ff); fileUnlock(f); return 0; }
File * fileRoot(Source *r) { Block *b; Source *r0, *r1, *r2; MetaBlock mb; MetaEntry me; File *root, *mr; Fs *fs; b = nil; root = nil; mr = nil; r1 = nil; r2 = nil; fs = r->fs; if(!sourceLock(r, -1)) return nil; r0 = sourceOpen(r, 0, fs->mode, 0); if(r0 == nil) goto Err; r1 = sourceOpen(r, 1, fs->mode, 0); if(r1 == nil) goto Err; r2 = sourceOpen(r, 2, fs->mode, 0); if(r2 == nil) goto Err; mr = fileAlloc(fs); mr->msource = r2; r2 = nil; root = fileAlloc(fs); root->boff = 0; root->up = mr; root->source = r0; r0->file = root; /* point back to source */ r0 = nil; root->msource = r1; r1 = nil; mr->down = root; if(!sourceLock(mr->msource, -1)) goto Err; b = sourceBlock(mr->msource, 0, OReadOnly); sourceUnlock(mr->msource); if(b == nil) goto Err; if(!mbUnpack(&mb, b->data, mr->msource->dsize)) goto Err; meUnpack(&me, &mb, 0); if(!deUnpack(&root->dir, &me)) goto Err; blockPut(b); sourceUnlock(r); fileRAccess(root); return root; Err: blockPut(b); if(r0) sourceClose(r0); if(r1) sourceClose(r1); if(r2) sourceClose(r2); if(mr) fileFree(mr); if(root) fileFree(root); sourceUnlock(r); return nil; }
/* assumes metaLock is held */ static int fileMetaFlush2(File *f, char *oelem) { File *fp; Block *b, *bb; MetaBlock mb; MetaEntry me, me2; int i, n; u32int boff; if(!f->dirty) return 0; if(oelem == nil) oelem = f->dir.elem; //print("fileMetaFlush %s->%s\n", oelem, f->dir.elem); fp = f->up; if(!sourceLock(fp->msource, -1)) return -1; /* can happen if source is clri'ed out from under us */ if(f->boff == NilBlock) goto Err1; b = sourceBlock(fp->msource, f->boff, OReadWrite); if(b == nil) goto Err1; if(!mbUnpack(&mb, b->data, fp->msource->dsize)) goto Err; if(!mbSearch(&mb, oelem, &i, &me)) goto Err; n = deSize(&f->dir); if(0)fprint(2, "old size %d new size %d\n", me.size, n); if(mbResize(&mb, &me, n)){ /* fits in the block */ mbDelete(&mb, i); if(strcmp(f->dir.elem, oelem) != 0) mbSearch(&mb, f->dir.elem, &i, &me2); dePack(&f->dir, &me); mbInsert(&mb, i, &me); mbPack(&mb); blockDirty(b); blockPut(b); sourceUnlock(fp->msource); f->dirty = 0; return 1; } /* * moving entry to another block * it is feasible for the fs to crash leaving two copies * of the directory entry. This is just too much work to * fix. Given that entries are only allocated in a block that * is less than PercentageFull, most modifications of meta data * will fit within the block. i.e. this code should almost * never be executed. */ boff = fileMetaAlloc(fp, &f->dir, f->boff+1); if(boff == NilBlock){ /* mbResize might have modified block */ mbPack(&mb); blockDirty(b); goto Err; } fprint(2, "fileMetaFlush moving entry from %ud -> %ud\n", f->boff, boff); f->boff = boff; /* make sure deletion goes to disk after new entry */ bb = sourceBlock(fp->msource, f->boff, OReadWrite); mbDelete(&mb, i); mbPack(&mb); blockDependency(b, bb, -1, nil, nil); blockPut(bb); blockDirty(b); blockPut(b); sourceUnlock(fp->msource); f->dirty = 0; return 1; Err: blockPut(b); Err1: sourceUnlock(fp->msource); return -1; }
/* * Walk the source tree making sure that the BtData * sources containing directory entries are okay. */ static void chkDir(Fsck *chk, char *name, Source *source, Source *meta) { int i; u32int a1, a2, nb, o; char *s, *nn; uchar *bm; Block *b, *bb; DirEntry de; Entry e1, e2; MetaBlock mb; MetaEntry me; Source *r, *mr; if(!chk->useventi && globalToLocal(source->score)==NilBlock && globalToLocal(meta->score)==NilBlock) return; if(!sourceLock2(source, meta, OReadOnly)){ warn(chk, "could not lock sources for %s: %R", name); return; } if(!sourceGetEntry(source, &e1) || !sourceGetEntry(meta, &e2)){ warn(chk, "could not load entries for %s: %R", name); return; } a1 = globalToLocal(e1.score); a2 = globalToLocal(e2.score); if((!chk->useventi && a1==NilBlock && a2==NilBlock) || (getBit(chk->smap, a1) && getBit(chk->smap, a2))){ sourceUnlock(source); sourceUnlock(meta); return; } setBit(chk->smap, a1); setBit(chk->smap, a2); bm = vtMemAllocZ(sourceGetDirSize(source)/8 + 1); nb = (sourceGetSize(meta) + meta->dsize - 1)/meta->dsize; for(o = 0; o < nb; o++){ b = sourceBlock(meta, o, OReadOnly); if(b == nil){ error(chk, "could not read block in meta file: %s[%ud]: %R", name, o); continue; } if(0) fprint(2, "source %V:%d block %d addr %d\n", source->score, source->offset, o, b->addr); if(b->addr != NilBlock && getBit(chk->errmap, b->addr)) warn(chk, "previously reported error in block %ux is in %s", b->addr, name); if(!mbUnpack(&mb, b->data, meta->dsize)){ error(chk, "could not unpack meta block: %s[%ud]: %R", name, o); blockPut(b); continue; } if(!chkMetaBlock(&mb)){ error(chk, "bad meta block: %s[%ud]: %R", name, o); blockPut(b); continue; } s = nil; for(i=mb.nindex-1; i>=0; i--){ meUnpack(&me, &mb, i); if(!deUnpack(&de, &me)){ error(chk, "could not unpack dir entry: %s[%ud][%d]: %R", name, o, i); continue; } if(s && strcmp(s, de.elem) <= 0) error(chk, "dir entry out of order: %s[%ud][%d] = %s last = %s", name, o, i, de.elem, s); vtMemFree(s); s = vtStrDup(de.elem); nn = smprint("%s/%s", name, de.elem); if(nn == nil){ error(chk, "out of memory"); continue; } if(chk->printdirs) if(de.mode&ModeDir) chk->print("%s/\n", nn); if(chk->printfiles) if(!(de.mode&ModeDir)) chk->print("%s\n", nn); if(!(de.mode & ModeDir)){ r = openSource(chk, source, nn, bm, de.entry, de.gen, 0, &mb, i, b); if(r != nil){ if(sourceLock(r, OReadOnly)){ scanSource(chk, nn, r); sourceUnlock(r); } sourceClose(r); } deCleanup(&de); free(nn); continue; } r = openSource(chk, source, nn, bm, de.entry, de.gen, 1, &mb, i, b); if(r == nil){ deCleanup(&de); free(nn); continue; } mr = openSource(chk, source, nn, bm, de.mentry, de.mgen, 0, &mb, i, b); if(mr == nil){ sourceClose(r); deCleanup(&de); free(nn); continue; } if(!(de.mode&ModeSnapshot) || chk->walksnapshots) chkDir(chk, nn, r, mr); sourceClose(mr); sourceClose(r); deCleanup(&de); free(nn); deCleanup(&de); } vtMemFree(s); blockPut(b); } nb = sourceGetDirSize(source); for(o=0; o<nb; o++){ if(getBit(bm, o)) continue; r = sourceOpen(source, o, OReadOnly, 0); if(r == nil) continue; warn(chk, "non referenced entry in source %s[%d]", name, o); if((bb = sourceBlock(source, o/(source->dsize/VtEntrySize), OReadOnly)) != nil){ if(bb->addr != NilBlock){ setBit(chk->errmap, bb->addr); chk->clre(chk, bb, o%(source->dsize/VtEntrySize)); chk->nclre++; } blockPut(bb); } sourceClose(r); } sourceUnlock(source); sourceUnlock(meta); vtMemFree(bm); }
/* * Retrieve the block containing the entry for r. * If a snapshot has happened, we might need * to get a new copy of the block. We avoid this * in the common case by caching the score for * the block and the last epoch in which it was valid. * * We use r->mode to tell the difference between active * file system sources (OReadWrite) and sources for the * snapshot file system (OReadOnly). */ static Block* sourceLoadBlock(Source *r, int mode) { uint32_t addr; Block *b; switch(r->mode) { default: assert(0); case OReadWrite: assert(r->mode == OReadWrite); /* * This needn't be true -- we might bump the low epoch * to reclaim some old blocks, but since this score is * OReadWrite, the blocks must all still be open, so none * are reclaimed. Thus it's okay that the epoch is so low. * Proceed. assert(r->epoch >= r->fs->elo); */ if(r->epoch == r->fs->ehi) { b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, OReadWrite); if(b == nil) return nil; assert(r->epoch == b->l.epoch); return b; } assert(r->parent != nil); if(!sourceLock(r->parent, OReadWrite)) return nil; b = sourceBlock(r->parent, r->offset/r->epb, OReadWrite); sourceUnlock(r->parent); if(b == nil) return nil; assert(b->l.epoch == r->fs->ehi); // fprint(2, "sourceLoadBlock %p %V => %V\n", r, r->score, b->score); memmove(r->score, b->score, VtScoreSize); r->scoreEpoch = b->l.epoch; r->tag = b->l.tag; r->epoch = r->fs->ehi; return b; case OReadOnly: addr = globalToLocal(r->score); if(addr == NilBlock) return cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, mode); b = cacheLocalData(r->fs->cache, addr, BtDir, r->tag, mode, r->scoreEpoch); if(b) return b; /* * If it failed because the epochs don't match, the block has been * archived and reclaimed. Rewalk from the parent and get the * new pointer. This can't happen in the OReadWrite case * above because blocks in the current epoch don't get * reclaimed. The fact that we're OReadOnly means we're * a snapshot. (Or else the file system is read-only, but then * the archiver isn't going around deleting blocks.) */ if(strcmp(vtGetError(), ELabelMismatch) == 0) { if(!sourceLock(r->parent, OReadOnly)) return nil; b = sourceBlock(r->parent, r->offset/r->epb, OReadOnly); sourceUnlock(r->parent); if(b) { fprint(2, "sourceAlloc: lost %V found %V\n", r->score, b->score); memmove(r->score, b->score, VtScoreSize); r->scoreEpoch = b->l.epoch; return b; } } return nil; } }
const ShaderProgram& ShaderManager::CreateShader(const std::string& name, ShaderParts requestedParts, const std::string& macros) { // lock shader maps std::unique_lock<std::mutex> shaderMapLock(m_shaderMutex); ShaderId shaderId{ name, macros }; auto it = m_shaders.find(shaderId); // shader exists if (it != m_shaders.end()) { // check if it has the requested binaries, and return it if (requestedParts.SubsetOf(it->second->parts)) { return it->second->program; } } // shader does not exist else { // insert new entry for shader auto ins = m_shaders.insert({ name, std::make_unique<ShaderStore>() }); it = ins.first; } ShaderStore* shader = it->second.get(); // release shader maps shaderMapLock.unlock(); // lock source maps (read only lock) std::shared_lock<std::shared_mutex> sourceLock(m_sourceMutex); // lock the shader itself size_t nameHash = ShaderIdHash()(shaderId); std::unique_lock<std::mutex> shaderLock(m_compileMutexes[nameHash % m_numCompileMutexes]); // find requested shader code std::string shaderSource; std::string shaderPath; { auto pathSourcePair = FindShaderCode(name.c_str()); shaderPath = pathSourcePair.first; shaderSource = pathSourcePair.second; } // determine what parts to compile, and compile them ShaderParts partsToCompile = requestedParts.SetSubtract(shader->parts); ShaderProgram program; try { program = CompileShaderInternal(shaderSource.c_str(), partsToCompile, macros.c_str()); } catch (gxapi::ShaderCompilationError& ex) { throw gxapi::ShaderCompilationError("Error while compiling shader \"" + shaderPath + "\"", ex.Subject()); } shader->parts = shader->parts.SetUnion(partsToCompile); if (partsToCompile.vs) { shader->program.vs = std::move(program.vs); } if (partsToCompile.hs) { shader->program.hs = std::move(program.hs); } if (partsToCompile.ds) { shader->program.ds = std::move(program.ds); } if (partsToCompile.gs) { shader->program.gs = std::move(program.gs); } if (partsToCompile.ps) { shader->program.ps = std::move(program.ps); } if (partsToCompile.cs) { shader->program.cs = std::move(program.cs); } return shader->program; }
ShaderProgram ShaderManager::CompileShader(const std::string& sourceCode, ShaderParts parts, const std::string& macros) { // lock source maps (read only lock) std::shared_lock<std::shared_mutex> sourceLock(m_sourceMutex); return CompileShaderInternal(sourceCode, parts, macros); }