annotate data/fft/FFTDataServer.h @ 547:806e3c72b5df

* fix deadlock in fft memory cache usage
author Chris Cannam
date Wed, 04 Feb 2009 20:39:11 +0000
parents a5a17152b6df
children 1469caaa8e67
rev   line source
Chris@148 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@148 2
Chris@148 3 /*
Chris@148 4 Sonic Visualiser
Chris@148 5 An audio file viewer and annotation editor.
Chris@148 6 Centre for Digital Music, Queen Mary, University of London.
Chris@202 7 This file copyright 2006 Chris Cannam and QMUL.
Chris@148 8
Chris@148 9 This program is free software; you can redistribute it and/or
Chris@148 10 modify it under the terms of the GNU General Public License as
Chris@148 11 published by the Free Software Foundation; either version 2 of the
Chris@148 12 License, or (at your option) any later version. See the file
Chris@148 13 COPYING included with this distribution for more information.
Chris@148 14 */
Chris@148 15
Chris@148 16 #ifndef _FFT_DATA_SERVER_H_
Chris@148 17 #define _FFT_DATA_SERVER_H_
Chris@148 18
Chris@148 19 #include "base/Window.h"
Chris@148 20 #include "base/Thread.h"
Chris@334 21 #include "base/StorageAdviser.h"
Chris@148 22
Chris@226 23 #include "FFTapi.h"
Chris@537 24 #include "FFTFileCacheReader.h"
Chris@537 25 #include "FFTFileCacheWriter.h"
Chris@537 26 #include "FFTMemoryCache.h"
Chris@148 27
Chris@148 28 #include <QMutex>
Chris@537 29 #include <QReadWriteLock>
Chris@537 30 #include <QReadLocker>
Chris@148 31 #include <QWaitCondition>
Chris@148 32 #include <QString>
Chris@148 33
Chris@148 34 #include <vector>
Chris@148 35 #include <deque>
Chris@148 36
Chris@148 37 class DenseTimeValueModel;
Chris@215 38 class Model;
Chris@148 39
Chris@148 40 class FFTDataServer
Chris@148 41 {
Chris@148 42 public:
Chris@148 43 static FFTDataServer *getInstance(const DenseTimeValueModel *model,
Chris@148 44 int channel,
Chris@148 45 WindowType windowType,
Chris@148 46 size_t windowSize,
Chris@148 47 size_t windowIncrement,
Chris@148 48 size_t fftSize,
Chris@148 49 bool polar,
Chris@334 50 StorageAdviser::Criteria criteria =
Chris@334 51 StorageAdviser::NoCriteria,
Chris@148 52 size_t fillFromColumn = 0);
Chris@148 53
Chris@148 54 static FFTDataServer *getFuzzyInstance(const DenseTimeValueModel *model,
Chris@148 55 int channel,
Chris@148 56 WindowType windowType,
Chris@148 57 size_t windowSize,
Chris@148 58 size_t windowIncrement,
Chris@148 59 size_t fftSize,
Chris@148 60 bool polar,
Chris@334 61 StorageAdviser::Criteria criteria =
Chris@334 62 StorageAdviser::NoCriteria,
Chris@148 63 size_t fillFromColumn = 0);
Chris@148 64
Chris@152 65 static void claimInstance(FFTDataServer *);
Chris@148 66 static void releaseInstance(FFTDataServer *);
Chris@148 67
Chris@215 68 static void modelAboutToBeDeleted(Model *);
Chris@215 69
Chris@148 70 const DenseTimeValueModel *getModel() const { return m_model; }
Chris@148 71 int getChannel() const { return m_channel; }
Chris@148 72 WindowType getWindowType() const { return m_windower.getType(); }
Chris@148 73 size_t getWindowSize() const { return m_windowSize; }
Chris@148 74 size_t getWindowIncrement() const { return m_windowIncrement; }
Chris@148 75 size_t getFFTSize() const { return m_fftSize; }
Chris@148 76 bool getPolar() const { return m_polar; }
Chris@148 77
Chris@148 78 size_t getWidth() const { return m_width; }
Chris@148 79 size_t getHeight() const { return m_height; }
Chris@148 80
Chris@148 81 float getMagnitudeAt(size_t x, size_t y);
Chris@148 82 float getNormalizedMagnitudeAt(size_t x, size_t y);
Chris@148 83 float getMaximumMagnitudeAt(size_t x);
Chris@148 84 float getPhaseAt(size_t x, size_t y);
Chris@148 85 void getValuesAt(size_t x, size_t y, float &real, float &imaginary);
Chris@148 86 bool isColumnReady(size_t x);
Chris@148 87
Chris@408 88 bool getMagnitudesAt(size_t x, float *values, size_t minbin = 0, size_t count = 0, size_t step = 1);
Chris@408 89 bool getNormalizedMagnitudesAt(size_t x, float *values, size_t minbin = 0, size_t count = 0, size_t step = 1);
Chris@408 90 bool getPhasesAt(size_t x, float *values, size_t minbin = 0, size_t count = 0, size_t step = 1);
Chris@408 91
Chris@148 92 void suspend();
Chris@155 93 void suspendWrites();
Chris@154 94 void resume(); // also happens automatically if new data needed
Chris@148 95
Chris@148 96 // Convenience functions:
Chris@148 97
Chris@148 98 bool isLocalPeak(size_t x, size_t y) {
Chris@148 99 float mag = getMagnitudeAt(x, y);
Chris@148 100 if (y > 0 && mag < getMagnitudeAt(x, y - 1)) return false;
Chris@148 101 if (y < getHeight()-1 && mag < getMagnitudeAt(x, y + 1)) return false;
Chris@148 102 return true;
Chris@148 103 }
Chris@148 104 bool isOverThreshold(size_t x, size_t y, float threshold) {
Chris@148 105 return getMagnitudeAt(x, y) > threshold;
Chris@148 106 }
Chris@148 107
Chris@148 108 size_t getFillCompletion() const;
Chris@148 109 size_t getFillExtent() const;
Chris@148 110
Chris@148 111 private:
Chris@148 112 FFTDataServer(QString fileBaseName,
Chris@148 113 const DenseTimeValueModel *model,
Chris@148 114 int channel,
Chris@148 115 WindowType windowType,
Chris@148 116 size_t windowSize,
Chris@148 117 size_t windowIncrement,
Chris@148 118 size_t fftSize,
Chris@148 119 bool polar,
Chris@334 120 StorageAdviser::Criteria criteria,
Chris@148 121 size_t fillFromColumn = 0);
Chris@148 122
Chris@148 123 virtual ~FFTDataServer();
Chris@148 124
Chris@148 125 FFTDataServer(const FFTDataServer &); // not implemented
Chris@148 126 FFTDataServer &operator=(const FFTDataServer &); // not implemented
Chris@148 127
Chris@148 128 typedef float fftsample;
Chris@148 129
Chris@148 130 QString m_fileBaseName;
Chris@148 131 const DenseTimeValueModel *m_model;
Chris@148 132 int m_channel;
Chris@148 133
Chris@148 134 Window<fftsample> m_windower;
Chris@148 135
Chris@148 136 size_t m_windowSize;
Chris@148 137 size_t m_windowIncrement;
Chris@148 138 size_t m_fftSize;
Chris@148 139 bool m_polar;
Chris@148 140
Chris@148 141 size_t m_width;
Chris@148 142 size_t m_height;
Chris@148 143 size_t m_cacheWidth;
Chris@183 144 size_t m_cacheWidthPower;
Chris@183 145 size_t m_cacheWidthMask;
Chris@359 146
Chris@537 147 struct CacheBlock {
Chris@537 148 FFTMemoryCache *memoryCache;
Chris@537 149 typedef std::map<QThread *, FFTFileCacheReader *> ThreadReaderMap;
Chris@537 150 ThreadReaderMap fileCacheReader;
Chris@537 151 FFTFileCacheWriter *fileCacheWriter;
Chris@537 152 CacheBlock() : memoryCache(0), fileCacheWriter(0) { }
Chris@537 153 ~CacheBlock() {
Chris@537 154 delete memoryCache;
Chris@537 155 while (!fileCacheReader.empty()) {
Chris@537 156 delete fileCacheReader.begin()->second;
Chris@537 157 fileCacheReader.erase(fileCacheReader.begin());
Chris@537 158 }
Chris@537 159 delete fileCacheWriter;
Chris@537 160 }
Chris@537 161 };
Chris@537 162
Chris@537 163 typedef std::vector<CacheBlock *> CacheVector;
Chris@537 164 CacheVector m_caches;
Chris@537 165 QReadWriteLock m_cacheVectorLock; // locks cache lookup, not use
Chris@537 166
Chris@537 167 FFTCacheReader *getCacheReader(size_t x, size_t &col) {
Chris@537 168 Profiler profiler("FFTDataServer::getCacheReader");
Chris@537 169 col = x & m_cacheWidthMask;
Chris@359 170 int c = x >> m_cacheWidthPower;
Chris@537 171 m_cacheVectorLock.lockForRead();
Chris@537 172 CacheBlock *cb(m_caches.at(c));
Chris@537 173 if (cb) {
Chris@547 174 if (cb->memoryCache) {
Chris@547 175 m_cacheVectorLock.unlock();
Chris@547 176 return cb->memoryCache;
Chris@547 177 }
Chris@537 178 if (cb->fileCacheWriter) {
Chris@537 179 QThread *me = QThread::currentThread();
Chris@537 180 CacheBlock::ThreadReaderMap &map = cb->fileCacheReader;
Chris@537 181 if (map.find(me) == map.end()) {
Chris@537 182 m_cacheVectorLock.unlock();
Chris@537 183 if (!makeCacheReader(c)) return 0;
Chris@537 184 return getCacheReader(x, col);
Chris@537 185 }
Chris@539 186 FFTCacheReader *reader = cb->fileCacheReader[me];
Chris@537 187 m_cacheVectorLock.unlock();
Chris@537 188 return reader;
Chris@537 189 }
Chris@537 190 // if cb exists but cb->fileCacheWriter doesn't, creation
Chris@537 191 // must have failed: don't try again
Chris@537 192 m_cacheVectorLock.unlock();
Chris@537 193 return 0;
Chris@537 194 }
Chris@537 195 m_cacheVectorLock.unlock();
Chris@537 196 if (!makeCache(c)) return 0;
Chris@537 197 return getCacheReader(x, col);
Chris@359 198 }
Chris@537 199
Chris@537 200 FFTCacheWriter *getCacheWriter(size_t x, size_t &col) {
Chris@537 201 Profiler profiler("FFTDataServer::getCacheWriter");
Chris@537 202 col = x & m_cacheWidthMask;
Chris@537 203 int c = x >> m_cacheWidthPower;
Chris@537 204 {
Chris@537 205 QReadLocker locker(&m_cacheVectorLock);
Chris@537 206 CacheBlock *cb(m_caches.at(c));
Chris@537 207 if (cb) {
Chris@537 208 if (cb->memoryCache) return cb->memoryCache;
Chris@537 209 if (cb->fileCacheWriter) return cb->fileCacheWriter;
Chris@537 210 // if cb exists, creation must have failed: don't try again
Chris@537 211 return 0;
Chris@537 212 }
Chris@537 213 }
Chris@537 214 if (!makeCache(c)) return 0;
Chris@537 215 return getCacheWriter(x, col);
Chris@537 216 }
Chris@537 217
Chris@359 218 bool haveCache(size_t x) {
Chris@359 219 int c = x >> m_cacheWidthPower;
Chris@537 220 return (m_caches[c] != 0);
Chris@359 221 }
Chris@148 222
Chris@537 223 bool makeCache(int c);
Chris@537 224 bool makeCacheReader(int c);
Chris@537 225
Chris@359 226 StorageAdviser::Criteria m_criteria;
Chris@359 227
Chris@359 228 void getStorageAdvice(size_t w, size_t h, bool &memory, bool &compact);
Chris@148 229
Chris@148 230 QMutex m_writeMutex;
Chris@148 231 QWaitCondition m_condition;
Chris@148 232
Chris@148 233 fftsample *m_fftInput;
Chris@226 234 fftf_complex *m_fftOutput;
Chris@148 235 float *m_workbuffer;
Chris@226 236 fftf_plan m_fftPlan;
Chris@148 237
Chris@148 238 class FillThread : public Thread
Chris@148 239 {
Chris@148 240 public:
Chris@148 241 FillThread(FFTDataServer &server, size_t fillFromColumn) :
Chris@148 242 m_server(server), m_extent(0), m_completion(0),
Chris@148 243 m_fillFrom(fillFromColumn) { }
Chris@148 244
Chris@148 245 size_t getExtent() const { return m_extent; }
Chris@148 246 size_t getCompletion() const { return m_completion ? m_completion : 1; }
Chris@148 247 virtual void run();
Chris@148 248
Chris@148 249 protected:
Chris@148 250 FFTDataServer &m_server;
Chris@148 251 size_t m_extent;
Chris@148 252 size_t m_completion;
Chris@148 253 size_t m_fillFrom;
Chris@148 254 };
Chris@148 255
Chris@148 256 bool m_exiting;
Chris@148 257 bool m_suspended;
Chris@148 258 FillThread *m_fillThread;
Chris@148 259
Chris@148 260 void deleteProcessingData();
Chris@408 261 void fillColumn(size_t x, bool lockHeld);
Chris@537 262 void fillComplete();
Chris@148 263
Chris@148 264 QString generateFileBasename() const;
Chris@148 265 static QString generateFileBasename(const DenseTimeValueModel *model,
Chris@148 266 int channel,
Chris@148 267 WindowType windowType,
Chris@148 268 size_t windowSize,
Chris@148 269 size_t windowIncrement,
Chris@148 270 size_t fftSize,
Chris@148 271 bool polar);
Chris@148 272
Chris@148 273 typedef std::pair<FFTDataServer *, int> ServerCountPair;
Chris@148 274 typedef std::map<QString, ServerCountPair> ServerMap;
Chris@215 275 typedef std::deque<FFTDataServer *> ServerQueue;
Chris@148 276
Chris@148 277 static ServerMap m_servers;
Chris@215 278 static ServerQueue m_releasedServers; // these are still in m_servers as well, with zero refcount
Chris@148 279 static QMutex m_serverMapMutex;
Chris@148 280 static FFTDataServer *findServer(QString); // call with serverMapMutex held
Chris@148 281 static void purgeLimbo(int maxSize = 3); // call with serverMapMutex held
Chris@216 282
Chris@216 283 static void claimInstance(FFTDataServer *, bool needLock);
Chris@216 284 static void releaseInstance(FFTDataServer *, bool needLock);
Chris@216 285
Chris@148 286 };
Chris@148 287
Chris@148 288 #endif