annotate data/fileio/MatrixFile.cpp @ 150:4b2ea82fd0ed

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