comparison Core/Cache/MemoryObjectCache.cpp @ 3557:4d809b2e1141

better cache toolbox: MemoryObjectCache
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 04 Nov 2019 15:18:38 +0100
parents
children 4812825e69fc
comparison
equal deleted inserted replaced
3556:87940f7156e0 3557:4d809b2e1141
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-2019 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 namespace Orthanc
38 {
39 class MemoryObjectCache::Item : public boost::noncopyable
40 {
41 private:
42 ICacheable* value_;
43 boost::posix_time::ptime time_;
44
45 public:
46 explicit Item(ICacheable* value) : // Takes ownership
47 value_(value),
48 time_(boost::posix_time::second_clock::local_time())
49 {
50 if (value == NULL)
51 {
52 throw OrthancException(ErrorCode_NullPointer);
53 }
54 }
55
56 ~Item()
57 {
58 assert(value_ != NULL);
59 delete value_;
60 }
61
62 ICacheable& GetValue() const
63 {
64 assert(value_ != NULL);
65 return *value_;
66 }
67
68 const boost::posix_time::ptime& GetTime() const
69 {
70 return time_;
71 }
72 };
73
74
75 void MemoryObjectCache::Recycle(size_t targetSize)
76 {
77 // WARNING: "cacheMutex_" must be locked
78 while (currentSize_ > targetSize)
79 {
80 assert(!content_.IsEmpty());
81
82 Item* item = NULL;
83 content_.RemoveOldest(item);
84
85 assert(item != NULL);
86 const size_t size = item->GetValue().GetMemoryUsage();
87 delete item;
88
89 assert(currentSize_ >= size);
90 currentSize_ -= size;
91 }
92
93 // Post-condition: "currentSize_ <= targetSize"
94 }
95
96
97 MemoryObjectCache::MemoryObjectCache() :
98 currentSize_(0),
99 maxSize_(100 * 1024 * 1024) // 100 MB
100 {
101 }
102
103
104 MemoryObjectCache::~MemoryObjectCache()
105 {
106 Recycle(0);
107 assert(content_.IsEmpty());
108 }
109
110
111 size_t MemoryObjectCache::GetMaximumSize()
112 {
113 #if !defined(__EMSCRIPTEN__)
114 boost::mutex::scoped_lock lock(cacheMutex_);
115 #endif
116
117 return maxSize_;
118 }
119
120
121 void MemoryObjectCache::SetMaximumSize(size_t size)
122 {
123 if (size == 0)
124 {
125 throw OrthancException(ErrorCode_ParameterOutOfRange);
126 }
127
128 #if !defined(__EMSCRIPTEN__)
129 // Make sure no accessor is currently open (as its data may be
130 // removed if recycling is needed)
131 WriterLock contentLock(contentMutex_);
132
133 // Lock the global structure of the cache
134 boost::mutex::scoped_lock cacheLock(cacheMutex_);
135 #endif
136
137 Recycle(size);
138 maxSize_ = size;
139 }
140
141
142 void MemoryObjectCache::Acquire(const std::string& key,
143 ICacheable* value)
144 {
145 std::auto_ptr<Item> item(new Item(value));
146
147 if (value == NULL)
148 {
149 throw OrthancException(ErrorCode_NullPointer);
150 }
151 else
152 {
153 #if !defined(__EMSCRIPTEN__)
154 // Make sure no accessor is currently open (as its data may be
155 // removed if recycling is needed)
156 WriterLock contentLock(contentMutex_);
157
158 // Lock the global structure of the cache
159 boost::mutex::scoped_lock cacheLock(cacheMutex_);
160 #endif
161
162 const size_t size = item->GetValue().GetMemoryUsage();
163
164 if (size > maxSize_)
165 {
166 // This object is too large to be stored in the cache, discard it
167 }
168 else if (content_.Contains(key))
169 {
170 // Value already stored, don't overwrite the old value
171 content_.MakeMostRecent(key);
172 }
173 else
174 {
175 Recycle(maxSize_ - size); // Post-condition: currentSize_ <= maxSize_ - size
176 assert(currentSize_ + size <= maxSize_);
177
178 content_.Add(key, item.release());
179 currentSize_ += size;
180 }
181 }
182 }
183
184
185 void MemoryObjectCache::Invalidate(const std::string& key)
186 {
187 #if !defined(__EMSCRIPTEN__)
188 // Make sure no accessor is currently open (as it may correspond
189 // to the key to remove)
190 WriterLock contentLock(contentMutex_);
191
192 // Lock the global structure of the cache
193 boost::mutex::scoped_lock cacheLock(cacheMutex_);
194 #endif
195
196 Item* item = NULL;
197 if (content_.Contains(key, item))
198 {
199 assert(item != NULL);
200 const size_t size = item->GetValue().GetMemoryUsage();
201 delete item;
202
203 content_.Invalidate(key);
204
205 assert(currentSize_ >= size);
206 currentSize_ -= size;
207 }
208 }
209
210
211 MemoryObjectCache::Reader::Reader(MemoryObjectCache& cache,
212 const std::string& key) :
213 #if !defined(__EMSCRIPTEN__)
214 contentLock_(cache.contentMutex_),
215 cacheLock_(cache.cacheMutex_),
216 #endif
217 item_(NULL)
218 {
219 if (cache.content_.Contains(key, item_))
220 {
221 cache.content_.MakeMostRecent(key);
222 }
223
224 #if !defined(__EMSCRIPTEN__)
225 cacheLock_.unlock();
226
227 if (item_ == NULL)
228 {
229 contentLock_.unlock();
230 }
231 #endif
232 }
233
234
235 ICacheable& MemoryObjectCache::Reader::GetValue() const
236 {
237 if (IsValid())
238 {
239 return item_->GetValue();
240 }
241 else
242 {
243 throw OrthancException(ErrorCode_BadSequenceOfCalls);
244 }
245 }
246
247
248 const boost::posix_time::ptime& MemoryObjectCache::Reader::GetTime() const
249 {
250 if (IsValid())
251 {
252 return item_->GetTime();
253 }
254 else
255 {
256 throw OrthancException(ErrorCode_BadSequenceOfCalls);
257 }
258 }
259 }