Mercurial > hg > orthanc-transfers
view Framework/OrthancInstancesCache.cpp @ 51:ec6bbdd80851 OrthancTransfers-1.2
closing branch OrthancTransfers-1.2
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 05 Dec 2022 14:25:21 +0100 |
parents | eb6edb022a00 |
children | 1e396fb509ca |
line wrap: on
line source
/** * Transfers accelerator plugin for Orthanc * Copyright (C) 2018-2021 Osimis S.A., Belgium * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * 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 * Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. **/ #include "OrthancInstancesCache.h" #include <Compatibility.h> // For std::unique_ptr #include <Logging.h> namespace OrthancPlugins { void OrthancInstancesCache::CacheAccessor::CheckValid() const { if (instance_ == NULL) { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } } OrthancInstancesCache::CacheAccessor::CacheAccessor(OrthancInstancesCache& cache, const std::string& instanceId) : lock_(cache.mutex_), instance_(NULL) { cache.CheckInvariants(); if (cache.index_.Contains(instanceId)) { // Move the instance at the end of the LRU recycling cache.index_.MakeMostRecent(instanceId); Content::const_iterator instance = cache.content_.find(instanceId); assert(instance != cache.content_.end() && instance->first == instanceId && instance->second != NULL); instance_ = instance->second; } } const DicomInstanceInfo& OrthancInstancesCache::CacheAccessor::GetInfo() const { CheckValid(); return instance_->GetInfo(); } void OrthancInstancesCache::CacheAccessor::GetChunk(std::string& chunk, std::string& md5, size_t offset, size_t size) { CheckValid(); return instance_->GetChunk(chunk, md5, offset, size); } void OrthancInstancesCache::CheckInvariants() { #ifndef NDEBUG size_t s = 0; assert(content_.size() == index_.GetSize()); for (Content::const_iterator it = content_.begin(); it != content_.end(); ++it) { assert(it->second != NULL); s += it->second->GetInfo().GetSize(); assert(index_.Contains(it->first)); } assert(s == memorySize_); if (memorySize_ > maxMemorySize_) { // It is only allowed to overtake the max memory size if the // cache contains a single, large DICOM instance assert(index_.GetSize() == 1 && content_.size() == 1 && memorySize_ == (content_.begin())->second->GetInfo().GetSize()); } #endif } void OrthancInstancesCache::RemoveOldest() { CheckInvariants(); assert(!index_.IsEmpty()); std::string oldest = index_.RemoveOldest(); Content::iterator instance = content_.find(oldest); assert(instance != content_.end() && instance->second != NULL); memorySize_ -= instance->second->GetInfo().GetSize(); delete instance->second; content_.erase(instance); } void OrthancInstancesCache::Store(const std::string& instanceId, std::unique_ptr<SourceDicomInstance>& instance) { if (instance.get() == NULL) { throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); } if (index_.Contains(instanceId)) { // This instance has been read by another thread since the cache // lookup, give up index_.MakeMostRecent(instanceId); return; } else { // Make room in the cache for the new instance while (!index_.IsEmpty() && memorySize_ + instance->GetInfo().GetSize() > maxMemorySize_) { RemoveOldest(); } CheckInvariants(); index_.AddOrMakeMostRecent(instanceId); memorySize_ += instance->GetInfo().GetSize(); content_[instanceId] = instance.release(); CheckInvariants(); } } OrthancInstancesCache::OrthancInstancesCache() : memorySize_(0), maxMemorySize_(512 * MB) // 512 MB by default { } OrthancInstancesCache::~OrthancInstancesCache() { CheckInvariants(); for (Content::iterator it = content_.begin(); it != content_.end(); ++it) { assert(it->second != NULL); delete it->second; } } size_t OrthancInstancesCache::GetMemorySize() { boost::mutex::scoped_lock lock(mutex_); return memorySize_; } size_t OrthancInstancesCache::GetMaxMemorySize() { boost::mutex::scoped_lock lock(mutex_); return maxMemorySize_; } void OrthancInstancesCache::SetMaxMemorySize(size_t size) { if (size <= 0) { throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); } boost::mutex::scoped_lock lock(mutex_); while (memorySize_ > size) { RemoveOldest(); } maxMemorySize_ = size; CheckInvariants(); } void OrthancInstancesCache::GetInstanceInfo(size_t& size, std::string& md5, const std::string& instanceId) { // Check whether the instance is part of the cache { CacheAccessor accessor(*this, instanceId); if (accessor.IsValid()) { size = accessor.GetInfo().GetSize(); md5 = accessor.GetInfo().GetMD5(); return; } } // The instance was not in the cache, load it std::unique_ptr<SourceDicomInstance> instance(new SourceDicomInstance(instanceId)); size = instance->GetInfo().GetSize(); md5 = instance->GetInfo().GetMD5(); // Store the just-loaded DICOM instance into the cache { boost::mutex::scoped_lock lock(mutex_); Store(instanceId, instance); } } void OrthancInstancesCache::GetChunk(std::string& chunk, std::string& md5, const std::string& instanceId, size_t offset, size_t size) { // Check whether the instance is part of the cache { CacheAccessor accessor(*this, instanceId); if (accessor.IsValid()) { // keeping this log for future (see TODO) LOG(INFO) << "++++ Chunk for " << instanceId << " is in cache"; accessor.GetChunk(chunk, md5, offset, size); return; } } // The instance was not in the cache, load it std::unique_ptr<SourceDicomInstance> instance(new SourceDicomInstance(instanceId)); instance->GetChunk(chunk, md5, offset, size); // Store the just-loaded DICOM instance into the cache { // keeping this log for future (see TODO) LOG(ERROR) << "---- Chunk for " << instanceId << " not in cache -> adding it"; boost::mutex::scoped_lock lock(mutex_); Store(instanceId, instance); } } void OrthancInstancesCache::GetChunk(std::string& chunk, std::string& md5, const TransferBucket& bucket, size_t chunkIndex) { GetChunk(chunk, md5, bucket.GetChunkInstanceId(chunkIndex), bucket.GetChunkOffset(chunkIndex), bucket.GetChunkSize(chunkIndex)); } }