annotate data/fileio/MatrixFile.cpp @ 405:65311fb86166

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