annotate data/fileio/MatrixFile.cpp @ 409:6075c90744d4

...
author Chris Cannam
date Fri, 09 May 2008 12:39:02 +0000
parents 115f60df1e4d
children 52303ec15cd2
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@148 7 This file copyright 2006 Chris Cannam.
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@403 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@148 48 QMutex MatrixFile::m_refcountMutex;
Chris@148 49
Chris@148 50 MatrixFile::ResizeableBitsetMap MatrixFile::m_columnBitsets;
Chris@148 51 QMutex MatrixFile::m_columnBitsetWriteMutex;
Chris@148 52
Chris@148 53 FileReadThread *MatrixFile::m_readThread = 0;
Chris@148 54
Chris@148 55 static size_t totalStorage = 0;
Chris@148 56 static size_t totalMemory = 0;
Chris@148 57 static size_t totalCount = 0;
Chris@148 58
Chris@148 59 MatrixFile::MatrixFile(QString fileBase, Mode mode,
Chris@148 60 size_t cellSize, bool eagerCache) :
Chris@148 61 m_fd(-1),
Chris@148 62 m_mode(mode),
Chris@148 63 m_flags(0),
Chris@148 64 m_fmode(0),
Chris@148 65 m_cellSize(cellSize),
Chris@148 66 m_width(0),
Chris@148 67 m_height(0),
Chris@148 68 m_headerSize(2 * sizeof(size_t)),
Chris@148 69 m_defaultCacheWidth(1024),
Chris@148 70 m_prevX(0),
Chris@148 71 m_eagerCache(eagerCache),
Chris@148 72 m_requestToken(-1),
Chris@148 73 m_spareData(0),
Chris@148 74 m_columnBitset(0)
Chris@148 75 {
Chris@148 76 Profiler profiler("MatrixFile::MatrixFile", true);
Chris@148 77
Chris@148 78 if (!m_readThread) {
Chris@148 79 m_readThread = new FileReadThread;
Chris@148 80 m_readThread->start();
Chris@148 81 }
Chris@148 82
Chris@148 83 m_cache.data = 0;
Chris@148 84
Chris@148 85 QDir tempDir(TempDirectory::getInstance()->getPath());
Chris@148 86 QString fileName(tempDir.filePath(QString("%1.mfc").arg(fileBase)));
Chris@148 87 bool newFile = !QFileInfo(fileName).exists();
Chris@148 88
Chris@148 89 if (newFile && m_mode == ReadOnly) {
Chris@148 90 std::cerr << "ERROR: MatrixFile::MatrixFile: Read-only mode "
Chris@148 91 << "specified, but cache file does not exist" << std::endl;
Chris@148 92 throw FileNotFound(fileName);
Chris@148 93 }
Chris@148 94
Chris@148 95 if (!newFile && m_mode == ReadWrite) {
Chris@148 96 std::cerr << "Note: MatrixFile::MatrixFile: Read/write mode "
Chris@148 97 << "specified, but file already exists; falling back to "
Chris@148 98 << "read-only mode" << std::endl;
Chris@148 99 m_mode = ReadOnly;
Chris@148 100 }
Chris@148 101
Chris@148 102 if (!eagerCache && m_mode == ReadOnly) {
Chris@148 103 std::cerr << "WARNING: MatrixFile::MatrixFile: Eager cacheing not "
Chris@148 104 << "specified, but file is open in read-only mode -- cache "
Chris@148 105 << "will not be used" << std::endl;
Chris@148 106 }
Chris@148 107
Chris@148 108 m_flags = 0;
Chris@148 109 m_fmode = S_IRUSR | S_IWUSR;
Chris@148 110
Chris@148 111 if (m_mode == ReadWrite) {
Chris@148 112 m_flags = O_RDWR | O_CREAT;
Chris@148 113 } else {
Chris@148 114 m_flags = O_RDONLY;
Chris@148 115 }
Chris@148 116
Chris@233 117 #ifdef _WIN32
Chris@233 118 m_flags |= O_BINARY;
Chris@233 119 #endif
Chris@233 120
Chris@148 121 #ifdef DEBUG_MATRIX_FILE
Chris@148 122 std::cerr << "MatrixFile::MatrixFile: opening " << fileName.toStdString() << "..." << std::endl;
Chris@148 123 #endif
Chris@148 124
Chris@148 125 if ((m_fd = ::open(fileName.toLocal8Bit(), m_flags, m_fmode)) < 0) {
Chris@148 126 ::perror("Open failed");
Chris@148 127 std::cerr << "ERROR: MatrixFile::MatrixFile: "
Chris@148 128 << "Failed to open cache file \""
Chris@148 129 << fileName.toStdString() << "\"";
Chris@148 130 if (m_mode == ReadWrite) std::cerr << " for writing";
Chris@148 131 std::cerr << std::endl;
Chris@148 132 throw FailedToOpenFile(fileName);
Chris@148 133 }
Chris@148 134
Chris@148 135 if (newFile) {
Chris@148 136 resize(0, 0); // write header
Chris@148 137 } else {
Chris@148 138 size_t header[2];
Chris@148 139 if (::read(m_fd, header, 2 * sizeof(size_t)) < 0) {
Chris@236 140 ::perror("MatrixFile::MatrixFile: read failed");
Chris@148 141 std::cerr << "ERROR: MatrixFile::MatrixFile: "
Chris@148 142 << "Failed to read header (fd " << m_fd << ", file \""
Chris@148 143 << fileName.toStdString() << "\")" << std::endl;
Chris@148 144 throw FileReadFailed(fileName);
Chris@148 145 }
Chris@148 146 m_width = header[0];
Chris@148 147 m_height = header[1];
Chris@148 148 seekTo(0, 0);
Chris@148 149 }
Chris@148 150
Chris@148 151 m_fileName = fileName;
Chris@148 152
Chris@408 153 {
Chris@408 154 MutexLocker locker
Chris@408 155 (&m_columnBitsetWriteMutex,
Chris@408 156 "MatrixFile::MatrixFile::m_columnBitsetWriteMutex");
Chris@148 157
Chris@408 158 if (m_columnBitsets.find(m_fileName) == m_columnBitsets.end()) {
Chris@408 159 m_columnBitsets[m_fileName] = new ResizeableBitset;
Chris@408 160 }
Chris@408 161 m_columnBitset = m_columnBitsets[m_fileName];
Chris@148 162 }
Chris@148 163
Chris@408 164 MutexLocker locker(&m_refcountMutex,
Chris@408 165 "MatrixFile::MatrixFile::m_refcountMutex");
Chris@148 166 ++m_refcount[fileName];
Chris@148 167
Chris@259 168 // std::cerr << "MatrixFile(" << this << "): fd " << m_fd << ", file " << fileName.toStdString() << ", ref " << m_refcount[fileName] << std::endl;
Chris@148 169
Chris@148 170 // std::cerr << "MatrixFile::MatrixFile: Done, size is " << "(" << m_width << ", " << m_height << ")" << std::endl;
Chris@148 171
Chris@148 172 ++totalCount;
Chris@148 173
Chris@148 174 }
Chris@148 175
Chris@148 176 MatrixFile::~MatrixFile()
Chris@148 177 {
Chris@148 178 char *requestData = 0;
Chris@148 179
Chris@148 180 if (m_requestToken >= 0) {
Chris@148 181 FileReadThread::Request request;
Chris@148 182 if (m_readThread->getRequest(m_requestToken, request)) {
Chris@148 183 requestData = request.data;
Chris@148 184 }
Chris@148 185 m_readThread->cancel(m_requestToken);
Chris@148 186 }
Chris@148 187
Chris@148 188 if (requestData) free(requestData);
Chris@148 189 if (m_cache.data) free(m_cache.data);
Chris@148 190 if (m_spareData) free(m_spareData);
Chris@148 191
Chris@148 192 if (m_fd >= 0) {
Chris@148 193 if (::close(m_fd) < 0) {
Chris@148 194 ::perror("MatrixFile::~MatrixFile: close failed");
Chris@148 195 }
Chris@148 196 }
Chris@148 197
Chris@148 198 if (m_fileName != "") {
Chris@148 199
Chris@408 200 MutexLocker locker(&m_refcountMutex,
Chris@408 201 "MatrixFile::~MatrixFile::m_refcountMutex");
Chris@148 202
Chris@148 203 if (--m_refcount[m_fileName] == 0) {
Chris@148 204
Chris@148 205 if (::unlink(m_fileName.toLocal8Bit())) {
Chris@259 206 // ::perror("Unlink failed");
Chris@259 207 // std::cerr << "WARNING: MatrixFile::~MatrixFile: reference count reached 0, but failed to unlink file \"" << m_fileName.toStdString() << "\"" << std::endl;
Chris@148 208 } else {
Chris@259 209 // std::cerr << "deleted " << m_fileName.toStdString() << std::endl;
Chris@148 210 }
Chris@148 211
Chris@408 212 MutexLocker locker2
Chris@408 213 (&m_columnBitsetWriteMutex,
Chris@408 214 "MatrixFile::~MatrixFile::m_columnBitsetWriteMutex");
Chris@148 215 m_columnBitsets.erase(m_fileName);
Chris@148 216 delete m_columnBitset;
Chris@148 217 }
Chris@148 218 }
Chris@148 219
Chris@148 220 totalStorage -= (m_headerSize + (m_width * m_height * m_cellSize));
Chris@148 221 totalMemory -= (2 * m_defaultCacheWidth * m_height * m_cellSize);
Chris@148 222 totalCount --;
Chris@148 223
Chris@259 224 // std::cerr << "MatrixFile::~MatrixFile: " << std::endl;
Chris@259 225 // std::cerr << "Total storage now " << totalStorage/1024 << "K, theoretical max memory "
Chris@259 226 // << totalMemory/1024 << "K in " << totalCount << " instances" << std::endl;
Chris@148 227
Chris@148 228 }
Chris@148 229
Chris@148 230 void
Chris@148 231 MatrixFile::resize(size_t w, size_t h)
Chris@148 232 {
Chris@148 233 Profiler profiler("MatrixFile::resize", true);
Chris@148 234
Chris@148 235 assert(m_mode == ReadWrite);
Chris@148 236
Chris@408 237 MutexLocker locker(&m_fdMutex, "MatrixFile::resize::m_fdMutex");
Chris@148 238
Chris@148 239 totalStorage -= (m_headerSize + (m_width * m_height * m_cellSize));
Chris@148 240 totalMemory -= (2 * m_defaultCacheWidth * m_height * m_cellSize);
Chris@148 241
Chris@148 242 off_t off = m_headerSize + (w * h * m_cellSize);
Chris@148 243
Chris@148 244 #ifdef DEBUG_MATRIX_FILE
Chris@148 245 std::cerr << "MatrixFile::resize(" << w << ", " << h << "): resizing file" << std::endl;
Chris@148 246 #endif
Chris@148 247
Chris@148 248 if (w * h < m_width * m_height) {
Chris@148 249 if (::ftruncate(m_fd, off) < 0) {
Chris@148 250 ::perror("WARNING: MatrixFile::resize: ftruncate failed");
Chris@148 251 throw FileOperationFailed(m_fileName, "ftruncate");
Chris@148 252 }
Chris@148 253 }
Chris@148 254
Chris@148 255 m_width = 0;
Chris@148 256 m_height = 0;
Chris@148 257
Chris@148 258 if (::lseek(m_fd, 0, SEEK_SET) == (off_t)-1) {
Chris@148 259 ::perror("ERROR: MatrixFile::resize: Seek to write header failed");
Chris@148 260 throw FileOperationFailed(m_fileName, "lseek");
Chris@148 261 }
Chris@148 262
Chris@148 263 size_t header[2];
Chris@148 264 header[0] = w;
Chris@148 265 header[1] = h;
Chris@148 266 if (::write(m_fd, header, 2 * sizeof(size_t)) != 2 * sizeof(size_t)) {
Chris@148 267 ::perror("ERROR: MatrixFile::resize: Failed to write header");
Chris@148 268 throw FileOperationFailed(m_fileName, "write");
Chris@148 269 }
Chris@148 270
Chris@148 271 if (w > 0 && m_defaultCacheWidth > w) {
Chris@148 272 m_defaultCacheWidth = w;
Chris@148 273 }
Chris@148 274
Chris@148 275 static size_t maxCacheMB = 16;
Chris@148 276 if (2 * m_defaultCacheWidth * h * m_cellSize > maxCacheMB * 1024 * 1024) { //!!!
Chris@148 277 m_defaultCacheWidth = (maxCacheMB * 1024 * 1024) / (2 * h * m_cellSize);
Chris@148 278 if (m_defaultCacheWidth < 16) m_defaultCacheWidth = 16;
Chris@148 279 }
Chris@148 280
Chris@148 281 if (m_columnBitset) {
Chris@408 282 MutexLocker locker(&m_columnBitsetWriteMutex,
Chris@408 283 "MatrixFile::resize::m_columnBitsetWriteMutex");
Chris@148 284 m_columnBitset->resize(w);
Chris@148 285 }
Chris@148 286
Chris@148 287 if (m_cache.data) {
Chris@148 288 free(m_cache.data);
Chris@148 289 m_cache.data = 0;
Chris@148 290 }
Chris@148 291
Chris@148 292 if (m_spareData) {
Chris@148 293 free(m_spareData);
Chris@148 294 m_spareData = 0;
Chris@148 295 }
Chris@148 296
Chris@148 297 m_width = w;
Chris@148 298 m_height = h;
Chris@148 299
Chris@148 300 totalStorage += (m_headerSize + (m_width * m_height * m_cellSize));
Chris@148 301 totalMemory += (2 * m_defaultCacheWidth * m_height * m_cellSize);
Chris@148 302
Chris@148 303 #ifdef DEBUG_MATRIX_FILE
Chris@148 304 std::cerr << "MatrixFile::resize(" << w << ", " << h << "): cache width "
Chris@148 305 << m_defaultCacheWidth << ", storage "
Chris@148 306 << (m_headerSize + w * h * m_cellSize) << ", mem "
Chris@148 307 << (2 * h * m_defaultCacheWidth * m_cellSize) << std::endl;
Chris@148 308
Chris@148 309 std::cerr << "Total storage " << totalStorage/1024 << "K, theoretical max memory "
Chris@148 310 << totalMemory/1024 << "K in " << totalCount << " instances" << std::endl;
Chris@148 311 #endif
Chris@148 312
Chris@148 313 seekTo(0, 0);
Chris@148 314 }
Chris@148 315
Chris@148 316 void
Chris@148 317 MatrixFile::reset()
Chris@148 318 {
Chris@148 319 Profiler profiler("MatrixFile::reset", true);
Chris@148 320
Chris@148 321 assert (m_mode == ReadWrite);
Chris@148 322
Chris@148 323 if (m_eagerCache) {
Chris@148 324 void *emptyCol = calloc(m_height, m_cellSize);
Chris@148 325 for (size_t x = 0; x < m_width; ++x) setColumnAt(x, emptyCol);
Chris@148 326 free(emptyCol);
Chris@148 327 }
Chris@148 328
Chris@148 329 if (m_columnBitset) {
Chris@408 330 MutexLocker locker(&m_columnBitsetWriteMutex,
Chris@408 331 "MatrixFile::reset::m_columnBitsetWriteMutex");
Chris@148 332 m_columnBitset->resize(m_width);
Chris@148 333 }
Chris@148 334 }
Chris@148 335
Chris@148 336 void
Chris@148 337 MatrixFile::getColumnAt(size_t x, void *data)
Chris@148 338 {
Chris@408 339 Profiler profiler("MatrixFile::getColumnAt");
Chris@148 340
Chris@148 341 // assert(haveSetColumnAt(x));
Chris@148 342
Chris@148 343 if (getFromCache(x, 0, m_height, data)) return;
Chris@148 344
Chris@408 345 Profiler profiler2("MatrixFile::getColumnAt (uncached)");
Chris@148 346
Chris@148 347 ssize_t r = 0;
Chris@148 348
Chris@148 349 #ifdef DEBUG_MATRIX_FILE
Chris@148 350 std::cerr << "MatrixFile::getColumnAt(" << x << ")"
Chris@148 351 << ": reading the slow way";
Chris@148 352
Chris@148 353 if (m_requestToken >= 0 &&
Chris@148 354 x >= m_requestingX &&
Chris@148 355 x < m_requestingX + m_requestingWidth) {
Chris@148 356
Chris@148 357 std::cerr << " (awaiting " << m_requestingX << ", " << m_requestingWidth << " from disk)";
Chris@148 358 }
Chris@148 359
Chris@148 360 std::cerr << std::endl;
Chris@148 361 #endif
Chris@148 362
Chris@408 363 {
Chris@408 364 MutexLocker locker(&m_fdMutex, "MatrixFile::getColumnAt::m_fdMutex");
Chris@148 365
Chris@408 366 if (seekTo(x, 0)) {
Chris@408 367 r = ::read(m_fd, data, m_height * m_cellSize);
Chris@408 368 }
Chris@148 369 }
Chris@148 370
Chris@148 371 if (r < 0) {
Chris@148 372 ::perror("MatrixFile::getColumnAt: read failed");
Chris@236 373 std::cerr << "ERROR: MatrixFile::getColumnAt: "
Chris@236 374 << "Failed to read column " << x << " (height " << m_height << ", cell size " << m_cellSize << ", fd " << m_fd << ", file \""
Chris@236 375 << m_fileName.toStdString() << "\")" << std::endl;
Chris@148 376 throw FileReadFailed(m_fileName);
Chris@148 377 }
Chris@148 378
Chris@148 379 return;
Chris@148 380 }
Chris@148 381
Chris@148 382 bool
Chris@148 383 MatrixFile::getFromCache(size_t x, size_t ystart, size_t ycount, void *data)
Chris@148 384 {
Chris@408 385 bool fail = false;
Chris@408 386 bool primeLeft = false;
Chris@148 387
Chris@408 388 {
Chris@408 389 MutexLocker locker(&m_cacheMutex,
Chris@408 390 "MatrixFile::getFromCache::m_cacheMutex");
Chris@408 391
Chris@408 392 if (!m_cache.data || x < m_cache.x || x >= m_cache.x + m_cache.width) {
Chris@408 393 fail = true;
Chris@408 394 primeLeft = (m_cache.data && x < m_cache.x);
Chris@408 395 } else {
Chris@408 396 memcpy(data,
Chris@408 397 m_cache.data + m_cellSize * ((x - m_cache.x) * m_height + ystart),
Chris@408 398 ycount * m_cellSize);
Chris@408 399 }
Chris@408 400 }
Chris@408 401
Chris@408 402 if (fail) {
Chris@408 403 primeCache(x, primeLeft); // this doesn't take effect until a later callback
Chris@148 404 m_prevX = x;
Chris@148 405 return false;
Chris@148 406 }
Chris@148 407
Chris@148 408 if (m_cache.x > 0 && x < m_prevX && x < m_cache.x + m_cache.width/4) {
Chris@148 409 primeCache(x, true);
Chris@148 410 }
Chris@148 411
Chris@148 412 if (m_cache.x + m_cache.width < m_width &&
Chris@148 413 x > m_prevX &&
Chris@148 414 x > m_cache.x + (m_cache.width * 3) / 4) {
Chris@148 415 primeCache(x, false);
Chris@148 416 }
Chris@148 417
Chris@148 418 m_prevX = x;
Chris@148 419 return true;
Chris@148 420 }
Chris@148 421
Chris@148 422 void
Chris@148 423 MatrixFile::setColumnAt(size_t x, const void *data)
Chris@148 424 {
Chris@148 425 assert(m_mode == ReadWrite);
Chris@148 426
Chris@148 427 #ifdef DEBUG_MATRIX_FILE_READ_SET
Chris@148 428 std::cerr << "MatrixFile::setColumnAt(" << x << ")" << std::endl;
Chris@148 429 #endif
Chris@148 430
Chris@148 431 ssize_t w = 0;
Chris@148 432 bool seekFailed = false;
Chris@408 433
Chris@408 434 {
Chris@408 435 MutexLocker locker(&m_fdMutex, "MatrixFile::setColumnAt::m_fdMutex");
Chris@148 436
Chris@408 437 if (seekTo(x, 0)) {
Chris@408 438 w = ::write(m_fd, data, m_height * m_cellSize);
Chris@408 439 } else {
Chris@408 440 seekFailed = true;
Chris@408 441 }
Chris@148 442 }
Chris@148 443
Chris@148 444 if (!seekFailed && w != ssize_t(m_height * m_cellSize)) {
Chris@148 445 ::perror("WARNING: MatrixFile::setColumnAt: write failed");
Chris@148 446 throw FileOperationFailed(m_fileName, "write");
Chris@148 447 } else if (seekFailed) {
Chris@148 448 throw FileOperationFailed(m_fileName, "seek");
Chris@148 449 } else {
Chris@408 450 MutexLocker locker
Chris@408 451 (&m_columnBitsetWriteMutex,
Chris@408 452 "MatrixFile::setColumnAt::m_columnBitsetWriteMutex");
Chris@148 453 m_columnBitset->set(x);
Chris@148 454 }
Chris@148 455 }
Chris@148 456
Chris@148 457 void
Chris@148 458 MatrixFile::suspend()
Chris@148 459 {
Chris@408 460 MutexLocker locker(&m_fdMutex, "MatrixFile::suspend::m_fdMutex");
Chris@408 461 MutexLocker locker2(&m_cacheMutex, "MatrixFile::suspend::m_cacheMutex");
Chris@148 462
Chris@148 463 if (m_fd < 0) return; // already suspended
Chris@148 464
Chris@148 465 #ifdef DEBUG_MATRIX_FILE
Chris@148 466 std::cerr << "MatrixFile(" << this << ":" << m_fileName.toStdString() << ")::suspend(): fd was " << m_fd << std::endl;
Chris@148 467 #endif
Chris@148 468
Chris@148 469 if (m_requestToken >= 0) {
Chris@148 470 void *data = 0;
Chris@148 471 FileReadThread::Request request;
Chris@148 472 if (m_readThread->getRequest(m_requestToken, request)) {
Chris@148 473 data = request.data;
Chris@148 474 }
Chris@148 475 m_readThread->cancel(m_requestToken);
Chris@148 476 if (data) free(data);
Chris@148 477 m_requestToken = -1;
Chris@148 478 }
Chris@148 479
Chris@148 480 if (m_cache.data) {
Chris@148 481 free(m_cache.data);
Chris@148 482 m_cache.data = 0;
Chris@148 483 }
Chris@148 484
Chris@148 485 if (m_spareData) {
Chris@148 486 free(m_spareData);
Chris@148 487 m_spareData = 0;
Chris@148 488 }
Chris@148 489
Chris@148 490 if (::close(m_fd) < 0) {
Chris@148 491 ::perror("WARNING: MatrixFile::suspend: close failed");
Chris@148 492 throw FileOperationFailed(m_fileName, "close");
Chris@148 493 }
Chris@148 494
Chris@148 495 m_fd = -1;
Chris@148 496 }
Chris@148 497
Chris@148 498 void
Chris@148 499 MatrixFile::resume()
Chris@148 500 {
Chris@148 501 if (m_fd >= 0) return;
Chris@148 502
Chris@148 503 #ifdef DEBUG_MATRIX_FILE
Chris@148 504 std::cerr << "MatrixFile(" << this << ")::resume()" << std::endl;
Chris@148 505 #endif
Chris@148 506
Chris@148 507 if ((m_fd = ::open(m_fileName.toLocal8Bit(), m_flags, m_fmode)) < 0) {
Chris@148 508 ::perror("Open failed");
Chris@148 509 std::cerr << "ERROR: MatrixFile::resume: "
Chris@148 510 << "Failed to open cache file \""
Chris@148 511 << m_fileName.toStdString() << "\"";
Chris@148 512 if (m_mode == ReadWrite) std::cerr << " for writing";
Chris@148 513 std::cerr << std::endl;
Chris@148 514 throw FailedToOpenFile(m_fileName);
Chris@148 515 }
Chris@148 516
Chris@148 517 std::cerr << "MatrixFile(" << this << ":" << m_fileName.toStdString() << ")::resume(): fd is " << m_fd << std::endl;
Chris@148 518 }
Chris@148 519
Chris@148 520 void
Chris@148 521 MatrixFile::primeCache(size_t x, bool goingLeft)
Chris@148 522 {
Chris@148 523 // Profiler profiler("MatrixFile::primeCache");
Chris@148 524
Chris@148 525 #ifdef DEBUG_MATRIX_FILE_READ_SET
Chris@148 526 std::cerr << "MatrixFile::primeCache(" << x << ", " << goingLeft << ")" << std::endl;
Chris@148 527 #endif
Chris@148 528
Chris@148 529 size_t rx = x;
Chris@148 530 size_t rw = m_defaultCacheWidth;
Chris@148 531
Chris@148 532 size_t left = rw / 3;
Chris@148 533 if (goingLeft) left = (rw * 2) / 3;
Chris@148 534
Chris@148 535 if (rx > left) rx -= left;
Chris@148 536 else rx = 0;
Chris@148 537
Chris@148 538 if (rx + rw > m_width) rw = m_width - rx;
Chris@148 539
Chris@148 540 if (!m_eagerCache) {
Chris@148 541
Chris@148 542 size_t ti = 0;
Chris@148 543
Chris@148 544 for (ti = 0; ti < rw; ++ti) {
Chris@148 545 if (!m_columnBitset->get(rx + ti)) break;
Chris@148 546 }
Chris@148 547
Chris@148 548 #ifdef DEBUG_MATRIX_FILE
Chris@148 549 if (ti < rw) {
Chris@148 550 std::cerr << "eagerCache is false and there's a hole at "
Chris@148 551 << rx + ti << ", reducing rw from " << rw << " to "
Chris@148 552 << ti << std::endl;
Chris@148 553 }
Chris@148 554 #endif
Chris@148 555
Chris@148 556 rw = std::min(rw, ti);
Chris@148 557 if (rw < 10 || rx + rw <= x) return;
Chris@148 558 }
Chris@148 559
Chris@408 560 MutexLocker locker(&m_cacheMutex, "MatrixFile::primeCache::m_cacheMutex");
Chris@148 561
Chris@148 562 FileReadThread::Request request;
Chris@148 563
Chris@148 564 if (m_requestToken >= 0 &&
Chris@148 565 m_readThread->getRequest(m_requestToken, request)) {
Chris@148 566
Chris@148 567 if (x >= m_requestingX &&
Chris@148 568 x < m_requestingX + m_requestingWidth) {
Chris@148 569
Chris@148 570 if (m_readThread->isReady(m_requestToken)) {
Chris@148 571
Chris@148 572 if (!request.successful) {
Chris@148 573 std::cerr << "ERROR: MatrixFile::primeCache: Last request was unsuccessful" << std::endl;
Chris@148 574 throw FileReadFailed(m_fileName);
Chris@148 575 }
Chris@148 576
Chris@148 577 #ifdef DEBUG_MATRIX_FILE_READ_SET
Chris@148 578 std::cerr << "last request is ready! (" << m_requestingX << ", "<< m_requestingWidth << ")" << std::endl;
Chris@148 579 #endif
Chris@148 580
Chris@148 581 m_cache.x = (request.start - m_headerSize) / (m_height * m_cellSize);
Chris@148 582 m_cache.width = request.size / (m_height * m_cellSize);
Chris@148 583
Chris@148 584 #ifdef DEBUG_MATRIX_FILE_READ_SET
Chris@148 585 std::cerr << "received last request: actual size is: " << m_cache.x << ", " << m_cache.width << std::endl;
Chris@148 586 #endif
Chris@148 587
Chris@148 588 if (m_cache.data) {
Chris@148 589 if (m_spareData) free(m_spareData);
Chris@148 590 m_spareData = m_cache.data;
Chris@148 591 }
Chris@148 592 m_cache.data = request.data;
Chris@148 593
Chris@148 594 m_readThread->done(m_requestToken);
Chris@148 595 m_requestToken = -1;
Chris@148 596 }
Chris@148 597
Chris@148 598 // already requested something covering this area; wait for it
Chris@148 599 return;
Chris@148 600 }
Chris@148 601
Chris@148 602 // the current request is no longer of any use
Chris@148 603 m_readThread->cancel(m_requestToken);
Chris@148 604
Chris@148 605 // crude way to avoid leaking the data
Chris@148 606 while (!m_readThread->isCancelled(m_requestToken)) {
Chris@148 607 usleep(10000);
Chris@148 608 }
Chris@148 609
Chris@148 610 #ifdef DEBUG_MATRIX_FILE_READ_SET
Chris@148 611 std::cerr << "cancelled " << m_requestToken << std::endl;
Chris@148 612 #endif
Chris@148 613
Chris@148 614 if (m_spareData) free(m_spareData);
Chris@148 615 m_spareData = request.data;
Chris@148 616 m_readThread->done(m_requestToken);
Chris@148 617
Chris@148 618 m_requestToken = -1;
Chris@148 619 }
Chris@148 620
Chris@148 621 if (m_fd < 0) {
Chris@408 622 MutexLocker locker(&m_fdMutex, "MatrixFile::primeCache::m_fdMutex");
Chris@148 623 if (m_fd < 0) resume();
Chris@148 624 }
Chris@148 625
Chris@148 626 request.fd = m_fd;
Chris@148 627 request.mutex = &m_fdMutex;
Chris@148 628 request.start = m_headerSize + rx * m_height * m_cellSize;
Chris@148 629 request.size = rw * m_height * m_cellSize;
Chris@148 630 request.data = (char *)realloc(m_spareData, rw * m_height * m_cellSize);
Chris@148 631 MUNLOCK(request.data, rw * m_height * m_cellSize);
Chris@148 632 m_spareData = 0;
Chris@148 633
Chris@148 634 m_requestingX = rx;
Chris@148 635 m_requestingWidth = rw;
Chris@148 636
Chris@148 637 int token = m_readThread->request(request);
Chris@148 638 #ifdef DEBUG_MATRIX_FILE_READ_SET
Chris@148 639 std::cerr << "MatrixFile::primeCache: request token is "
Chris@148 640 << token << " (x = [" << rx << "], w = [" << rw << "], left = [" << goingLeft << "])" << std::endl;
Chris@148 641 #endif
Chris@148 642 m_requestToken = token;
Chris@148 643 }
Chris@148 644
Chris@148 645 bool
Chris@148 646 MatrixFile::seekTo(size_t x, size_t y)
Chris@148 647 {
Chris@148 648 if (m_fd < 0) resume();
Chris@148 649
Chris@148 650 off_t off = m_headerSize + (x * m_height + y) * m_cellSize;
Chris@148 651
Chris@148 652 if (::lseek(m_fd, off, SEEK_SET) == (off_t)-1) {
Chris@148 653 ::perror("Seek failed");
Chris@148 654 std::cerr << "ERROR: MatrixFile::seekTo(" << x << ", " << y
Chris@148 655 << ") failed" << std::endl;
Chris@148 656 return false;
Chris@148 657 }
Chris@148 658
Chris@148 659 return true;
Chris@148 660 }
Chris@148 661