Mercurial > hg > orthanc-transfers
annotate Framework/OrthancInstancesCache.cpp @ 77:1e396fb509ca default
updated copyright, as Orthanc Team now replaces Osimis
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 30 May 2024 22:44:10 +0200 |
parents | eb6edb022a00 |
children |
rev | line source |
---|---|
0 | 1 /** |
2 * Transfers accelerator plugin for Orthanc | |
77
1e396fb509ca
updated copyright, as Orthanc Team now replaces Osimis
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
41
diff
changeset
|
3 * Copyright (C) 2018-2023 Osimis S.A., Belgium |
1e396fb509ca
updated copyright, as Orthanc Team now replaces Osimis
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
41
diff
changeset
|
4 * Copyright (C) 2024-2024 Orthanc Team SRL, Belgium |
1e396fb509ca
updated copyright, as Orthanc Team now replaces Osimis
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
41
diff
changeset
|
5 * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium |
0 | 6 * |
7 * This program is free software: you can redistribute it and/or | |
8 * modify it under the terms of the GNU Affero General Public License | |
9 * as published by the Free Software Foundation, either version 3 of | |
10 * the License, or (at your option) any later version. | |
11 * | |
12 * This program is distributed in the hope that it will be useful, but | |
13 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 * Affero General Public License for more details. | |
16 * | |
17 * You should have received a copy of the GNU Affero General Public License | |
18 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 **/ | |
20 | |
21 | |
22 #include "OrthancInstancesCache.h" | |
23 | |
25
dfc43678aecb
replacing deprecated std::auto_ptr by std::unique_ptr
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
19
diff
changeset
|
24 #include <Compatibility.h> // For std::unique_ptr |
41 | 25 #include <Logging.h> |
0 | 26 |
27 namespace OrthancPlugins | |
28 { | |
29 void OrthancInstancesCache::CacheAccessor::CheckValid() const | |
30 { | |
31 if (instance_ == NULL) | |
32 { | |
33 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); | |
34 } | |
35 } | |
36 | |
37 | |
38 OrthancInstancesCache::CacheAccessor::CacheAccessor(OrthancInstancesCache& cache, | |
39 const std::string& instanceId) : | |
40 lock_(cache.mutex_), | |
41 instance_(NULL) | |
42 { | |
43 cache.CheckInvariants(); | |
44 | |
45 if (cache.index_.Contains(instanceId)) | |
46 { | |
47 // Move the instance at the end of the LRU recycling | |
48 cache.index_.MakeMostRecent(instanceId); | |
49 | |
50 Content::const_iterator instance = cache.content_.find(instanceId); | |
51 assert(instance != cache.content_.end() && | |
52 instance->first == instanceId && | |
53 instance->second != NULL); | |
54 | |
55 instance_ = instance->second; | |
56 } | |
57 } | |
58 | |
59 | |
60 const DicomInstanceInfo& OrthancInstancesCache::CacheAccessor::GetInfo() const | |
61 { | |
62 CheckValid(); | |
63 return instance_->GetInfo(); | |
64 } | |
65 | |
66 | |
67 void OrthancInstancesCache::CacheAccessor::GetChunk(std::string& chunk, | |
68 std::string& md5, | |
69 size_t offset, | |
70 size_t size) | |
71 { | |
72 CheckValid(); | |
73 return instance_->GetChunk(chunk, md5, offset, size); | |
74 } | |
75 | |
76 | |
77 void OrthancInstancesCache::CheckInvariants() | |
78 { | |
79 #ifndef NDEBUG | |
80 size_t s = 0; | |
81 | |
82 assert(content_.size() == index_.GetSize()); | |
83 | |
84 for (Content::const_iterator it = content_.begin(); | |
85 it != content_.end(); ++it) | |
86 { | |
87 assert(it->second != NULL); | |
88 s += it->second->GetInfo().GetSize(); | |
89 | |
90 assert(index_.Contains(it->first)); | |
91 } | |
92 | |
93 assert(s == memorySize_); | |
94 | |
95 if (memorySize_ > maxMemorySize_) | |
96 { | |
97 // It is only allowed to overtake the max memory size if the | |
98 // cache contains a single, large DICOM instance | |
99 assert(index_.GetSize() == 1 && | |
100 content_.size() == 1 && | |
101 memorySize_ == (content_.begin())->second->GetInfo().GetSize()); | |
102 } | |
103 #endif | |
104 } | |
105 | |
106 | |
107 void OrthancInstancesCache::RemoveOldest() | |
108 { | |
109 CheckInvariants(); | |
110 | |
111 assert(!index_.IsEmpty()); | |
112 | |
113 std::string oldest = index_.RemoveOldest(); | |
114 | |
115 Content::iterator instance = content_.find(oldest); | |
116 assert(instance != content_.end() && | |
117 instance->second != NULL); | |
118 | |
119 memorySize_ -= instance->second->GetInfo().GetSize(); | |
120 delete instance->second; | |
121 content_.erase(instance); | |
122 } | |
123 | |
124 | |
125 void OrthancInstancesCache::Store(const std::string& instanceId, | |
25
dfc43678aecb
replacing deprecated std::auto_ptr by std::unique_ptr
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
19
diff
changeset
|
126 std::unique_ptr<SourceDicomInstance>& instance) |
0 | 127 { |
128 if (instance.get() == NULL) | |
129 { | |
130 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); | |
131 } | |
132 | |
133 if (index_.Contains(instanceId)) | |
134 { | |
135 // This instance has been read by another thread since the cache | |
136 // lookup, give up | |
137 index_.MakeMostRecent(instanceId); | |
138 return; | |
139 } | |
140 else | |
141 { | |
142 // Make room in the cache for the new instance | |
143 while (!index_.IsEmpty() && | |
144 memorySize_ + instance->GetInfo().GetSize() > maxMemorySize_) | |
145 { | |
146 RemoveOldest(); | |
147 } | |
148 | |
149 CheckInvariants(); | |
150 | |
151 index_.AddOrMakeMostRecent(instanceId); | |
152 memorySize_ += instance->GetInfo().GetSize(); | |
153 content_[instanceId] = instance.release(); | |
154 | |
155 CheckInvariants(); | |
156 } | |
157 } | |
158 | |
159 | |
8
4c3437217518
fix for compatibility with simplified OrthancPluginCppWrapper
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
0
diff
changeset
|
160 OrthancInstancesCache::OrthancInstancesCache() : |
0 | 161 memorySize_(0), |
162 maxMemorySize_(512 * MB) // 512 MB by default | |
163 { | |
164 } | |
165 | |
166 | |
167 OrthancInstancesCache::~OrthancInstancesCache() | |
168 { | |
169 CheckInvariants(); | |
170 | |
171 for (Content::iterator it = content_.begin(); | |
172 it != content_.end(); ++it) | |
173 { | |
174 assert(it->second != NULL); | |
175 delete it->second; | |
176 } | |
177 } | |
178 | |
179 | |
180 size_t OrthancInstancesCache::GetMemorySize() | |
181 { | |
182 boost::mutex::scoped_lock lock(mutex_); | |
183 return memorySize_; | |
184 } | |
185 | |
186 | |
187 size_t OrthancInstancesCache::GetMaxMemorySize() | |
188 { | |
189 boost::mutex::scoped_lock lock(mutex_); | |
190 return maxMemorySize_; | |
191 } | |
192 | |
193 | |
194 void OrthancInstancesCache::SetMaxMemorySize(size_t size) | |
195 { | |
196 if (size <= 0) | |
197 { | |
198 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); | |
199 } | |
200 | |
201 boost::mutex::scoped_lock lock(mutex_); | |
202 | |
203 while (memorySize_ > size) | |
204 { | |
205 RemoveOldest(); | |
206 } | |
207 | |
208 maxMemorySize_ = size; | |
209 CheckInvariants(); | |
210 } | |
211 | |
212 | |
213 void OrthancInstancesCache::GetInstanceInfo(size_t& size, | |
214 std::string& md5, | |
215 const std::string& instanceId) | |
216 { | |
217 // Check whether the instance is part of the cache | |
218 { | |
219 CacheAccessor accessor(*this, instanceId); | |
220 if (accessor.IsValid()) | |
221 { | |
222 size = accessor.GetInfo().GetSize(); | |
223 md5 = accessor.GetInfo().GetMD5(); | |
224 return; | |
225 } | |
226 } | |
227 | |
228 // The instance was not in the cache, load it | |
25
dfc43678aecb
replacing deprecated std::auto_ptr by std::unique_ptr
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
19
diff
changeset
|
229 std::unique_ptr<SourceDicomInstance> instance(new SourceDicomInstance(instanceId)); |
0 | 230 size = instance->GetInfo().GetSize(); |
231 md5 = instance->GetInfo().GetMD5(); | |
232 | |
233 // Store the just-loaded DICOM instance into the cache | |
234 { | |
235 boost::mutex::scoped_lock lock(mutex_); | |
236 Store(instanceId, instance); | |
237 } | |
238 } | |
239 | |
240 | |
241 void OrthancInstancesCache::GetChunk(std::string& chunk, | |
242 std::string& md5, | |
243 const std::string& instanceId, | |
244 size_t offset, | |
245 size_t size) | |
246 { | |
247 // Check whether the instance is part of the cache | |
248 { | |
249 CacheAccessor accessor(*this, instanceId); | |
250 if (accessor.IsValid()) | |
251 { | |
41 | 252 // keeping this log for future (see TODO) LOG(INFO) << "++++ Chunk for " << instanceId << " is in cache"; |
0 | 253 accessor.GetChunk(chunk, md5, offset, size); |
254 return; | |
255 } | |
256 } | |
257 | |
258 // The instance was not in the cache, load it | |
25
dfc43678aecb
replacing deprecated std::auto_ptr by std::unique_ptr
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
19
diff
changeset
|
259 std::unique_ptr<SourceDicomInstance> instance(new SourceDicomInstance(instanceId)); |
41 | 260 instance->GetChunk(chunk, md5, offset, size); |
0 | 261 |
262 // Store the just-loaded DICOM instance into the cache | |
263 { | |
41 | 264 // keeping this log for future (see TODO) LOG(ERROR) << "---- Chunk for " << instanceId << " not in cache -> adding it"; |
0 | 265 boost::mutex::scoped_lock lock(mutex_); |
266 Store(instanceId, instance); | |
267 } | |
268 } | |
269 | |
270 | |
271 void OrthancInstancesCache::GetChunk(std::string& chunk, | |
272 std::string& md5, | |
273 const TransferBucket& bucket, | |
274 size_t chunkIndex) | |
275 { | |
276 GetChunk(chunk, md5, bucket.GetChunkInstanceId(chunkIndex), | |
277 bucket.GetChunkOffset(chunkIndex), | |
278 bucket.GetChunkSize(chunkIndex)); | |
279 } | |
280 } |