if (next_off < off + len) {
holes[next_off] = (size_t) (off + len - next_off);
}
- // FIXME: consolidate holes
}
-list<Bufferhead*> Filecache::simplify()
+void Filecache::simplify(list<Bufferhead*>& removed)
{
- list<Bufferhead*> removed;
map<off_t, Bufferhead*>::iterator start, next;
start = buffer_map.begin();
while (start != buffer_map.end()) {
(*start)->second->state == (*next)->second->state &&
(*start)->second->offset + (*start)->second->len == (*next)->second->offset) {
(*start)->second->claim_append((*next)->second);
- buffer_map.erase((*next)->first);
+ buffer_map.erase((*next)->first); (*next)->second->put();
removed.push_back((*next)->second);
next++;
} else {
}
start = next;
}
- return removed;
}
void Filecache::copy_out(size_t size, off_t offset, char *dst)
bcache_map[bh->ino] = fc;
}
if (fc->buffermap.count(bh->offset)) assert(0); // fail loudly if offset already exists!
- fc->buffer_map[bh->offset] = bh;
- lru.lru_insert_top(bh);
+ fc->buffer_map[bh->offset] = bh; bh->get();
+ lru.lru_insert_top(bh); bh->get();
clean_size += bh->len;
}
void Buffercache::simplify(inodeno_t ino)
{
Filecache *fc = bcache_map[ino];
- list<Bufferhead*> removed = fc->simplify();
+ list<Bufferhead*> removed;
+ fc->simplify(&removed);
for (list<Bufferhead*>::iterator it = removed.begin();
it != removed.end();
it++) {
- lru.lru_remove(*it);
- delete *it;
+ lru.lru_remove(*it); (*it)->put();
+ if (dirty_buffers.count(*it)) {
+ dirty_buffers.erase(*it);
+ (*it)->put();
+ }
+ if (bcache_map[ino]->dirty_buffers.count(*it)) {
+ bcache_map[ino]->dirty_buffers.erase(*it)
+ (*it)->put();
+ }
}
}
void Buffercache::map_or_alloc(inodeno_t ino, size_t len, off_t off,
- map<off_t, Bufferhead*> *buffers,
- map<off_t, Bufferhead*> *inflight)
+ map<off_t, Bufferhead*>& buffers,
+ map<off_t, Bufferhead*>& inflight)
{
Filecache *fc = bcache_map[ino];
map<off_t, size_t> holes;
{
assert(bh->state == BUFH_STATE_CLEAN);
assert(bh->lru_is_expirable());
- bcache_map[bh->ino]->buffer_map.erase(bh->offset);
- lru.lru_remove(bh);
clean_size -= bh->len;
- delete bh;
+ lru.lru_remove(bh); bh->put();
+ assert(bh->ref == 1); // next put is going to delete it
+ bcache_map[bh->ino]->buffer_map.erase(bh->offset); bh->put();
}
void Buffercache::release_file(inodeno_t ino)
class Buffercache;
class Bufferhead : public LRUObject {
- // reference counter
- int ref;
- void get() {
- if (ref == 0) lru_pin();
- ++ref;
- }
- void put() {
- --ref;
- if (ref == 0) lru_unpin();
- }
-
public: // FIXME: make more private and write some accessors
off_t offset;
size_t len;
// write_waiters: threads waiting for writes into the buffer
list<Cond*> read_waiters, write_waiters;
Buffercache *bc;
+ ref = 0;
// cons/destructors
Bufferhead(inodeno_t ino, off_t off, size_t len, Buffercache *bc, int state=BUFHD_STATE_CLEAN) {
- this->ref = 0;
this->ino = ino;
this->offset = off;
this->len = len;
}
~Bufferhead() {
- // no need to delete bufferlist bufferptr's explicitly; ~list() does that (since it's list<bufferptr>, not list<bufferptr*>)
+ list<bufferptr> bl = bh->bl.buffers();
+ for (list<bufferptr>::iterator it == bl.begin();
+ it != bl.end();
+ it++) {
+ delete *it;
+ }
}
//Bufferhead(inodeno_t ino, off_t off, size_t len, int state);
-
// ~Bufferhead(); FIXME: need to mesh with allocator scheme
-
-
- // -- wait for read, write: these will block
- // i think this will work okay? and reference coutning in the waiter makes sure the wakeup fn doesn't
- // inadvertantly unpin the bufferhead before the waiters get to go
- void wait_for_read(Mutex& lock) {
- Cond cond; // on local stack
- get();
- read_waiters.push_back(&cond);
- cond.Wait(lock);
- put();
- }
- void wait_for_write(Mutex& lock) {
- Cond cond; // on local stack
- get();
- write_waiters.push_back(&cond);
- cond.Wait(lock);
- put();
+
+ void get() {
+ ref++;
+ assert(ref > 0);
+ }
+
+ void put() {
+ assert (ref > 0);
+ ref--;
+ if (ref == 0) {
+ assert(!lru_pinned);
+ delete this;
+ }
}
+ void add_read_waiter(Cond *cond) {
+ read_waiters->push_back(cond);
+ lru_pin();
+ }
+
+ void add_write_waiter(Cond *cond) {
+ write_waiters->push_back(cond);
+ lru_pin();
+ }
+
void wakeup_read_waiters() {
for (list<Cond*>::iterator it = read_waiters.begin();
it != read_waiters.end();
(*it)->Signal();
}
read_waiters.clear();
+ if (write_waiters.empty()) lru_unpin();
}
void wakeup_write_waiters() {
it++) {
(*it)->Signal();
}
+ write_waiters.clear();
+ if (read_waiters.empty()) lru_unpin();
}
void miss_start() {
state = BUFHD_STATE_DIRTY;
bc->dirty_size += bh->len;
bc->clean_size -= bh->len;
- bc->dirty_map[last_written] = this;
+ if (bc->dirty_buffers.count(offset)) {
+ bc->dirty_buffers.insert(this);
+ get();
+ }
+ if (bc->bcache_map[ino]->dirty_buffers.count(offset)) {
+ bc->bcache_map[ino]->dirty_buffers.insert(this);
+ get();
+ }
}
}
void flush_finish() {
assert(state == BUFHD_STATE_INFLIGHT);
state = BUFHD_STATE_CLEAN;
+ last_written = time();
bc->flush_size -= len;
bc->clean_size += len;
+ bc->dirty_buffers.erase(this); put();
+ bc->bcache_map[ino]->dirty_buffers.erase(this); put();
wakeup_write_waiters(); // readers never wait on flushes
}
class Filecache {
public:
map<off_t, Bufferhead*> buffer_map;
+ set<Bufferhead*> dirty_buffers;
+ list<Cond*> waitfor_flushed;
size_t length() {
size_t len = 0;
void map_existing(size_t len, off_t start_off,
map<off_t, Bufferhead*>& hits, inflight,
map<off_t, size_t>& holes);
- void simplify();
+ void simplify(list<Bufferhead*>& removed);
};
map<inodeno_t, Filecache*> bcache_map;
LRU lru;
size_t dirty_size = 0, flushing_size = 0, clean_size = 0;
- map<time_t, Bufferhead*> dirty_map;
-
+ set<Bufferhead*> dirty_buffers;
+ list<Cond*> waitfor_flushed;
+
// FIXME: constructor & destructor need to mesh with allocator scheme
~Buffercache() {
// FIXME: make sure all buffers are cleaned and then free them