comparison OrthancFramework/Sources/Cache/MemoryObjectCache.cpp @ 4044:d25f4c0fa160 framework

splitting code into OrthancFramework and OrthancServer
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 10 Jun 2020 20:30:34 +0200
parents Core/Cache/MemoryObjectCache.cpp@2a170a8f1faf
children bf7b9edf6b81
comparison
equal deleted inserted replaced
4043:6c6239aec462 4044:d25f4c0fa160
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2020 Osimis S.A., Belgium
6 *
7 * This program is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
11 *
12 * In addition, as a special exception, the copyright holders of this
13 * program give permission to link the code of its release with the
14 * OpenSSL project's "OpenSSL" library (or with modified versions of it
15 * that use the same license as the "OpenSSL" library), and distribute
16 * the linked executables. You must obey the GNU General Public License
17 * in all respects for all of the code used other than "OpenSSL". If you
18 * modify file(s) with this exception, you may extend this exception to
19 * your version of the file(s), but you are not obligated to do so. If
20 * you do not wish to do so, delete this exception statement from your
21 * version. If you delete this exception statement from all source files
22 * in the program, then also delete it here.
23 *
24 * This program is distributed in the hope that it will be useful, but
25 * WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 * General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program. If not, see <http://www.gnu.org/licenses/>.
31 **/
32
33
34 #include "../PrecompiledHeaders.h"
35 #include "MemoryObjectCache.h"
36
37 #include "../Compatibility.h"
38
39 namespace Orthanc
40 {
41 class MemoryObjectCache::Item : public boost::noncopyable
42 {
43 private:
44 ICacheable* value_;
45 boost::posix_time::ptime time_;
46
47 public:
48 explicit Item(ICacheable* value) : // Takes ownership
49 value_(value),
50 time_(boost::posix_time::second_clock::local_time())
51 {
52 if (value == NULL)
53 {
54 throw OrthancException(ErrorCode_NullPointer);
55 }
56 }
57
58 ~Item()
59 {
60 assert(value_ != NULL);
61 delete value_;
62 }
63
64 ICacheable& GetValue() const
65 {
66 assert(value_ != NULL);
67 return *value_;
68 }
69
70 const boost::posix_time::ptime& GetTime() const
71 {
72 return time_;
73 }
74 };
75
76
77 void MemoryObjectCache::Recycle(size_t targetSize)
78 {
79 // WARNING: "cacheMutex_" must be locked
80 while (currentSize_ > targetSize)
81 {
82 assert(!content_.IsEmpty());
83
84 Item* item = NULL;
85 content_.RemoveOldest(item);
86
87 assert(item != NULL);
88 const size_t size = item->GetValue().GetMemoryUsage();
89 delete item;
90
91 assert(currentSize_ >= size);
92 currentSize_ -= size;
93 }
94
95 // Post-condition: "currentSize_ <= targetSize"
96 }
97
98
99 MemoryObjectCache::MemoryObjectCache() :
100 currentSize_(0),
101 maxSize_(100 * 1024 * 1024) // 100 MB
102 {
103 }
104
105
106 MemoryObjectCache::~MemoryObjectCache()
107 {
108 Recycle(0);
109 assert(content_.IsEmpty());
110 }
111
112
113 size_t MemoryObjectCache::GetMaximumSize()
114 {
115 #if !defined(__EMSCRIPTEN__)
116 boost::mutex::scoped_lock lock(cacheMutex_);
117 #endif
118
119 return maxSize_;
120 }
121
122
123 void MemoryObjectCache::SetMaximumSize(size_t size)
124 {
125 if (size == 0)
126 {
127 throw OrthancException(ErrorCode_ParameterOutOfRange);
128 }
129
130 #if !defined(__EMSCRIPTEN__)
131 // Make sure no accessor is currently open (as its data may be
132 // removed if recycling is needed)
133 WriterLock contentLock(contentMutex_);
134
135 // Lock the global structure of the cache
136 boost::mutex::scoped_lock cacheLock(cacheMutex_);
137 #endif
138
139 Recycle(size);
140 maxSize_ = size;
141 }
142
143
144 void MemoryObjectCache::Acquire(const std::string& key,
145 ICacheable* value)
146 {
147 std::unique_ptr<Item> item(new Item(value));
148
149 if (value == NULL)
150 {
151 throw OrthancException(ErrorCode_NullPointer);
152 }
153 else
154 {
155 #if !defined(__EMSCRIPTEN__)
156 // Make sure no accessor is currently open (as its data may be
157 // removed if recycling is needed)
158 WriterLock contentLock(contentMutex_);
159
160 // Lock the global structure of the cache
161 boost::mutex::scoped_lock cacheLock(cacheMutex_);
162 #endif
163
164 const size_t size = item->GetValue().GetMemoryUsage();
165
166 if (size > maxSize_)
167 {
168 // This object is too large to be stored in the cache, discard it
169 }
170 else if (content_.Contains(key))
171 {
172 // Value already stored, don't overwrite the old value
173 content_.MakeMostRecent(key);
174 }
175 else
176 {
177 Recycle(maxSize_ - size); // Post-condition: currentSize_ <= maxSize_ - size
178 assert(currentSize_ + size <= maxSize_);
179
180 content_.Add(key, item.release());
181 currentSize_ += size;
182 }
183 }
184 }
185
186
187 void MemoryObjectCache::Invalidate(const std::string& key)
188 {
189 #if !defined(__EMSCRIPTEN__)
190 // Make sure no accessor is currently open (as it may correspond
191 // to the key to remove)
192 WriterLock contentLock(contentMutex_);
193
194 // Lock the global structure of the cache
195 boost::mutex::scoped_lock cacheLock(cacheMutex_);
196 #endif
197
198 Item* item = NULL;
199 if (content_.Contains(key, item))
200 {
201 assert(item != NULL);
202 const size_t size = item->GetValue().GetMemoryUsage();
203 delete item;
204
205 content_.Invalidate(key);
206
207 assert(currentSize_ >= size);
208 currentSize_ -= size;
209 }
210 }
211
212
213 MemoryObjectCache::Accessor::Accessor(MemoryObjectCache& cache,
214 const std::string& key,
215 bool unique) :
216 item_(NULL)
217 {
218 #if !defined(__EMSCRIPTEN__)
219 if (unique)
220 {
221 writerLock_ = WriterLock(cache.contentMutex_);
222 }
223 else
224 {
225 readerLock_ = ReaderLock(cache.contentMutex_);
226 }
227
228 // Lock the global structure of the cache, must be *after* the
229 // reader/writer lock
230 cacheLock_ = boost::mutex::scoped_lock(cache.cacheMutex_);
231 #endif
232
233 if (cache.content_.Contains(key, item_))
234 {
235 cache.content_.MakeMostRecent(key);
236 }
237
238 #if !defined(__EMSCRIPTEN__)
239 cacheLock_.unlock();
240
241 if (item_ == NULL)
242 {
243 // This item does not exist in the cache, we can release the
244 // reader/writer lock
245 if (unique)
246 {
247 writerLock_.unlock();
248 }
249 else
250 {
251 readerLock_.unlock();
252 }
253 }
254 #endif
255 }
256
257
258 ICacheable& MemoryObjectCache::Accessor::GetValue() const
259 {
260 if (IsValid())
261 {
262 return item_->GetValue();
263 }
264 else
265 {
266 throw OrthancException(ErrorCode_BadSequenceOfCalls);
267 }
268 }
269
270
271 const boost::posix_time::ptime& MemoryObjectCache::Accessor::GetTime() const
272 {
273 if (IsValid())
274 {
275 return item_->GetTime();
276 }
277 else
278 {
279 throw OrthancException(ErrorCode_BadSequenceOfCalls);
280 }
281 }
282 }