annotate data/fileio/MatrixFile.cpp @ 547:806e3c72b5df

* fix deadlock in fft memory cache usage
author Chris Cannam
date Wed, 04 Feb 2009 20:39:11 +0000
parents 95391b480e17
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@537 7 This file copyright 2006-2009 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 #include "MatrixFile.h"
Chris@148 17 #include "base/TempDirectory.h"
Chris@150 18 #include "system/System.h"
Chris@148 19 #include "base/Profiler.h"
Chris@148 20 #include "base/Exceptions.h"
Chris@408 21 #include "base/Thread.h"
Chris@148 22
Chris@148 23 #include <sys/types.h>
Chris@148 24 #include <sys/stat.h>
Chris@148 25 #include <fcntl.h>
Chris@148 26 #include <unistd.h>
Chris@148 27
Chris@148 28 #include <iostream>
Chris@148 29
Chris@148 30 #include <cstdio>
Chris@148 31 #include <cassert>
Chris@148 32
Chris@405 33 #include <cstdlib>
Chris@405 34
Chris@148 35 #include <QFileInfo>
Chris@148 36 #include <QDir>
Chris@148 37
Chris@537 38 #define DEBUG_MATRIX_FILE 1
Chris@403 39 //#define DEBUG_MATRIX_FILE_READ_SET 1
Chris@148 40
Chris@148 41 #ifdef DEBUG_MATRIX_FILE_READ_SET
Chris@153 42 #ifndef DEBUG_MATRIX_FILE
Chris@148 43 #define DEBUG_MATRIX_FILE 1
Chris@148 44 #endif
Chris@153 45 #endif
Chris@148 46
Chris@148 47 std::map<QString, int> MatrixFile::m_refcount;
Chris@537 48 QMutex MatrixFile::m_createMutex;
Chris@148 49
Chris@148 50 static size_t totalStorage = 0;
Chris@148 51 static size_t totalCount = 0;
Chris@537 52 static size_t openCount = 0;
Chris@148 53
Chris@148 54 MatrixFile::MatrixFile(QString fileBase, Mode mode,
Chris@537 55 size_t cellSize, size_t width, size_t height) :
Chris@148 56 m_fd(-1),
Chris@148 57 m_mode(mode),
Chris@148 58 m_flags(0),
Chris@148 59 m_fmode(0),
Chris@148 60 m_cellSize(cellSize),
Chris@537 61 m_width(width),
Chris@537 62 m_height(height),
Chris@537 63 m_headerSize(2 * sizeof(size_t))
Chris@148 64 {
Chris@148 65 Profiler profiler("MatrixFile::MatrixFile", true);
Chris@148 66
Chris@537 67 #ifdef DEBUG_MATRIX_FILE
Chris@537 68 std::cerr << "MatrixFile::MatrixFile(" << fileBase.toStdString() << ", " << int(mode) << ", " << cellSize << ", " << width << ", " << height << ")" << std::endl;
Chris@537 69 #endif
Chris@148 70
Chris@537 71 QMutexLocker locker(&m_createMutex);
Chris@148 72
Chris@148 73 QDir tempDir(TempDirectory::getInstance()->getPath());
Chris@148 74 QString fileName(tempDir.filePath(QString("%1.mfc").arg(fileBase)));
Chris@148 75 bool newFile = !QFileInfo(fileName).exists();
Chris@148 76
Chris@148 77 if (newFile && m_mode == ReadOnly) {
Chris@148 78 std::cerr << "ERROR: MatrixFile::MatrixFile: Read-only mode "
Chris@148 79 << "specified, but cache file does not exist" << std::endl;
Chris@148 80 throw FileNotFound(fileName);
Chris@148 81 }
Chris@148 82
Chris@537 83 if (!newFile && m_mode == WriteOnly) {
Chris@537 84 std::cerr << "ERROR: MatrixFile::MatrixFile: Write-only mode "
Chris@537 85 << "specified, but file already exists" << std::endl;
Chris@537 86 throw FileOperationFailed(fileName, "create");
Chris@148 87 }
Chris@148 88
Chris@148 89 m_flags = 0;
Chris@148 90 m_fmode = S_IRUSR | S_IWUSR;
Chris@148 91
Chris@537 92 if (m_mode == WriteOnly) {
Chris@537 93 m_flags = O_WRONLY | O_CREAT;
Chris@148 94 } else {
Chris@148 95 m_flags = O_RDONLY;
Chris@148 96 }
Chris@148 97
Chris@233 98 #ifdef _WIN32
Chris@233 99 m_flags |= O_BINARY;
Chris@233 100 #endif
Chris@233 101
Chris@148 102 #ifdef DEBUG_MATRIX_FILE
Chris@537 103 std::cerr << "MatrixFile(" << this << ")::MatrixFile: opening " << fileName.toStdString() << "..." << std::endl;
Chris@148 104 #endif
Chris@148 105
Chris@148 106 if ((m_fd = ::open(fileName.toLocal8Bit(), m_flags, m_fmode)) < 0) {
Chris@148 107 ::perror("Open failed");
Chris@148 108 std::cerr << "ERROR: MatrixFile::MatrixFile: "
Chris@148 109 << "Failed to open cache file \""
Chris@148 110 << fileName.toStdString() << "\"";
Chris@537 111 if (m_mode == WriteOnly) std::cerr << " for writing";
Chris@148 112 std::cerr << std::endl;
Chris@148 113 throw FailedToOpenFile(fileName);
Chris@148 114 }
Chris@148 115
Chris@537 116 #ifdef DEBUG_MATRIX_FILE
Chris@537 117 std::cerr << "MatrixFile(" << this << ")::MatrixFile: fd is " << m_fd << std::endl;
Chris@537 118 #endif
Chris@537 119
Chris@148 120 if (newFile) {
Chris@537 121 initialise(); // write header and "unwritten" column tags
Chris@148 122 } else {
Chris@148 123 size_t header[2];
Chris@148 124 if (::read(m_fd, header, 2 * sizeof(size_t)) < 0) {
Chris@236 125 ::perror("MatrixFile::MatrixFile: read failed");
Chris@148 126 std::cerr << "ERROR: MatrixFile::MatrixFile: "
Chris@148 127 << "Failed to read header (fd " << m_fd << ", file \""
Chris@148 128 << fileName.toStdString() << "\")" << std::endl;
Chris@148 129 throw FileReadFailed(fileName);
Chris@148 130 }
Chris@537 131 if (header[0] != m_width || header[1] != m_height) {
Chris@537 132 std::cerr << "ERROR: MatrixFile::MatrixFile: "
Chris@537 133 << "Dimensions in file header (" << header[0] << "x"
Chris@537 134 << header[1] << ") differ from expected dimensions "
Chris@537 135 << m_width << "x" << m_height << std::endl;
Chris@537 136 throw FailedToOpenFile(fileName);
Chris@537 137 }
Chris@148 138 }
Chris@148 139
Chris@148 140 m_fileName = fileName;
Chris@148 141 ++m_refcount[fileName];
Chris@148 142
Chris@537 143 #ifdef DEBUG_MATRIX_FILE
Chris@537 144 std::cerr << "MatrixFile[" << m_fd << "]::MatrixFile: File " << fileName.toStdString() << ", ref " << m_refcount[fileName] << std::endl;
Chris@148 145
Chris@537 146 std::cerr << "MatrixFile[" << m_fd << "]::MatrixFile: Done, size is " << "(" << m_width << ", " << m_height << ")" << std::endl;
Chris@537 147 #endif
Chris@148 148
Chris@148 149 ++totalCount;
Chris@537 150 ++openCount;
Chris@148 151 }
Chris@148 152
Chris@148 153 MatrixFile::~MatrixFile()
Chris@148 154 {
Chris@148 155 if (m_fd >= 0) {
Chris@148 156 if (::close(m_fd) < 0) {
Chris@148 157 ::perror("MatrixFile::~MatrixFile: close failed");
Chris@148 158 }
Chris@546 159 openCount --;
Chris@148 160 }
Chris@148 161
Chris@537 162 QMutexLocker locker(&m_createMutex);
Chris@537 163
Chris@148 164 if (m_fileName != "") {
Chris@148 165
Chris@148 166 if (--m_refcount[m_fileName] == 0) {
Chris@148 167
Chris@148 168 if (::unlink(m_fileName.toLocal8Bit())) {
Chris@537 169 std::cerr << "WARNING: MatrixFile::~MatrixFile: reference count reached 0, but failed to unlink file \"" << m_fileName.toStdString() << "\"" << std::endl;
Chris@148 170 } else {
Chris@537 171 std::cerr << "deleted " << m_fileName.toStdString() << std::endl;
Chris@148 172 }
Chris@148 173 }
Chris@148 174 }
Chris@537 175
Chris@537 176 if (m_mode == WriteOnly) {
Chris@537 177 totalStorage -= (m_headerSize + (m_width * m_height * m_cellSize) + m_width);
Chris@537 178 }
Chris@148 179 totalCount --;
Chris@148 180
Chris@537 181 #ifdef DEBUG_MATRIX_FILE
Chris@537 182 std::cerr << "MatrixFile[" << m_fd << "]::~MatrixFile: " << std::endl;
Chris@537 183 std::cerr << "MatrixFile: Total storage now " << totalStorage/1024 << "K in " << totalCount << " instances (" << openCount << " open)" << std::endl;
Chris@537 184 #endif
Chris@148 185 }
Chris@148 186
Chris@148 187 void
Chris@537 188 MatrixFile::initialise()
Chris@148 189 {
Chris@537 190 Profiler profiler("MatrixFile::initialise", true);
Chris@148 191
Chris@537 192 assert(m_mode == WriteOnly);
Chris@148 193
Chris@537 194 off_t off = m_headerSize + (m_width * m_height * m_cellSize) + m_width;
Chris@148 195
Chris@148 196 #ifdef DEBUG_MATRIX_FILE
Chris@537 197 std::cerr << "MatrixFile[" << m_fd << "]::initialise(" << m_width << ", " << m_height << "): cell size " << m_cellSize << ", header size " << m_headerSize << ", resizing file" << std::endl;
Chris@148 198 #endif
Chris@148 199
Chris@537 200 if (::lseek(m_fd, off - 1, SEEK_SET) < 0) {
Chris@537 201 ::perror("ERROR: MatrixFile::initialise: seek to end failed");
Chris@537 202 throw FileOperationFailed(m_fileName, "lseek");
Chris@148 203 }
Chris@148 204
Chris@537 205 unsigned char byte = 0;
Chris@537 206 if (::write(m_fd, &byte, 1) != 1) {
Chris@537 207 ::perror("ERROR: MatrixFile::initialise: write at end failed");
Chris@537 208 throw FileOperationFailed(m_fileName, "write");
Chris@537 209 }
Chris@148 210
Chris@537 211 if (::lseek(m_fd, 0, SEEK_SET) < 0) {
Chris@547 212 ::perror("ERROR: MatrixFile::initialise: Seek to write header failed");
Chris@148 213 throw FileOperationFailed(m_fileName, "lseek");
Chris@148 214 }
Chris@148 215
Chris@148 216 size_t header[2];
Chris@537 217 header[0] = m_width;
Chris@537 218 header[1] = m_height;
Chris@148 219 if (::write(m_fd, header, 2 * sizeof(size_t)) != 2 * sizeof(size_t)) {
Chris@547 220 ::perror("ERROR: MatrixFile::initialise: Failed to write header");
Chris@148 221 throw FileOperationFailed(m_fileName, "write");
Chris@148 222 }
Chris@148 223
Chris@537 224 if (m_mode == WriteOnly) {
Chris@537 225 totalStorage += (m_headerSize + (m_width * m_height * m_cellSize) + m_width);
Chris@148 226 }
Chris@148 227
Chris@537 228 #ifdef DEBUG_MATRIX_FILE
Chris@547 229 std::cerr << "MatrixFile[" << m_fd << "]::initialise(" << m_width << ", " << m_height << "): storage "
Chris@537 230 << (m_headerSize + m_width * m_height * m_cellSize + m_width) << std::endl;
Chris@148 231
Chris@537 232 std::cerr << "MatrixFile: Total storage " << totalStorage/1024 << "K" << std::endl;
Chris@148 233 #endif
Chris@148 234
Chris@148 235 seekTo(0, 0);
Chris@148 236 }
Chris@148 237
Chris@148 238 void
Chris@537 239 MatrixFile::close()
Chris@148 240 {
Chris@537 241 #ifdef DEBUG_MATRIX_FILE
Chris@537 242 std::cerr << "MatrixFile::close()" << std::endl;
Chris@537 243 #endif
Chris@537 244 if (m_fd >= 0) {
Chris@537 245 if (::close(m_fd) < 0) {
Chris@537 246 ::perror("MatrixFile::close: close failed");
Chris@537 247 }
Chris@537 248 m_fd = -1;
Chris@537 249 -- openCount;
Chris@148 250 }
Chris@148 251 }
Chris@148 252
Chris@148 253 void
Chris@148 254 MatrixFile::getColumnAt(size_t x, void *data)
Chris@148 255 {
Chris@537 256 assert(m_mode == ReadOnly);
Chris@537 257
Chris@537 258 #ifdef DEBUG_MATRIX_FILE_READ_SET
Chris@537 259 std::cerr << "MatrixFile[" << m_fd << "]::getColumnAt(" << x << ")" << std::endl;
Chris@537 260 #endif
Chris@537 261
Chris@408 262 Profiler profiler("MatrixFile::getColumnAt");
Chris@148 263
Chris@537 264 unsigned char set = 0;
Chris@537 265 if (!seekTo(x, 0)) {
Chris@537 266 std::cerr << "ERROR: MatrixFile::getColumnAt(" << x << "): Seek failed" << std::endl;
Chris@537 267 throw FileOperationFailed(m_fileName, "seek");
Chris@148 268 }
Chris@148 269
Chris@537 270 ssize_t r = -1;
Chris@537 271 r = ::read(m_fd, &set, 1);
Chris@537 272 if (r < 0) {
Chris@537 273 ::perror("MatrixFile::getColumnAt: read failed");
Chris@537 274 throw FileReadFailed(m_fileName);
Chris@537 275 }
Chris@537 276 if (!set) {
Chris@537 277 std::cerr << "MatrixFile[" << m_fd << "]::getColumnAt(" << x << "): Column has not been set" << std::endl;
Chris@537 278 return;
Chris@537 279 }
Chris@537 280
Chris@537 281 r = ::read(m_fd, data, m_height * m_cellSize);
Chris@537 282 if (r < 0) {
Chris@537 283 ::perror("MatrixFile::getColumnAt: read failed");
Chris@537 284 throw FileReadFailed(m_fileName);
Chris@537 285 }
Chris@537 286 }
Chris@537 287
Chris@537 288 bool
Chris@537 289 MatrixFile::haveSetColumnAt(size_t x) const
Chris@537 290 {
Chris@537 291 assert(m_mode == ReadOnly);
Chris@537 292
Chris@537 293 Profiler profiler("MatrixFile::haveSetColumnAt");
Chris@537 294
Chris@537 295 #ifdef DEBUG_MATRIX_FILE_READ_SET
Chris@537 296 std::cerr << "MatrixFile[" << m_fd << "]::haveSetColumnAt(" << x << ")" << std::endl;
Chris@537 297 // std::cerr << ".";
Chris@148 298 #endif
Chris@148 299
Chris@537 300 unsigned char set = 0;
Chris@537 301 if (!seekTo(x, 0)) {
Chris@537 302 std::cerr << "ERROR: MatrixFile::haveSetColumnAt(" << x << "): Seek failed" << std::endl;
Chris@537 303 throw FileOperationFailed(m_fileName, "seek");
Chris@537 304 }
Chris@148 305
Chris@537 306 ssize_t r = -1;
Chris@537 307 r = ::read(m_fd, &set, 1);
Chris@148 308 if (r < 0) {
Chris@537 309 ::perror("MatrixFile::haveSetColumnAt: read failed");
Chris@148 310 throw FileReadFailed(m_fileName);
Chris@148 311 }
Chris@148 312
Chris@537 313 return set;
Chris@148 314 }
Chris@148 315
Chris@148 316 void
Chris@148 317 MatrixFile::setColumnAt(size_t x, const void *data)
Chris@148 318 {
Chris@537 319 assert(m_mode == WriteOnly);
Chris@148 320
Chris@148 321 #ifdef DEBUG_MATRIX_FILE_READ_SET
Chris@537 322 std::cerr << "MatrixFile[" << m_fd << "]::setColumnAt(" << x << ")" << std::endl;
Chris@537 323 // std::cerr << ".";
Chris@148 324 #endif
Chris@148 325
Chris@148 326 ssize_t w = 0;
Chris@148 327
Chris@537 328 if (!seekTo(x, 0)) {
Chris@537 329 std::cerr << "ERROR: MatrixFile::setColumnAt(" << x << "): Seek failed" << std::endl;
Chris@537 330 throw FileOperationFailed(m_fileName, "seek");
Chris@148 331 }
Chris@148 332
Chris@537 333 unsigned char set = 0;
Chris@537 334 w = ::write(m_fd, &set, 1);
Chris@537 335 if (w != 1) {
Chris@537 336 ::perror("WARNING: MatrixFile::setColumnAt: write failed (1)");
Chris@148 337 throw FileOperationFailed(m_fileName, "write");
Chris@537 338 }
Chris@537 339
Chris@537 340 w = ::write(m_fd, data, m_height * m_cellSize);
Chris@537 341 if (w != ssize_t(m_height * m_cellSize)) {
Chris@537 342 ::perror("WARNING: MatrixFile::setColumnAt: write failed (2)");
Chris@537 343 throw FileOperationFailed(m_fileName, "write");
Chris@537 344 }
Chris@537 345 /*
Chris@537 346 if (x == 0) {
Chris@537 347 std::cerr << "Wrote " << m_height * m_cellSize << " bytes, as follows:" << std::endl;
Chris@537 348 for (int i = 0; i < m_height * m_cellSize; ++i) {
Chris@537 349 std::cerr << (int)(((char *)data)[i]) << " ";
Chris@537 350 }
Chris@537 351 std::cerr << std::endl;
Chris@537 352 }
Chris@537 353 */
Chris@537 354 if (!seekTo(x, 0)) {
Chris@537 355 std::cerr << "MatrixFile[" << m_fd << "]::setColumnAt(" << x << "): Seek failed" << std::endl;
Chris@148 356 throw FileOperationFailed(m_fileName, "seek");
Chris@537 357 }
Chris@537 358
Chris@537 359 set = 1;
Chris@537 360 w = ::write(m_fd, &set, 1);
Chris@537 361 if (w != 1) {
Chris@537 362 ::perror("WARNING: MatrixFile::setColumnAt: write failed (3)");
Chris@537 363 throw FileOperationFailed(m_fileName, "write");
Chris@148 364 }
Chris@148 365 }
Chris@148 366
Chris@537 367 bool
Chris@537 368 MatrixFile::seekTo(size_t x, size_t y) const
Chris@148 369 {
Chris@537 370 if (m_fd < 0) {
Chris@537 371 std::cerr << "ERROR: MatrixFile::seekTo: File not open" << std::endl;
Chris@537 372 return false;
Chris@148 373 }
Chris@148 374
Chris@537 375 off_t off = m_headerSize + x * m_height * m_cellSize + x + y * m_cellSize;
Chris@148 376
Chris@148 377 #ifdef DEBUG_MATRIX_FILE_READ_SET
Chris@537 378 std::cerr << "MatrixFile[" << m_fd << "]::seekTo(" << x << "," << y << "): off = " << off << std::endl;
Chris@148 379 #endif
Chris@148 380
Chris@148 381 if (::lseek(m_fd, off, SEEK_SET) == (off_t)-1) {
Chris@148 382 ::perror("Seek failed");
Chris@148 383 std::cerr << "ERROR: MatrixFile::seekTo(" << x << ", " << y
Chris@537 384 << ") = " << off << " failed" << std::endl;
Chris@148 385 return false;
Chris@148 386 }
Chris@148 387
Chris@148 388 return true;
Chris@148 389 }
Chris@148 390