comparison data/fft/FFTDataServer.h @ 537:3cc4b7cd2aa5

* Merge from one-fftdataserver-per-fftmodel branch. This bit of reworking (which is not described very accurately by the title of the branch) turns the MatrixFile object into something that either reads or writes, but not both, and separates the FFT file cache reader and writer implementations separately. This allows the FFT data server to have a single thread owning writers and one reader per "customer" thread, and for all locking to be vastly simplified and concentrated in the data server alone (because none of the classes it makes use of is used in more than one thread at a time). The result is faster and more trustworthy code.
author Chris Cannam
date Tue, 27 Jan 2009 13:25:10 +0000
parents 115f60df1e4d
children a5a17152b6df
comparison
equal deleted inserted replaced
536:beb51f558e9c 537:3cc4b7cd2aa5
19 #include "base/Window.h" 19 #include "base/Window.h"
20 #include "base/Thread.h" 20 #include "base/Thread.h"
21 #include "base/StorageAdviser.h" 21 #include "base/StorageAdviser.h"
22 22
23 #include "FFTapi.h" 23 #include "FFTapi.h"
24 #include "FFTFileCacheReader.h"
25 #include "FFTFileCacheWriter.h"
26 #include "FFTMemoryCache.h"
24 27
25 #include <QMutex> 28 #include <QMutex>
29 #include <QReadWriteLock>
30 #include <QReadLocker>
26 #include <QWaitCondition> 31 #include <QWaitCondition>
27 #include <QString> 32 #include <QString>
28 33
29 #include <vector> 34 #include <vector>
30 #include <deque> 35 #include <deque>
31 36
32 class DenseTimeValueModel; 37 class DenseTimeValueModel;
33 class Model; 38 class Model;
34 class FFTCache;
35 39
36 class FFTDataServer 40 class FFTDataServer
37 { 41 {
38 public: 42 public:
39 static FFTDataServer *getInstance(const DenseTimeValueModel *model, 43 static FFTDataServer *getInstance(const DenseTimeValueModel *model,
138 size_t m_height; 142 size_t m_height;
139 size_t m_cacheWidth; 143 size_t m_cacheWidth;
140 size_t m_cacheWidthPower; 144 size_t m_cacheWidthPower;
141 size_t m_cacheWidthMask; 145 size_t m_cacheWidthMask;
142 146
143 int m_lastUsedCache; 147 struct CacheBlock {
144 FFTCache *getCache(size_t x, size_t &col) { 148 FFTMemoryCache *memoryCache;
145 col = x & m_cacheWidthMask; 149 typedef std::map<QThread *, FFTFileCacheReader *> ThreadReaderMap;
150 ThreadReaderMap fileCacheReader;
151 FFTFileCacheWriter *fileCacheWriter;
152 CacheBlock() : memoryCache(0), fileCacheWriter(0) { }
153 ~CacheBlock() {
154 delete memoryCache;
155 while (!fileCacheReader.empty()) {
156 delete fileCacheReader.begin()->second;
157 fileCacheReader.erase(fileCacheReader.begin());
158 }
159 delete fileCacheWriter;
160 }
161 };
162
163 typedef std::vector<CacheBlock *> CacheVector;
164 CacheVector m_caches;
165 QReadWriteLock m_cacheVectorLock; // locks cache lookup, not use
166
167 FFTCacheReader *getCacheReader(size_t x, size_t &col) {
168 Profiler profiler("FFTDataServer::getCacheReader");
169 col = x & m_cacheWidthMask;
146 int c = x >> m_cacheWidthPower; 170 int c = x >> m_cacheWidthPower;
147 // The only use of m_lastUsedCache without a lock is to 171 m_cacheVectorLock.lockForRead();
148 // establish whether a cache has been created at all (they're 172 CacheBlock *cb(m_caches.at(c));
149 // created on demand, but not destroyed until the server is). 173 if (cb) {
150 if (c == m_lastUsedCache) return m_caches[c]; 174 if (cb->memoryCache) return cb->memoryCache;
151 else return getCacheAux(c); 175 if (cb->fileCacheWriter) {
152 } 176 QThread *me = QThread::currentThread();
177 CacheBlock::ThreadReaderMap &map = cb->fileCacheReader;
178 if (map.find(me) == map.end()) {
179 m_cacheVectorLock.unlock();
180 if (!makeCacheReader(c)) return 0;
181 return getCacheReader(x, col);
182 }
183 FFTCacheReader *reader = cb->fileCacheReader.at(me);
184 m_cacheVectorLock.unlock();
185 return reader;
186 }
187 // if cb exists but cb->fileCacheWriter doesn't, creation
188 // must have failed: don't try again
189 m_cacheVectorLock.unlock();
190 return 0;
191 }
192 m_cacheVectorLock.unlock();
193 if (!makeCache(c)) return 0;
194 return getCacheReader(x, col);
195 }
196
197 FFTCacheWriter *getCacheWriter(size_t x, size_t &col) {
198 Profiler profiler("FFTDataServer::getCacheWriter");
199 col = x & m_cacheWidthMask;
200 int c = x >> m_cacheWidthPower;
201 {
202 QReadLocker locker(&m_cacheVectorLock);
203 CacheBlock *cb(m_caches.at(c));
204 if (cb) {
205 if (cb->memoryCache) return cb->memoryCache;
206 if (cb->fileCacheWriter) return cb->fileCacheWriter;
207 // if cb exists, creation must have failed: don't try again
208 return 0;
209 }
210 }
211 if (!makeCache(c)) return 0;
212 return getCacheWriter(x, col);
213 }
214
153 bool haveCache(size_t x) { 215 bool haveCache(size_t x) {
154 int c = x >> m_cacheWidthPower; 216 int c = x >> m_cacheWidthPower;
155 if (c == m_lastUsedCache) return true; 217 return (m_caches[c] != 0);
156 else return (m_caches[c] != 0); 218 }
157 } 219
158 220 bool makeCache(int c);
159 typedef std::vector<FFTCache *> CacheVector; 221 bool makeCacheReader(int c);
160 CacheVector m_caches; 222
161
162 typedef std::deque<int> IntQueue;
163 IntQueue m_dormantCaches;
164
165 StorageAdviser::Criteria m_criteria; 223 StorageAdviser::Criteria m_criteria;
166 224
167 void getStorageAdvice(size_t w, size_t h, bool &memory, bool &compact); 225 void getStorageAdvice(size_t w, size_t h, bool &memory, bool &compact);
168 226
169 FFTCache *getCacheAux(size_t c);
170 QMutex m_writeMutex; 227 QMutex m_writeMutex;
171 QWaitCondition m_condition; 228 QWaitCondition m_condition;
172 229
173 fftsample *m_fftInput; 230 fftsample *m_fftInput;
174 fftf_complex *m_fftOutput; 231 fftf_complex *m_fftOutput;
197 bool m_suspended; 254 bool m_suspended;
198 FillThread *m_fillThread; 255 FillThread *m_fillThread;
199 256
200 void deleteProcessingData(); 257 void deleteProcessingData();
201 void fillColumn(size_t x, bool lockHeld); 258 void fillColumn(size_t x, bool lockHeld);
259 void fillComplete();
202 260
203 QString generateFileBasename() const; 261 QString generateFileBasename() const;
204 static QString generateFileBasename(const DenseTimeValueModel *model, 262 static QString generateFileBasename(const DenseTimeValueModel *model,
205 int channel, 263 int channel,
206 WindowType windowType, 264 WindowType windowType,