// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab /* * Ceph - scalable distributed file system * * Copyright (C) 2004-2006 Sage Weil * * This is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software * Foundation. See file COPYING. * */ #ifndef CEPH_SHAREDCACHE_H #define CEPH_SHAREDCACHE_H #include #include #include #include #include "common/Mutex.h" #include "common/Cond.h" template class SharedLRU { typedef std::tr1::shared_ptr VPtr; typedef std::tr1::weak_ptr WeakVPtr; Mutex lock; size_t max_size; Cond cond; map >::iterator > contents; list > lru; map weak_refs; void trim_cache(list *to_release) { while (lru.size() > max_size) { to_release->push_back(lru.back().second); lru_remove(lru.back().first); } } void lru_remove(K key) { if (!contents.count(key)) return; lru.erase(contents[key]); contents.erase(key); } void lru_add(K key, VPtr val, list *to_release) { if (contents.count(key)) { lru.splice(lru.begin(), lru, contents[key]); } else { lru.push_front(make_pair(key, val)); contents[key] = lru.begin(); trim_cache(to_release); } } void remove(K key) { Mutex::Locker l(lock); weak_refs.erase(key); cond.Signal(); } class Cleanup { public: SharedLRU *cache; K key; Cleanup(SharedLRU *cache, K key) : cache(cache), key(key) {} void operator()(V *ptr) { cache->remove(key); delete ptr; } }; public: SharedLRU(size_t max_size = 20) : lock("SharedLRU::lock"), max_size(max_size) {} void set_size(size_t new_size) { list to_release; { Mutex::Locker l(lock); max_size = new_size; trim_cache(to_release); } } VPtr lower_bound(K key) { VPtr val; list to_release; { Mutex::Locker l(lock); bool retry = false; do { retry = false; if (weak_refs.empty()) break; typename map::iterator i = weak_refs.lower_bound(key); if (i == weak_refs.end()) --i; val = i->second.lock(); if (val) { lru_add(i->first, val, &to_release); } else { retry = true; } if (retry) cond.Wait(lock); } while (retry); } return val; } VPtr lookup(K key) { VPtr val; list to_release; { Mutex::Locker l(lock); bool retry = false; do { retry = false; if (weak_refs.count(key)) { val = weak_refs[key].lock(); if (val) { lru_add(key, val, &to_release); } else { retry = true; } } if (retry) cond.Wait(lock); } while (retry); } return val; } VPtr add(K key, V *value) { VPtr val(value, Cleanup(this, key)); list to_release; { Mutex::Locker l(lock); weak_refs.insert(make_pair(key, val)); lru_add(key, val, &to_release); } return val; } }; #endif