comparison ViewerPlugin/DicomPyramidCache.cpp @ 73:a8c90aa32ca6

LRU caching of pyramids, OrthancWSIClearCache script
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 28 Nov 2016 16:51:19 +0100
parents
children ff0ef01c332c
comparison
equal deleted inserted replaced
72:ea6309f70f1f 73:a8c90aa32ca6
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 *
6 * This program is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU Affero General Public License
8 * as published by the Free Software Foundation, either version 3 of
9 * the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 **/
19
20
21 #include "../Framework/PrecompiledHeadersWSI.h"
22 #include "DicomPyramidCache.h"
23
24 #include <cassert>
25
26 namespace OrthancWSI
27 {
28 DicomPyramid* DicomPyramidCache::GetCachedPyramid(const std::string& seriesId)
29 {
30 // Mutex is assumed to be locked
31 DicomPyramid* pyramid = NULL;
32
33 // Is the series of interest already cached as a pyramid?
34 if (cache_.Contains(seriesId, pyramid))
35 {
36 if (pyramid == NULL)
37 {
38 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
39 }
40
41 // Tag the series as the most recently used
42 cache_.MakeMostRecent(seriesId);
43 }
44
45 return pyramid;
46 }
47
48
49 DicomPyramid& DicomPyramidCache::GetPyramid(const std::string& seriesId,
50 boost::mutex::scoped_lock& lock)
51 {
52 // Mutex is assumed to be locked
53
54 {
55 DicomPyramid* pyramid = GetCachedPyramid(seriesId);
56 if (pyramid != NULL)
57 {
58 return *pyramid;
59 }
60 }
61
62 // Unlock the mutex to construct the pyramid (this is a
63 // time-consuming operation, we don't want it to block other clients)
64 lock.unlock();
65
66 std::auto_ptr<DicomPyramid> pyramid
67 (new DicomPyramid(orthanc_, seriesId, true /* use metadata cache */));
68
69 {
70 // The pyramid is constructed: Store it into the cache
71 lock.lock();
72
73 DicomPyramid* cached = GetCachedPyramid(seriesId);
74 if (cached != NULL)
75 {
76 // The pyramid was already constructed by another request in
77 // between, reuse the cached value (the auto_ptr destroys
78 // the just-constructed pyramid)
79 return *cached;
80 }
81
82 if (cache_.GetSize() == maxSize_)
83 {
84 // The cache has grown too large: First delete the least
85 // recently used entry
86 DicomPyramid* oldest = NULL;
87 cache_.RemoveOldest(oldest);
88
89 if (oldest == NULL)
90 {
91 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
92 }
93 else
94 {
95 delete oldest;
96 }
97 }
98
99 // Now we have at least one free entry in the cache
100 assert(cache_.GetSize() < maxSize_);
101
102 // Add a new element to the cache and make it the most
103 // recently used entry
104 DicomPyramid* payload = pyramid.release();
105 cache_.Add(seriesId, payload);
106 return *payload;
107 }
108 }
109
110
111 DicomPyramidCache::DicomPyramidCache(OrthancPlugins::IOrthancConnection& orthanc,
112 size_t maxSize) :
113 orthanc_(orthanc),
114 maxSize_(maxSize)
115 {
116 }
117
118
119 DicomPyramidCache::~DicomPyramidCache()
120 {
121 while (!cache_.IsEmpty())
122 {
123 DicomPyramid* pyramid = NULL;
124 std::string seriesId = cache_.RemoveOldest(pyramid);
125
126 if (pyramid != NULL)
127 {
128 delete pyramid;
129 }
130 }
131 }
132
133
134 void DicomPyramidCache::Invalidate(const std::string& seriesId)
135 {
136 boost::mutex::scoped_lock lock(mutex_);
137
138 if (cache_.Contains(seriesId))
139 {
140 std::auto_ptr<DicomPyramid> pyramid(cache_.Invalidate(seriesId));
141
142 if (pyramid.get() == NULL)
143 {
144 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
145 }
146 }
147 }
148
149
150 DicomPyramidCache::Locker::Locker(DicomPyramidCache& cache,
151 const std::string& seriesId) :
152 lock_(cache.mutex_),
153 pyramid_(cache.GetPyramid(seriesId, lock_))
154 {
155 }
156 }