Mercurial > hg > svcore
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; |