comparison data/fileio/MatrixFile.cpp @ 455:3e0f1f7bec85

* Fix a nasty and long-standing race condition in MatrixFile's use of FileReadThread that was causing crashes sometimes
author Chris Cannam
date Thu, 09 Oct 2008 20:10:28 +0000
parents 1f15beefcd76
children 3cc4b7cd2aa5
comparison
equal deleted inserted replaced
454:ba7aaacb7211 455:3e0f1f7bec85
33 #include <cstdlib> 33 #include <cstdlib>
34 34
35 #include <QFileInfo> 35 #include <QFileInfo>
36 #include <QDir> 36 #include <QDir>
37 37
38 #define DEBUG_MATRIX_FILE 1 38 //#define DEBUG_MATRIX_FILE 1
39 //#define DEBUG_MATRIX_FILE_READ_SET 1 39 //#define DEBUG_MATRIX_FILE_READ_SET 1
40 40
41 #ifdef DEBUG_MATRIX_FILE_READ_SET 41 #ifdef DEBUG_MATRIX_FILE_READ_SET
42 #ifndef DEBUG_MATRIX_FILE 42 #ifndef DEBUG_MATRIX_FILE
43 #define DEBUG_MATRIX_FILE 1 43 #define DEBUG_MATRIX_FILE 1
519 #ifdef DEBUG_MATRIX_FILE 519 #ifdef DEBUG_MATRIX_FILE
520 std::cerr << "MatrixFile(" << this << ":" << m_fileName.toStdString() << ")::resume(): fd is " << m_fd << std::endl; 520 std::cerr << "MatrixFile(" << this << ":" << m_fileName.toStdString() << ")::resume(): fd is " << m_fd << std::endl;
521 #endif 521 #endif
522 } 522 }
523 523
524 static int alloc = 0;
525
526 void 524 void
527 MatrixFile::primeCache(size_t x, bool goingLeft) 525 MatrixFile::primeCache(size_t x, bool goingLeft)
528 { 526 {
529 // Profiler profiler("MatrixFile::primeCache"); 527 // Profiler profiler("MatrixFile::primeCache");
530 528
563 if (rw < 10 || rx + rw <= x) return; 561 if (rw < 10 || rx + rw <= x) return;
564 } 562 }
565 563
566 MutexLocker locker(&m_cacheMutex, "MatrixFile::primeCache::m_cacheMutex"); 564 MutexLocker locker(&m_cacheMutex, "MatrixFile::primeCache::m_cacheMutex");
567 565
568 FileReadThread::Request request; 566 // Check for the existence of the request first; if it exists,
567 // check whether it's ready. Only when we know it's ready do we
568 // retrieve the actual request, because the reason we need the
569 // request is to check whether it was successful or not and
570 // extract data from it, and none of that can be relied upon if we
571 // retrieve the request before it's ready. (There used to be a
572 // race condition here, where we retrieved the request and only
573 // afterwards checked the ready status, pulling data from the
574 // request if it was found to be ready then.)
569 575
570 if (m_requestToken >= 0 && 576 if (m_requestToken >= 0 &&
571 m_readThread->getRequest(m_requestToken, request)) { 577 m_readThread->haveRequest(m_requestToken)) {
572 578
573 if (x >= m_requestingX && 579 if (x >= m_requestingX &&
574 x < m_requestingX + m_requestingWidth) { 580 x < m_requestingX + m_requestingWidth) {
575 581
576 if (m_readThread->isReady(m_requestToken)) { 582 if (m_readThread->isReady(m_requestToken)) {
583
584 FileReadThread::Request request;
585 if (!m_readThread->getRequest(m_requestToken, request)) {
586 std::cerr << "ERROR: MatrixFile::primeCache: File read thread has lost our request!" << std::endl;
587 throw FileReadFailed(m_fileName);
588 }
577 589
578 if (!request.successful) { 590 if (!request.successful) {
579 std::cerr << "ERROR: MatrixFile::primeCache: Last request was unsuccessful" << std::endl; 591 std::cerr << "ERROR: MatrixFile::primeCache: Last request was unsuccessful" << std::endl;
580 throw FileReadFailed(m_fileName); 592 throw FileReadFailed(m_fileName);
581 } 593 }
608 620
609 // already requested something covering this area; wait for it 621 // already requested something covering this area; wait for it
610 return; 622 return;
611 } 623 }
612 624
613 // the current request is no longer of any use 625 FileReadThread::Request dud;
614 m_readThread->cancel(m_requestToken); 626
615 627 if (!m_readThread->getRequest(m_requestToken, dud)) {
616 // crude way to avoid leaking the data 628
617 while (!m_readThread->isCancelled(m_requestToken)) { 629 std::cerr << "ERROR: MatrixFile::primeCache: Inconsistent replies from FileReadThread" << std::endl;
618 usleep(10000); 630
619 } 631 } else {
632
633 // current request is for the wrong area, so no longer of any use
634 m_readThread->cancel(m_requestToken);
635
636 // crude way to avoid leaking the data
637 while (!m_readThread->isCancelled(m_requestToken)) {
638 usleep(10000);
639 }
620 640
621 #ifdef DEBUG_MATRIX_FILE_READ_SET 641 #ifdef DEBUG_MATRIX_FILE_READ_SET
622 std::cerr << "cancelled " << m_requestToken << std::endl; 642 std::cerr << "cancelled " << m_requestToken << std::endl;
623 #endif 643 #endif
624 644
625 if (m_spareData) { 645 if (m_spareData) {
626 // std::cerr << this << ": Freeing spare data" << std::endl; 646 free(m_spareData);
627 free(m_spareData); 647 }
628 } 648 m_spareData = dud.data;
629 // std::cerr << this << ": Moving request data to spare" << std::endl; 649 m_readThread->done(m_requestToken);
630 m_spareData = request.data; 650 }
631 m_readThread->done(m_requestToken);
632 651
633 m_requestToken = -1; 652 m_requestToken = -1;
634 } 653 }
635 654
636 if (m_fd < 0) { 655 if (m_fd < 0) {
637 MutexLocker locker(&m_fdMutex, "MatrixFile::primeCache::m_fdMutex"); 656 MutexLocker locker(&m_fdMutex, "MatrixFile::primeCache::m_fdMutex");
638 if (m_fd < 0) resume(); 657 if (m_fd < 0) resume();
639 } 658 }
659
660 FileReadThread::Request request;
640 661
641 request.fd = m_fd; 662 request.fd = m_fd;
642 request.mutex = &m_fdMutex; 663 request.mutex = &m_fdMutex;
643 request.start = m_headerSize + rx * m_height * m_cellSize; 664 request.start = m_headerSize + rx * m_height * m_cellSize;
644 request.size = rw * m_height * m_cellSize; 665 request.size = rw * m_height * m_cellSize;