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-qt5.h>
11 #include <poppler-form.h>
12 
13 #include <QtCore/QDebug>
14 #include <QtCore/QFile>
15 #include <QtCore/QMutex>
16 #include <QtCore/QThread>
17 #include <QtGui/QImage>
18 
19 class SillyThread : public QThread
20 {
21     Q_OBJECT
22 public:
23     explicit SillyThread(Poppler::Document *document, QObject *parent = nullptr);
24 
25     void run() override;
26 
27 private:
28     Poppler::Document *m_document;
29     QVector<Poppler::Page *> m_pages;
30 };
31 
32 class CrazyThread : public QThread
33 {
34     Q_OBJECT
35 public:
36     CrazyThread(uint seed, Poppler::Document *document, QMutex *annotationMutex, QObject *parent = nullptr);
37 
38     void run() override;
39 
40 private:
41     uint m_seed;
42     Poppler::Document *m_document;
43     QMutex *m_annotationMutex;
44 };
45 
loadPage(Poppler::Document * document,int index)46 static Poppler::Page *loadPage(Poppler::Document *document, int index)
47 {
48     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 
loadRandomPage(Poppler::Document * document)59 static Poppler::Page *loadRandomPage(Poppler::Document *document)
60 {
61     return loadPage(document, qrand() % document->numPages());
62 }
63 
SillyThread(Poppler::Document * document,QObject * parent)64 SillyThread::SillyThread(Poppler::Document *document, QObject *parent) : QThread(parent), m_document(document), m_pages()
65 {
66     m_pages.reserve(m_document->numPages());
67 
68     for (int index = 0; index < m_document->numPages(); ++index) {
69         m_pages.append(loadPage(m_document, index));
70     }
71 }
72 
run()73 void SillyThread::run()
74 {
75     forever {
76         foreach (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 
CrazyThread(uint seed,Poppler::Document * document,QMutex * annotationMutex,QObject * parent)88 CrazyThread::CrazyThread(uint seed, Poppler::Document *document, QMutex *annotationMutex, QObject *parent) : QThread(parent), m_seed(seed), m_document(document), m_annotationMutex(annotationMutex) { }
89 
run()90 void CrazyThread::run()
91 {
92     typedef QScopedPointer<Poppler::Page> PagePointer;
93 
94     qsrand(m_seed);
95 
96     forever {
97         if (qrand() % 2 == 0) {
98             qDebug() << "search...";
99 
100             PagePointer page(loadRandomPage(m_document));
101 
102             page->search(QStringLiteral("c"), Poppler::Page::IgnoreCase);
103             page->search(QStringLiteral("r"));
104             page->search(QStringLiteral("a"), Poppler::Page::IgnoreCase);
105             page->search(QStringLiteral("z"));
106             page->search(QStringLiteral("y"), Poppler::Page::IgnoreCase);
107         }
108 
109         if (qrand() % 2 == 0) {
110             qDebug() << "links...";
111 
112             PagePointer page(loadRandomPage(m_document));
113 
114             QList<Poppler::Link *> links = page->links();
115 
116             qDeleteAll(links);
117         }
118 
119         if (qrand() % 2 == 0) {
120             qDebug() << "form fields...";
121 
122             PagePointer page(loadRandomPage(m_document));
123 
124             QList<Poppler::FormField *> formFields = page->formFields();
125 
126             qDeleteAll(formFields);
127         }
128 
129         if (qrand() % 2 == 0) {
130             qDebug() << "thumbnail...";
131 
132             PagePointer page(loadRandomPage(m_document));
133 
134             page->thumbnail();
135         }
136 
137         if (qrand() % 2 == 0) {
138             qDebug() << "text...";
139 
140             PagePointer page(loadRandomPage(m_document));
141 
142             page->text(QRectF(QPointF(), page->pageSizeF()));
143         }
144 
145         if (qrand() % 2 == 0) {
146             QMutexLocker mutexLocker(m_annotationMutex);
147 
148             qDebug() << "add annotation...";
149 
150             PagePointer page(loadRandomPage(m_document));
151 
152             Poppler::Annotation *annotation = nullptr;
153 
154             switch (qrand() % 3) {
155             default:
156             case 0:
157                 annotation = new Poppler::TextAnnotation(qrand() % 2 == 0 ? Poppler::TextAnnotation::Linked : Poppler::TextAnnotation::InPlace);
158                 break;
159             case 1:
160                 annotation = new Poppler::HighlightAnnotation();
161                 break;
162             case 2:
163                 annotation = new Poppler::InkAnnotation();
164                 break;
165             }
166 
167             annotation->setBoundary(QRectF(0.0, 0.0, 0.5, 0.5));
168             annotation->setContents(QStringLiteral("crazy"));
169 
170             page->addAnnotation(annotation);
171 
172             delete annotation;
173         }
174 
175         if (qrand() % 2 == 0) {
176             QMutexLocker mutexLocker(m_annotationMutex);
177 
178             for (int index = 0; index < m_document->numPages(); ++index) {
179                 PagePointer page(loadPage(m_document, index));
180 
181                 QList<Poppler::Annotation *> annotations = page->annotations();
182 
183                 if (!annotations.isEmpty()) {
184                     qDebug() << "modify annotation...";
185 
186                     annotations.at(qrand() % annotations.size())->setBoundary(QRectF(0.5, 0.5, 0.25, 0.25));
187                     annotations.at(qrand() % annotations.size())->setAuthor(QStringLiteral("foo"));
188                     annotations.at(qrand() % annotations.size())->setContents(QStringLiteral("bar"));
189                     annotations.at(qrand() % annotations.size())->setCreationDate(QDateTime::currentDateTime());
190                     annotations.at(qrand() % annotations.size())->setModificationDate(QDateTime::currentDateTime());
191                 }
192 
193                 qDeleteAll(annotations);
194 
195                 if (!annotations.isEmpty()) {
196                     break;
197                 }
198             }
199         }
200 
201         if (qrand() % 2 == 0) {
202             QMutexLocker mutexLocker(m_annotationMutex);
203 
204             for (int index = 0; index < m_document->numPages(); ++index) {
205                 PagePointer page(loadPage(m_document, index));
206 
207                 QList<Poppler::Annotation *> annotations = page->annotations();
208 
209                 if (!annotations.isEmpty()) {
210                     qDebug() << "remove annotation...";
211 
212                     page->removeAnnotation(annotations.takeAt(qrand() % annotations.size()));
213                 }
214 
215                 qDeleteAll(annotations);
216 
217                 if (!annotations.isEmpty()) {
218                     break;
219                 }
220             }
221         }
222 
223         if (qrand() % 2 == 0) {
224             qDebug() << "fonts...";
225 
226             m_document->fonts();
227         }
228     }
229 }
230 
main(int argc,char ** argv)231 int main(int argc, char **argv)
232 {
233     if (argc < 5) {
234         qDebug() << "usage: stress-threads-qt duration sillyCount crazyCount file(s)";
235 
236         return EXIT_FAILURE;
237     }
238 
239     const int duration = atoi(argv[1]);
240     const int sillyCount = atoi(argv[2]);
241     const int crazyCount = atoi(argv[3]);
242 
243     qsrand(time(nullptr));
244 
245     for (int argi = 4; argi < argc; ++argi) {
246         const QString file = QFile::decodeName(argv[argi]);
247         Poppler::Document *document = Poppler::Document::load(file);
248 
249         if (document == nullptr) {
250             qDebug() << "Could not load" << file;
251             continue;
252         }
253 
254         if (document->isLocked()) {
255             qDebug() << file << "is locked";
256             continue;
257         }
258 
259         for (int i = 0; i < sillyCount; ++i) {
260             (new SillyThread(document))->start();
261         }
262 
263         QMutex *annotationMutex = new QMutex();
264 
265         for (int i = 0; i < crazyCount; ++i) {
266             (new CrazyThread(qrand(), document, annotationMutex))->start();
267         }
268     }
269 
270     sleep(duration);
271 
272     return EXIT_SUCCESS;
273 }
274 
275 #include "stress-threads-qt5.moc"
276