annotate data/fileio/MatrixFile.cpp @ 167:665342c6ec57

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