#ifndef MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING_CACHINGSTORE_H_ #define MESSMER_BLOCKSTORE_IMPLEMENTATIONS_CACHING_CACHINGSTORE_H_ #include #include #include #include #include #include template class CachingStore { public: CachingStore() {} //TODO Init member variables //TODO Enforce CachedResourceRef inherits from CachedResource class CachedResource { public: //TODO Better way to initialize CachedResource(): _cachingStore(nullptr), _key(Key::CreateRandomKey()) {} void init(CachingStore *cachingStore, const Key &key) { _cachingStore = cachingStore; _key = key; } virtual ~CachedResource() { _cachingStore->release(_key); } private: CachingStore *_cachingStore; //TODO We're storing Key twice (here and in the base resource). Rather use getKey() on the base resource if possible somehow. Key _key; }; std::unique_ptr add(std::unique_ptr resource); std::unique_ptr load(const Key &key); void remove(std::unique_ptr block); protected: //TODO Template instead of virtual for getKey? virtual const Key &getKey(const Resource &resource) const = 0; virtual std::unique_ptr loadFromBaseStore(const Key &key) = 0; virtual void removeFromBaseStore(std::unique_ptr resource) = 0; private: class OpenResource { public: OpenResource(std::unique_ptr resource): _resource(std::move(resource)), _refCount(0) {} Resource *getReference() { ++_refCount; return _resource.get(); } void releaseReference() { --_refCount; } bool refCountIsZero() const { return 0 == _refCount; } std::unique_ptr moveResourceOut() { return std::move(_resource); } private: std::unique_ptr _resource; uint32_t _refCount; }; std::mutex _mutex; std::map _openResources; std::map>> _resourcesToRemove; std::unique_ptr _add(std::unique_ptr resource); std::unique_ptr _createCachedResourceRef(Resource *resource, const Key &key); void release(const Key &key); friend class CachedResource; DISALLOW_COPY_AND_ASSIGN(CachingStore); }; template std::unique_ptr CachingStore::add(std::unique_ptr resource) { std::lock_guard lock(_mutex); return _add(std::move(resource)); } template std::unique_ptr CachingStore::_add(std::unique_ptr resource) { auto insertResult = _openResources.emplace(getKey(*resource), std::move(resource)); assert(true == insertResult.second); return _createCachedResourceRef(insertResult.first->second.getReference(), getKey(*resource)); } template std::unique_ptr CachingStore::_createCachedResourceRef(Resource *resource, const Key &key) { auto resourceRef = std::make_unique(resource); resourceRef->init(this, getKey(*resource)); return std::move(resourceRef); } template std::unique_ptr CachingStore::load(const Key &key) { std::lock_guard lock(_mutex); auto found = _openResources.find(key); if (found == _openResources.end()) { auto resource = loadFromBaseStore(key); if (resource.get() == nullptr) { return nullptr; } return _add(std::move(resource)); } else { return _createCachedResourceRef(found->second.getReference(), key); } } template void CachingStore::remove(std::unique_ptr resource) { auto insertResult = _resourcesToRemove.emplace(getKey(*resource), std::promise>()); assert(true == insertResult.second); resource.reset(); //Wait for last resource user to release it auto resourceToRemove = insertResult.first->second.get_future().get(); removeFromBaseStore(std::move(resourceToRemove)); } template void CachingStore::release(const Key &key) { std::lock_guard lock(_mutex); auto found = _openResources.find(key); assert (found != _openResources.end()); found->second.releaseReference(); if (found->second.refCountIsZero()) { auto foundToRemove = _resourcesToRemove.find(key); if (foundToRemove != _resourcesToRemove.end()) { foundToRemove->second.set_value(found->second.moveResourceOut()); } _openResources.erase(found); } } #endif