1 | |
2 | #ifndef _WIN32 |
3 | # include <unistd.h> |
4 | #else |
5 | # include <windows.h> |
6 | # define sleep Sleep |
7 | #endif |
8 | #include <ctime> |
9 | |
10 | #include <poppler-qt6.h> |
11 | #include <poppler-form.h> |
12 | |
13 | #include <QDebug> |
14 | #include <QFile> |
15 | #include <QImage> |
16 | #include <QMutex> |
17 | #include <QRandomGenerator> |
18 | #include <QThread> |
19 | |
20 | class SillyThread : public QThread |
21 | { |
22 | Q_OBJECT |
23 | public: |
24 | explicit SillyThread(Poppler::Document *document, QObject *parent = nullptr); |
25 | |
26 | void run() override; |
27 | |
28 | private: |
29 | Poppler::Document *m_document; |
30 | std::vector<std::unique_ptr<Poppler::Page>> m_pages; |
31 | }; |
32 | |
33 | class CrazyThread : public QThread |
34 | { |
35 | Q_OBJECT |
36 | public: |
37 | CrazyThread(Poppler::Document *document, QMutex *annotationMutex, QObject *parent = nullptr); |
38 | |
39 | void run() override; |
40 | |
41 | private: |
42 | Poppler::Document *m_document; |
43 | QMutex *m_annotationMutex; |
44 | }; |
45 | |
46 | static std::unique_ptr<Poppler::Page> loadPage(Poppler::Document *document, int index) |
47 | { |
48 | std::unique_ptr<Poppler::Page> page = document->page(index); |
49 | |
50 | if (page == nullptr) { |
51 | qDebug() << "!Document::page" ; |
52 | |
53 | exit(EXIT_FAILURE); |
54 | } |
55 | |
56 | return page; |
57 | } |
58 | |
59 | static std::unique_ptr<Poppler::Page> loadRandomPage(Poppler::Document *document) |
60 | { |
61 | return loadPage(document, index: QRandomGenerator::global()->bounded(highest: document->numPages())); |
62 | } |
63 | |
64 | SillyThread::SillyThread(Poppler::Document *document, QObject *parent) : QThread(parent), m_document(document), m_pages() |
65 | { |
66 | m_pages.reserve(n: m_document->numPages()); |
67 | |
68 | for (int index = 0; index < m_document->numPages(); ++index) { |
69 | m_pages.push_back(x: loadPage(document: m_document, index)); |
70 | } |
71 | } |
72 | |
73 | void SillyThread::run() |
74 | { |
75 | forever { |
76 | for (std::unique_ptr<Poppler::Page> &page : m_pages) { |
77 | QImage image = page->renderToImage(); |
78 | |
79 | if (image.isNull()) { |
80 | qDebug() << "!Page::renderToImage" ; |
81 | |
82 | ::exit(EXIT_FAILURE); |
83 | } |
84 | } |
85 | } |
86 | } |
87 | |
88 | CrazyThread::CrazyThread(Poppler::Document *document, QMutex *annotationMutex, QObject *parent) : QThread(parent), m_document(document), m_annotationMutex(annotationMutex) { } |
89 | |
90 | void CrazyThread::run() |
91 | { |
92 | typedef std::unique_ptr<Poppler::Page> PagePointer; |
93 | |
94 | forever { |
95 | if (QRandomGenerator::global()->bounded(highest: 2) == 0) { |
96 | qDebug() << "search..." ; |
97 | |
98 | PagePointer page(loadRandomPage(document: m_document)); |
99 | |
100 | page->search(QStringLiteral("c" ), flags: Poppler::Page::IgnoreCase); |
101 | page->search(QStringLiteral("r" )); |
102 | page->search(QStringLiteral("a" ), flags: Poppler::Page::IgnoreCase); |
103 | page->search(QStringLiteral("z" )); |
104 | page->search(QStringLiteral("y" ), flags: Poppler::Page::IgnoreCase); |
105 | } |
106 | |
107 | if (QRandomGenerator::global()->bounded(highest: 2) == 0) { |
108 | qDebug() << "links..." ; |
109 | |
110 | PagePointer page(loadRandomPage(document: m_document)); |
111 | |
112 | std::vector<std::unique_ptr<Poppler::Link>> links = page->links(); |
113 | } |
114 | |
115 | if (QRandomGenerator::global()->bounded(highest: 2) == 0) { |
116 | qDebug() << "form fields..." ; |
117 | |
118 | PagePointer page(loadRandomPage(document: m_document)); |
119 | |
120 | std::vector<std::unique_ptr<Poppler::FormField>> formFields = page->formFields(); |
121 | } |
122 | |
123 | if (QRandomGenerator::global()->bounded(highest: 2) == 0) { |
124 | qDebug() << "thumbnail..." ; |
125 | |
126 | PagePointer page(loadRandomPage(document: m_document)); |
127 | |
128 | page->thumbnail(); |
129 | } |
130 | |
131 | if (QRandomGenerator::global()->bounded(highest: 2) == 0) { |
132 | qDebug() << "text..." ; |
133 | |
134 | PagePointer page(loadRandomPage(document: m_document)); |
135 | |
136 | page->text(rect: QRectF(QPointF(), page->pageSizeF())); |
137 | } |
138 | |
139 | if (QRandomGenerator::global()->bounded(highest: 2) == 0) { |
140 | QMutexLocker mutexLocker(m_annotationMutex); |
141 | |
142 | qDebug() << "add annotation..." ; |
143 | |
144 | PagePointer page(loadRandomPage(document: m_document)); |
145 | |
146 | Poppler::Annotation *annotation = nullptr; |
147 | |
148 | switch (QRandomGenerator::global()->bounded(highest: 3)) { |
149 | default: |
150 | case 0: |
151 | annotation = new Poppler::TextAnnotation(QRandomGenerator::global()->bounded(highest: 2) == 0 ? Poppler::TextAnnotation::Linked : Poppler::TextAnnotation::InPlace); |
152 | break; |
153 | case 1: |
154 | annotation = new Poppler::HighlightAnnotation(); |
155 | break; |
156 | case 2: |
157 | annotation = new Poppler::InkAnnotation(); |
158 | break; |
159 | } |
160 | |
161 | annotation->setBoundary(QRectF(0.0, 0.0, 0.5, 0.5)); |
162 | annotation->setContents(QStringLiteral("crazy" )); |
163 | |
164 | page->addAnnotation(ann: annotation); |
165 | |
166 | delete annotation; |
167 | } |
168 | |
169 | if (QRandomGenerator::global()->bounded(highest: 2) == 0) { |
170 | QMutexLocker mutexLocker(m_annotationMutex); |
171 | |
172 | for (int index = 0; index < m_document->numPages(); ++index) { |
173 | PagePointer page(loadPage(document: m_document, index)); |
174 | |
175 | std::vector<std::unique_ptr<Poppler::Annotation>> annotations = page->annotations(); |
176 | |
177 | if (!annotations.empty()) { |
178 | qDebug() << "modify annotation..." ; |
179 | |
180 | // size is now a qsizetype which confuses bounded(), pretend we will never have that many annotations anyway |
181 | const quint32 annotationsSize = annotations.size(); |
182 | annotations.at(n: QRandomGenerator::global()->bounded(highest: annotationsSize))->setBoundary(QRectF(0.5, 0.5, 0.25, 0.25)); |
183 | annotations.at(n: QRandomGenerator::global()->bounded(highest: annotationsSize))->setAuthor(QStringLiteral("foo" )); |
184 | annotations.at(n: QRandomGenerator::global()->bounded(highest: annotationsSize))->setContents(QStringLiteral("bar" )); |
185 | annotations.at(n: QRandomGenerator::global()->bounded(highest: annotationsSize))->setCreationDate(QDateTime::currentDateTime()); |
186 | annotations.at(n: QRandomGenerator::global()->bounded(highest: annotationsSize))->setModificationDate(QDateTime::currentDateTime()); |
187 | } |
188 | |
189 | if (!annotations.empty()) { |
190 | break; |
191 | } |
192 | } |
193 | } |
194 | |
195 | if (QRandomGenerator::global()->bounded(highest: 2) == 0) { |
196 | QMutexLocker mutexLocker(m_annotationMutex); |
197 | |
198 | for (int index = 0; index < m_document->numPages(); ++index) { |
199 | PagePointer page(loadPage(document: m_document, index)); |
200 | |
201 | std::vector<std::unique_ptr<Poppler::Annotation>> annotations = page->annotations(); |
202 | |
203 | if (!annotations.empty()) { |
204 | qDebug() << "remove annotation..." ; |
205 | |
206 | // size is now a qsizetype which confuses bounded(), pretend we will never have that many annotations anyway |
207 | const quint32 annotationsSize = annotations.size(); |
208 | page->removeAnnotation(ann: annotations[QRandomGenerator::global()->bounded(highest: annotationsSize)].get()); |
209 | annotations.erase(position: annotations.begin() + QRandomGenerator::global()->bounded(highest: annotationsSize)); |
210 | } |
211 | |
212 | if (!annotations.empty()) { |
213 | break; |
214 | } |
215 | } |
216 | } |
217 | |
218 | if (QRandomGenerator::global()->bounded(highest: 2) == 0) { |
219 | qDebug() << "fonts..." ; |
220 | |
221 | m_document->fonts(); |
222 | } |
223 | } |
224 | } |
225 | |
226 | int main(int argc, char **argv) |
227 | { |
228 | if (argc < 5) { |
229 | qDebug() << "usage: stress-threads-qt duration sillyCount crazyCount file(s)" ; |
230 | |
231 | return EXIT_FAILURE; |
232 | } |
233 | |
234 | const int duration = atoi(nptr: argv[1]); |
235 | const int sillyCount = atoi(nptr: argv[2]); |
236 | const int crazyCount = atoi(nptr: argv[3]); |
237 | |
238 | for (int argi = 4; argi < argc; ++argi) { |
239 | const QString file = QFile::decodeName(localFileName: argv[argi]); |
240 | std::unique_ptr<Poppler::Document> document = Poppler::Document::load(filePath: file); |
241 | |
242 | if (document == nullptr) { |
243 | qDebug() << "Could not load" << file; |
244 | continue; |
245 | } |
246 | |
247 | if (document->isLocked()) { |
248 | qDebug() << file << "is locked" ; |
249 | continue; |
250 | } |
251 | |
252 | for (int i = 0; i < sillyCount; ++i) { |
253 | (new SillyThread(document.get()))->start(); |
254 | } |
255 | |
256 | QMutex *annotationMutex = new QMutex(); |
257 | |
258 | for (int i = 0; i < crazyCount; ++i) { |
259 | (new CrazyThread(document.get(), annotationMutex))->start(); |
260 | } |
261 | } |
262 | |
263 | sleep(seconds: duration); |
264 | |
265 | return EXIT_SUCCESS; |
266 | } |
267 | |
268 | #include "stress-threads-qt6.moc" |
269 | |