// ********************************************************************** // // Copyright (c) 2003-2004 ZeroC, Inc. All rights reserved. // // This copy of Ice is licensed to you under the terms described in the // ICE_LICENSE file included in this distribution. // // ********************************************************************** #ifndef ICE_UTIL_CACHE_H #define ICE_UTIL_CACHE_H #include #include #include #include namespace IceUtil { // // An abstraction to efficiently populate a Cache, without holding // a lock while loading from a database. // template class Cache { public: // // Latch and CacheValue are implementation details; // application code should not use them. // struct Latch : public CountDownLatch { Latch() : CountDownLatch(1), useCount(0) { } int useCount; }; struct CacheValue { CacheValue(const Handle& o) : obj(o), latch(0) { } Handle obj; Latch* latch; }; typedef typename std::map::iterator Position; Handle getIfPinned(const Key&) const; void unpin(Position); bool unpin(const Key&); void clear(); size_t size() const; bool pin(const Key&, const Handle&); Handle pin(const Key&); Handle putIfAbsent(const Key&, const Handle&); protected: virtual Handle load(const Key&) = 0; virtual void pinned(const Handle&, Position) { } virtual ~Cache() { } private: Handle pinImpl(const Key&, const Handle&); typedef std::map CacheMap; Mutex _mutex; CacheMap _map; }; template Handle Cache::getIfPinned(const Key& key) const { Mutex::Lock sync(_mutex); typename CacheMap::const_iterator p = _map.find(key); if(p != _map.end()) { return (*p).second.obj; } else { return 0; } } template void Cache::unpin(typename Cache::Position p) { Mutex::Lock sync(_mutex); _map.erase(p); } template bool Cache::unpin(const Key& key) { Mutex::Lock sync(_mutex); return _map.erase(key) > 0; } template void Cache::clear() { Mutex::Lock sync(_mutex); _map.clear(); } template size_t Cache::size() const { Mutex::Lock sync(_mutex); return _map.size(); } template bool Cache::pin(const Key& key, const Handle& obj) { Mutex::Lock sync(_mutex); #if defined(_MSC_VER) && (_MSC_VER < 1300) std::pair ir = #else std::pair ir = #endif #if defined(_MSC_VER) _map.insert(CacheMap::value_type(key, CacheValue(obj))); #else _map.insert(typename CacheMap::value_type(key, CacheValue(obj))); #endif if(ir.second) { pinned(obj, ir.first); } return ir.second; } template Handle Cache::pin(const Key& key) { return pinImpl(key, 0); } template Handle Cache::putIfAbsent(const Key& key, const Handle& obj) { return pinImpl(key, obj); } template Handle Cache::pinImpl(const Key& key, const Handle& newObj) { Latch* latch = 0; Position p; for(;;) { { Mutex::Lock sync(_mutex); // // Clean up latch from previous loop // if(latch != 0) { if(--latch->useCount == 0) { delete latch; } latch = 0; } #if defined(_MSC_VER) && (_MSC_VER < 1300) std::pair ir = #else std::pair ir = #endif #if defined(_MSC_VER) _map.insert(CacheMap::value_type(key, CacheValue(0))); #else _map.insert(typename CacheMap::value_type(key, CacheValue(0))); #endif if(ir.second == false) { CacheValue& val = ir.first->second; if(val.obj != 0) { return val.obj; } // // Otherwise wait // if(val.latch == 0) { // // The first queued thread creates the latch // val.latch = new Latch; } latch = val.latch; latch->useCount++; } p = ir.first; } if(latch != 0) { latch->await(); // // p could be stale now, e.g. some other thread pinned and unpinned the // object while we were waiting. // So start over. // continue; } else { Handle obj; try { obj = load(key); } catch(...) { { Mutex::Lock sync(_mutex); latch = p->second.latch; p->second.latch = 0; _map.erase(p); } if(latch != 0) { assert(latch->getCount() == 1); latch->countDown(); } throw; } { Mutex::Lock sync(_mutex); latch = p->second.latch; p->second.latch = 0; try { if(obj != 0) { p->second.obj = obj; pinned(obj, p); } else { if(newObj == 0) { // // pin() did not find the object // // // The waiting threads will have to call load() to see by themselves. // _map.erase(p); } else { // // putIfAbsent() inserts key/newObj // p->second.obj = newObj; pinned(newObj, p); } } } catch(...) { if(latch != 0) { assert(latch->getCount() == 1); latch->countDown(); } throw; } } if(latch != 0) { assert(latch->getCount() == 1); latch->countDown(); } return obj; } } } } #endif