#ifndef _WIN32 # include #else # include # define sleep Sleep #endif #include #include #include #include #include #include #include #include #include class SillyThread : public QThread { Q_OBJECT public: explicit SillyThread(Poppler::Document *document, QObject *parent = nullptr); void run() override; private: Poppler::Document *m_document; std::vector> m_pages; }; class CrazyThread : public QThread { Q_OBJECT public: CrazyThread(Poppler::Document *document, QMutex *annotationMutex, QObject *parent = nullptr); void run() override; private: Poppler::Document *m_document; QMutex *m_annotationMutex; }; static std::unique_ptr loadPage(Poppler::Document *document, int index) { std::unique_ptr page = document->page(index); if (page == nullptr) { qDebug() << "!Document::page"; exit(EXIT_FAILURE); } return page; } static std::unique_ptr loadRandomPage(Poppler::Document *document) { return loadPage(document, QRandomGenerator::global()->bounded(document->numPages())); } SillyThread::SillyThread(Poppler::Document *document, QObject *parent) : QThread(parent), m_document(document), m_pages() { m_pages.reserve(m_document->numPages()); for (int index = 0; index < m_document->numPages(); ++index) { m_pages.push_back(loadPage(m_document, index)); } } void SillyThread::run() { forever { for (std::unique_ptr &page : m_pages) { QImage image = page->renderToImage(); if (image.isNull()) { qDebug() << "!Page::renderToImage"; ::exit(EXIT_FAILURE); } } } } CrazyThread::CrazyThread(Poppler::Document *document, QMutex *annotationMutex, QObject *parent) : QThread(parent), m_document(document), m_annotationMutex(annotationMutex) { } void CrazyThread::run() { typedef std::unique_ptr PagePointer; forever { if (QRandomGenerator::global()->bounded(2) == 0) { qDebug() << "search..."; PagePointer page(loadRandomPage(m_document)); page->search(QStringLiteral("c"), Poppler::Page::IgnoreCase); page->search(QStringLiteral("r")); page->search(QStringLiteral("a"), Poppler::Page::IgnoreCase); page->search(QStringLiteral("z")); page->search(QStringLiteral("y"), Poppler::Page::IgnoreCase); } if (QRandomGenerator::global()->bounded(2) == 0) { qDebug() << "links..."; PagePointer page(loadRandomPage(m_document)); std::vector> links = page->links(); } if (QRandomGenerator::global()->bounded(2) == 0) { qDebug() << "form fields..."; PagePointer page(loadRandomPage(m_document)); std::vector> formFields = page->formFields(); } if (QRandomGenerator::global()->bounded(2) == 0) { qDebug() << "thumbnail..."; PagePointer page(loadRandomPage(m_document)); page->thumbnail(); } if (QRandomGenerator::global()->bounded(2) == 0) { qDebug() << "text..."; PagePointer page(loadRandomPage(m_document)); page->text(QRectF(QPointF(), page->pageSizeF())); } if (QRandomGenerator::global()->bounded(2) == 0) { QMutexLocker mutexLocker(m_annotationMutex); qDebug() << "add annotation..."; PagePointer page(loadRandomPage(m_document)); Poppler::Annotation *annotation = nullptr; switch (QRandomGenerator::global()->bounded(3)) { default: case 0: annotation = new Poppler::TextAnnotation(QRandomGenerator::global()->bounded(2) == 0 ? Poppler::TextAnnotation::Linked : Poppler::TextAnnotation::InPlace); break; case 1: annotation = new Poppler::HighlightAnnotation(); break; case 2: annotation = new Poppler::InkAnnotation(); break; } annotation->setBoundary(QRectF(0.0, 0.0, 0.5, 0.5)); annotation->setContents(QStringLiteral("crazy")); page->addAnnotation(annotation); delete annotation; } if (QRandomGenerator::global()->bounded(2) == 0) { QMutexLocker mutexLocker(m_annotationMutex); for (int index = 0; index < m_document->numPages(); ++index) { PagePointer page(loadPage(m_document, index)); std::vector> annotations = page->annotations(); if (!annotations.empty()) { qDebug() << "modify annotation..."; // size is now a qsizetype which confuses bounded(), pretend we will never have that many annotations anyway const quint32 annotationsSize = annotations.size(); annotations.at(QRandomGenerator::global()->bounded(annotationsSize))->setBoundary(QRectF(0.5, 0.5, 0.25, 0.25)); annotations.at(QRandomGenerator::global()->bounded(annotationsSize))->setAuthor(QStringLiteral("foo")); annotations.at(QRandomGenerator::global()->bounded(annotationsSize))->setContents(QStringLiteral("bar")); annotations.at(QRandomGenerator::global()->bounded(annotationsSize))->setCreationDate(QDateTime::currentDateTime()); annotations.at(QRandomGenerator::global()->bounded(annotationsSize))->setModificationDate(QDateTime::currentDateTime()); } if (!annotations.empty()) { break; } } } if (QRandomGenerator::global()->bounded(2) == 0) { QMutexLocker mutexLocker(m_annotationMutex); for (int index = 0; index < m_document->numPages(); ++index) { PagePointer page(loadPage(m_document, index)); std::vector> annotations = page->annotations(); if (!annotations.empty()) { qDebug() << "remove annotation..."; // size is now a qsizetype which confuses bounded(), pretend we will never have that many annotations anyway const quint32 annotationsSize = annotations.size(); page->removeAnnotation(annotations[QRandomGenerator::global()->bounded(annotationsSize)].get()); annotations.erase(annotations.begin() + QRandomGenerator::global()->bounded(annotationsSize)); } if (!annotations.empty()) { break; } } } if (QRandomGenerator::global()->bounded(2) == 0) { qDebug() << "fonts..."; m_document->fonts(); } } } int main(int argc, char **argv) { if (argc < 5) { qDebug() << "usage: stress-threads-qt duration sillyCount crazyCount file(s)"; return EXIT_FAILURE; } const int duration = atoi(argv[1]); const int sillyCount = atoi(argv[2]); const int crazyCount = atoi(argv[3]); for (int argi = 4; argi < argc; ++argi) { const QString file = QFile::decodeName(argv[argi]); std::unique_ptr document = Poppler::Document::load(file); if (document == nullptr) { qDebug() << "Could not load" << file; continue; } if (document->isLocked()) { qDebug() << file << "is locked"; continue; } for (int i = 0; i < sillyCount; ++i) { (new SillyThread(document.get()))->start(); } QMutex *annotationMutex = new QMutex(); for (int i = 0; i < crazyCount; ++i) { (new CrazyThread(document.get(), annotationMutex))->start(); } } sleep(duration); return EXIT_SUCCESS; } #include "stress-threads-qt6.moc"