# HG changeset patch # User Sebastien Jodogne # Date 1572877118 -3600 # Node ID 4d809b2e1141d0d2b73ee43303bc6b6f1a290127 # Parent 87940f7156e036d9b468bc3140f8572c99d34170 better cache toolbox: MemoryObjectCache diff -r 87940f7156e0 -r 4d809b2e1141 Core/Cache/ICachePageProvider.h --- a/Core/Cache/ICachePageProvider.h Thu Oct 31 14:00:39 2019 +0100 +++ b/Core/Cache/ICachePageProvider.h Mon Nov 04 15:18:38 2019 +0100 @@ -38,13 +38,16 @@ namespace Orthanc { - class ICachePageProvider + namespace Deprecated { - public: - virtual ~ICachePageProvider() + class ICachePageProvider { - } + public: + virtual ~ICachePageProvider() + { + } - virtual IDynamicObject* Provide(const std::string& id) = 0; - }; + virtual IDynamicObject* Provide(const std::string& id) = 0; + }; + } } diff -r 87940f7156e0 -r 4d809b2e1141 Core/Cache/ICacheable.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Cache/ICacheable.h Mon Nov 04 15:18:38 2019 +0100 @@ -0,0 +1,49 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include + +namespace Orthanc +{ + class ICacheable : public boost::noncopyable + { + public: + virtual ~ICacheable() + { + } + + virtual size_t GetMemoryUsage() const = 0; + }; +} diff -r 87940f7156e0 -r 4d809b2e1141 Core/Cache/MemoryCache.cpp --- a/Core/Cache/MemoryCache.cpp Thu Oct 31 14:00:39 2019 +0100 +++ b/Core/Cache/MemoryCache.cpp Mon Nov 04 15:18:38 2019 +0100 @@ -38,71 +38,74 @@ namespace Orthanc { - MemoryCache::Page& MemoryCache::Load(const std::string& id) + namespace Deprecated { - // Reuse the cache entry if it already exists - Page* p = NULL; - if (index_.Contains(id, p)) + MemoryCache::Page& MemoryCache::Load(const std::string& id) { - VLOG(1) << "Reusing a cache page"; - assert(p != NULL); - index_.MakeMostRecent(id); + // Reuse the cache entry if it already exists + Page* p = NULL; + if (index_.Contains(id, p)) + { + VLOG(1) << "Reusing a cache page"; + assert(p != NULL); + index_.MakeMostRecent(id); + return *p; + } + + // The id is not in the cache yet. Make some room if the cache + // is full. + if (index_.GetSize() == cacheSize_) + { + VLOG(1) << "Dropping the oldest cache page"; + index_.RemoveOldest(p); + delete p; + } + + // Create a new cache page + std::auto_ptr result(new Page); + result->id_ = id; + result->content_.reset(provider_.Provide(id)); + + // Add the newly create page to the cache + VLOG(1) << "Registering new data in a cache page"; + p = result.release(); + index_.Add(id, p); return *p; } - // The id is not in the cache yet. Make some room if the cache - // is full. - if (index_.GetSize() == cacheSize_) + MemoryCache::MemoryCache(ICachePageProvider& provider, + size_t cacheSize) : + provider_(provider), + cacheSize_(cacheSize) { - VLOG(1) << "Dropping the oldest cache page"; - index_.RemoveOldest(p); - delete p; } - // Create a new cache page - std::auto_ptr result(new Page); - result->id_ = id; - result->content_.reset(provider_.Provide(id)); - - // Add the newly create page to the cache - VLOG(1) << "Registering new data in a cache page"; - p = result.release(); - index_.Add(id, p); - return *p; - } + void MemoryCache::Invalidate(const std::string& id) + { + Page* p = NULL; + if (index_.Contains(id, p)) + { + VLOG(1) << "Invalidating a cache page"; + assert(p != NULL); + delete p; + index_.Invalidate(id); + } + } - MemoryCache::MemoryCache(ICachePageProvider& provider, - size_t cacheSize) : - provider_(provider), - cacheSize_(cacheSize) - { - } + MemoryCache::~MemoryCache() + { + while (!index_.IsEmpty()) + { + Page* element = NULL; + index_.RemoveOldest(element); + assert(element != NULL); + delete element; + } + } - void MemoryCache::Invalidate(const std::string& id) - { - Page* p = NULL; - if (index_.Contains(id, p)) + IDynamicObject& MemoryCache::Access(const std::string& id) { - VLOG(1) << "Invalidating a cache page"; - assert(p != NULL); - delete p; - index_.Invalidate(id); + return *Load(id).content_; } } - - MemoryCache::~MemoryCache() - { - while (!index_.IsEmpty()) - { - Page* element = NULL; - index_.RemoveOldest(element); - assert(element != NULL); - delete element; - } - } - - IDynamicObject& MemoryCache::Access(const std::string& id) - { - return *Load(id).content_; - } } diff -r 87940f7156e0 -r 4d809b2e1141 Core/Cache/MemoryCache.h --- a/Core/Cache/MemoryCache.h Thu Oct 31 14:00:39 2019 +0100 +++ b/Core/Cache/MemoryCache.h Mon Nov 04 15:18:38 2019 +0100 @@ -39,32 +39,35 @@ namespace Orthanc { - /** - * WARNING: This class is NOT thread-safe. - **/ - class MemoryCache + namespace Deprecated { - private: - struct Page + /** + * WARNING: This class is NOT thread-safe. + **/ + class MemoryCache { - std::string id_; - std::auto_ptr content_; - }; + private: + struct Page + { + std::string id_; + std::auto_ptr content_; + }; - ICachePageProvider& provider_; - size_t cacheSize_; - LeastRecentlyUsedIndex index_; + ICachePageProvider& provider_; + size_t cacheSize_; + LeastRecentlyUsedIndex index_; - Page& Load(const std::string& id); + Page& Load(const std::string& id); - public: - MemoryCache(ICachePageProvider& provider, - size_t cacheSize); + public: + MemoryCache(ICachePageProvider& provider, + size_t cacheSize); - ~MemoryCache(); + ~MemoryCache(); - IDynamicObject& Access(const std::string& id); + IDynamicObject& Access(const std::string& id); - void Invalidate(const std::string& id); - }; + void Invalidate(const std::string& id); + }; + } } diff -r 87940f7156e0 -r 4d809b2e1141 Core/Cache/MemoryObjectCache.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Cache/MemoryObjectCache.cpp Mon Nov 04 15:18:38 2019 +0100 @@ -0,0 +1,259 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "../PrecompiledHeaders.h" +#include "MemoryObjectCache.h" + +namespace Orthanc +{ + class MemoryObjectCache::Item : public boost::noncopyable + { + private: + ICacheable* value_; + boost::posix_time::ptime time_; + + public: + explicit Item(ICacheable* value) : // Takes ownership + value_(value), + time_(boost::posix_time::second_clock::local_time()) + { + if (value == NULL) + { + throw OrthancException(ErrorCode_NullPointer); + } + } + + ~Item() + { + assert(value_ != NULL); + delete value_; + } + + ICacheable& GetValue() const + { + assert(value_ != NULL); + return *value_; + } + + const boost::posix_time::ptime& GetTime() const + { + return time_; + } + }; + + + void MemoryObjectCache::Recycle(size_t targetSize) + { + // WARNING: "cacheMutex_" must be locked + while (currentSize_ > targetSize) + { + assert(!content_.IsEmpty()); + + Item* item = NULL; + content_.RemoveOldest(item); + + assert(item != NULL); + const size_t size = item->GetValue().GetMemoryUsage(); + delete item; + + assert(currentSize_ >= size); + currentSize_ -= size; + } + + // Post-condition: "currentSize_ <= targetSize" + } + + + MemoryObjectCache::MemoryObjectCache() : + currentSize_(0), + maxSize_(100 * 1024 * 1024) // 100 MB + { + } + + + MemoryObjectCache::~MemoryObjectCache() + { + Recycle(0); + assert(content_.IsEmpty()); + } + + + size_t MemoryObjectCache::GetMaximumSize() + { +#if !defined(__EMSCRIPTEN__) + boost::mutex::scoped_lock lock(cacheMutex_); +#endif + + return maxSize_; + } + + + void MemoryObjectCache::SetMaximumSize(size_t size) + { + if (size == 0) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + +#if !defined(__EMSCRIPTEN__) + // Make sure no accessor is currently open (as its data may be + // removed if recycling is needed) + WriterLock contentLock(contentMutex_); + + // Lock the global structure of the cache + boost::mutex::scoped_lock cacheLock(cacheMutex_); +#endif + + Recycle(size); + maxSize_ = size; + } + + + void MemoryObjectCache::Acquire(const std::string& key, + ICacheable* value) + { + std::auto_ptr item(new Item(value)); + + if (value == NULL) + { + throw OrthancException(ErrorCode_NullPointer); + } + else + { +#if !defined(__EMSCRIPTEN__) + // Make sure no accessor is currently open (as its data may be + // removed if recycling is needed) + WriterLock contentLock(contentMutex_); + + // Lock the global structure of the cache + boost::mutex::scoped_lock cacheLock(cacheMutex_); +#endif + + const size_t size = item->GetValue().GetMemoryUsage(); + + if (size > maxSize_) + { + // This object is too large to be stored in the cache, discard it + } + else if (content_.Contains(key)) + { + // Value already stored, don't overwrite the old value + content_.MakeMostRecent(key); + } + else + { + Recycle(maxSize_ - size); // Post-condition: currentSize_ <= maxSize_ - size + assert(currentSize_ + size <= maxSize_); + + content_.Add(key, item.release()); + currentSize_ += size; + } + } + } + + + void MemoryObjectCache::Invalidate(const std::string& key) + { +#if !defined(__EMSCRIPTEN__) + // Make sure no accessor is currently open (as it may correspond + // to the key to remove) + WriterLock contentLock(contentMutex_); + + // Lock the global structure of the cache + boost::mutex::scoped_lock cacheLock(cacheMutex_); +#endif + + Item* item = NULL; + if (content_.Contains(key, item)) + { + assert(item != NULL); + const size_t size = item->GetValue().GetMemoryUsage(); + delete item; + + content_.Invalidate(key); + + assert(currentSize_ >= size); + currentSize_ -= size; + } + } + + + MemoryObjectCache::Reader::Reader(MemoryObjectCache& cache, + const std::string& key) : +#if !defined(__EMSCRIPTEN__) + contentLock_(cache.contentMutex_), + cacheLock_(cache.cacheMutex_), +#endif + item_(NULL) + { + if (cache.content_.Contains(key, item_)) + { + cache.content_.MakeMostRecent(key); + } + +#if !defined(__EMSCRIPTEN__) + cacheLock_.unlock(); + + if (item_ == NULL) + { + contentLock_.unlock(); + } +#endif + } + + + ICacheable& MemoryObjectCache::Reader::GetValue() const + { + if (IsValid()) + { + return item_->GetValue(); + } + else + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + } + + + const boost::posix_time::ptime& MemoryObjectCache::Reader::GetTime() const + { + if (IsValid()) + { + return item_->GetTime(); + } + else + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + } +} diff -r 87940f7156e0 -r 4d809b2e1141 Core/Cache/MemoryObjectCache.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Cache/MemoryObjectCache.h Mon Nov 04 15:18:38 2019 +0100 @@ -0,0 +1,109 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "ICacheable.h" +#include "LeastRecentlyUsedIndex.h" + +#if !defined(__EMSCRIPTEN__) +// Multithreading is not supported in WebAssembly +# include +#endif + +#include + + +namespace Orthanc +{ + class MemoryObjectCache : public boost::noncopyable + { + private: + class Item; + +#if !defined(__EMSCRIPTEN__) + typedef boost::unique_lock WriterLock; + typedef boost::shared_lock ReaderLock; + + // This mutex protects modifications to the structure of the cache (monitor) + boost::mutex cacheMutex_; + + // This mutex protects modifications to the items that are stored in the cache + boost::shared_mutex contentMutex_; +#endif + + size_t currentSize_; + size_t maxSize_; + LeastRecentlyUsedIndex content_; + + void Recycle(size_t targetSize); + + public: + MemoryObjectCache(); + + ~MemoryObjectCache(); + + size_t GetMaximumSize(); + + void SetMaximumSize(size_t size); + + void Acquire(const std::string& key, + ICacheable* value); + + void Invalidate(const std::string& key); + + class Reader : public boost::noncopyable + { + private: +#if !defined(__EMSCRIPTEN__) + ReaderLock contentLock_; + boost::mutex::scoped_lock cacheLock_; +#endif + + Item* item_; + + public: + Reader(MemoryObjectCache& cache, + const std::string& key); + + bool IsValid() const + { + return item_ != NULL; + } + + ICacheable& GetValue() const; + + const boost::posix_time::ptime& GetTime() const; + }; + }; +} diff -r 87940f7156e0 -r 4d809b2e1141 Core/Cache/MemoryStringCache.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Cache/MemoryStringCache.cpp Mon Nov 04 15:18:38 2019 +0100 @@ -0,0 +1,84 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#include "../PrecompiledHeaders.h" +#include "MemoryStringCache.h" + +namespace Orthanc +{ + class MemoryStringCache::StringValue : public ICacheable + { + private: + std::string content_; + + public: + StringValue(const std::string& content) : + content_(content) + { + } + + const std::string& GetContent() const + { + return content_; + } + + virtual size_t GetMemoryUsage() const + { + return content_.size(); + } + }; + + + void MemoryStringCache::Add(const std::string& key, + const std::string& value) + { + cache_.Acquire(key, new StringValue(value)); + } + + + bool MemoryStringCache::Fetch(std::string& value, + const std::string& key) + { + MemoryObjectCache::Reader reader(cache_, key); + + if (reader.IsValid()) + { + value = dynamic_cast(reader.GetValue()).GetContent(); + return true; + } + else + { + return false; + } + } +} diff -r 87940f7156e0 -r 4d809b2e1141 Core/Cache/MemoryStringCache.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Core/Cache/MemoryStringCache.h Mon Nov 04 15:18:38 2019 +0100 @@ -0,0 +1,73 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "MemoryObjectCache.h" + +namespace Orthanc +{ + /** + * Facade object around "MemoryObjectCache" that caches a dictionary + * of strings, using the "fetch/add" paradigm of memcached. + **/ + class MemoryStringCache : public boost::noncopyable + { + private: + class StringValue; + + MemoryObjectCache cache_; + + public: + size_t GetMaximumSize() + { + return cache_.GetMaximumSize(); + } + + void SetMaximumSize(size_t size) + { + cache_.SetMaximumSize(size); + } + + void Add(const std::string& key, + const std::string& value); + + void Invalidate(const std::string& key) + { + cache_.Invalidate(key); + } + + bool Fetch(std::string& value, + const std::string& key); + }; +} diff -r 87940f7156e0 -r 4d809b2e1141 Core/Cache/SharedArchive.h --- a/Core/Cache/SharedArchive.h Thu Oct 31 14:00:39 2019 +0100 +++ b/Core/Cache/SharedArchive.h Mon Nov 04 15:18:38 2019 +0100 @@ -57,7 +57,7 @@ size_t maxSize_; boost::mutex mutex_; Archive archive_; - Orthanc::LeastRecentlyUsedIndex lru_; + LeastRecentlyUsedIndex lru_; void RemoveInternal(const std::string& id); diff -r 87940f7156e0 -r 4d809b2e1141 OrthancServer/ServerContext.h --- a/OrthancServer/ServerContext.h Thu Oct 31 14:00:39 2019 +0100 +++ b/OrthancServer/ServerContext.h Mon Nov 04 15:18:38 2019 +0100 @@ -120,7 +120,7 @@ } }; - class DicomCacheProvider : public ICachePageProvider + class DicomCacheProvider : public Deprecated::ICachePageProvider // TODO { private: ServerContext& context_; @@ -186,7 +186,7 @@ DicomCacheProvider provider_; boost::mutex dicomCacheMutex_; - MemoryCache dicomCache_; + Deprecated::MemoryCache dicomCache_; // TODO LuaScripting mainLua_; LuaScripting filterLua_; diff -r 87940f7156e0 -r 4d809b2e1141 Resources/CMake/OrthancFrameworkConfiguration.cmake --- a/Resources/CMake/OrthancFrameworkConfiguration.cmake Thu Oct 31 14:00:39 2019 +0100 +++ b/Resources/CMake/OrthancFrameworkConfiguration.cmake Mon Nov 04 15:18:38 2019 +0100 @@ -123,6 +123,8 @@ set(ORTHANC_CORE_SOURCES_INTERNAL ${ORTHANC_ROOT}/Core/Cache/MemoryCache.cpp + ${ORTHANC_ROOT}/Core/Cache/MemoryObjectCache.cpp + ${ORTHANC_ROOT}/Core/Cache/MemoryStringCache.cpp ${ORTHANC_ROOT}/Core/ChunkedBuffer.cpp ${ORTHANC_ROOT}/Core/DicomFormat/DicomTag.cpp ${ORTHANC_ROOT}/Core/EnumerationDictionary.h diff -r 87940f7156e0 -r 4d809b2e1141 UnitTestsSources/MemoryCacheTests.cpp --- a/UnitTestsSources/MemoryCacheTests.cpp Thu Oct 31 14:00:39 2019 +0100 +++ b/UnitTestsSources/MemoryCacheTests.cpp Mon Nov 04 15:18:38 2019 +0100 @@ -40,6 +40,7 @@ #include #include "../Core/Cache/MemoryCache.h" +#include "../Core/Cache/MemoryStringCache.h" #include "../Core/Cache/SharedArchive.h" #include "../Core/IDynamicObject.h" #include "../Core/Logging.h" @@ -212,7 +213,7 @@ } }; - class IntegerProvider : public Orthanc::ICachePageProvider + class IntegerProvider : public Orthanc::Deprecated::ICachePageProvider { public: std::string log_; @@ -231,7 +232,7 @@ IntegerProvider provider; { - Orthanc::MemoryCache cache(provider, 3); + Orthanc::Deprecated::MemoryCache cache(provider, 3); cache.Access("42"); // 42 -> exit cache.Access("43"); // 43, 42 -> exit cache.Access("45"); // 45, 43, 42 -> exit @@ -317,3 +318,51 @@ ASSERT_EQ(2u, count); } + + +TEST(MemoryStringCache, Basic) +{ + Orthanc::MemoryStringCache c; + ASSERT_THROW(c.SetMaximumSize(0), Orthanc::OrthancException); + + c.SetMaximumSize(2); + + std::string v; + ASSERT_FALSE(c.Fetch(v, "hello")); + + c.Add("hello", "a"); + ASSERT_TRUE(c.Fetch(v, "hello")); ASSERT_EQ("a", v); + ASSERT_FALSE(c.Fetch(v, "hello2")); + ASSERT_FALSE(c.Fetch(v, "hello3")); + + c.Add("hello2", "b"); + ASSERT_TRUE(c.Fetch(v, "hello")); ASSERT_EQ("a", v); + ASSERT_TRUE(c.Fetch(v, "hello2")); ASSERT_EQ("b", v); + ASSERT_FALSE(c.Fetch(v, "hello3")); + + c.Add("hello3", "too large value"); + ASSERT_TRUE(c.Fetch(v, "hello")); ASSERT_EQ("a", v); + ASSERT_TRUE(c.Fetch(v, "hello2")); ASSERT_EQ("b", v); + ASSERT_FALSE(c.Fetch(v, "hello3")); + + c.Add("hello3", "c"); + ASSERT_FALSE(c.Fetch(v, "hello")); // Recycled + ASSERT_TRUE(c.Fetch(v, "hello2")); ASSERT_EQ("b", v); + ASSERT_TRUE(c.Fetch(v, "hello3")); ASSERT_EQ("c", v); +} + + +TEST(MemoryStringCache, Invalidate) +{ + Orthanc::MemoryStringCache c; + c.Add("hello", "a"); + c.Add("hello2", "b"); + + std::string v; + ASSERT_TRUE(c.Fetch(v, "hello")); ASSERT_EQ("a", v); + ASSERT_TRUE(c.Fetch(v, "hello2")); ASSERT_EQ("b", v); + + c.Invalidate("hello"); + ASSERT_FALSE(c.Fetch(v, "hello")); + ASSERT_TRUE(c.Fetch(v, "hello2")); ASSERT_EQ("b", v); +}