1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42
43 #include <QtTest/QtTest>
44
45
46 #include <qcoreapplication.h>
47 #include <qthread.h>
48 #include <qsemaphore.h>
49
50 //TESTED_CLASS=
51 //TESTED_FILES=
52
53 class tst_QSemaphore : public QObject
54 {
55 Q_OBJECT
56
57 public:
58 tst_QSemaphore();
59 ~tst_QSemaphore();
60
61 private slots:
62 void acquire();
63 void tryAcquire();
64 void tryAcquireWithTimeout_data();
65 void tryAcquireWithTimeout();
66 void tryAcquireWithTimeoutStarvation();
67 void release();
68 void available();
69 void producerConsumer();
70 };
71
72 static QSemaphore *semaphore = 0;
73
tst_QSemaphore()74 tst_QSemaphore::tst_QSemaphore()
75 { }
76
~tst_QSemaphore()77 tst_QSemaphore::~tst_QSemaphore()
78 { }
79
80 class ThreadOne : public QThread
81 {
82 public:
ThreadOne()83 ThreadOne() {}
84
85 protected:
run()86 void run()
87 {
88 int i = 0;
89 while ( i < 100 ) {
90 semaphore->acquire();
91 i++;
92 semaphore->release();
93 }
94 }
95 };
96
97 class ThreadN : public QThread
98 {
99 int N;
100
101 public:
ThreadN(int n)102 ThreadN(int n) :N(n) { }
103
104 protected:
run()105 void run()
106 {
107 int i = 0;
108 while ( i < 100 ) {
109 semaphore->acquire(N);
110 i++;
111 semaphore->release(N);
112 }
113 }
114 };
115
acquire()116 void tst_QSemaphore::acquire()
117 {
118 {
119 // old incrementOne() test
120 QVERIFY(!semaphore);
121 semaphore = new QSemaphore;
122 // make some "thing" available
123 semaphore->release();
124
125 ThreadOne t1;
126 ThreadOne t2;
127
128 t1.start();
129 t2.start();
130
131 QVERIFY(t1.wait(4000));
132 QVERIFY(t2.wait(4000));
133
134 delete semaphore;
135 semaphore = 0;
136 }
137
138 // old incrementN() test
139 {
140 QVERIFY(!semaphore);
141 semaphore = new QSemaphore;
142 // make 4 "things" available
143 semaphore->release(4);
144
145 ThreadN t1(2);
146 ThreadN t2(3);
147
148 t1.start();
149 t2.start();
150
151 QVERIFY(t1.wait(4000));
152 QVERIFY(t2.wait(4000));
153
154 delete semaphore;
155 semaphore = 0;
156 }
157
158 QSemaphore semaphore;
159
160 QCOMPARE(semaphore.available(), 0);
161 semaphore.release();
162 QCOMPARE(semaphore.available(), 1);
163 semaphore.release();
164 QCOMPARE(semaphore.available(), 2);
165 semaphore.release(10);
166 QCOMPARE(semaphore.available(), 12);
167 semaphore.release(10);
168 QCOMPARE(semaphore.available(), 22);
169
170 semaphore.acquire();
171 QCOMPARE(semaphore.available(), 21);
172 semaphore.acquire();
173 QCOMPARE(semaphore.available(), 20);
174 semaphore.acquire(10);
175 QCOMPARE(semaphore.available(), 10);
176 semaphore.acquire(10);
177 QCOMPARE(semaphore.available(), 0);
178 }
179
tryAcquire()180 void tst_QSemaphore::tryAcquire()
181 {
182 QSemaphore semaphore;
183
184 QCOMPARE(semaphore.available(), 0);
185
186 semaphore.release();
187 QCOMPARE(semaphore.available(), 1);
188 QVERIFY(!semaphore.tryAcquire(2));
189 QCOMPARE(semaphore.available(), 1);
190
191 semaphore.release();
192 QCOMPARE(semaphore.available(), 2);
193 QVERIFY(!semaphore.tryAcquire(3));
194 QCOMPARE(semaphore.available(), 2);
195
196 semaphore.release(10);
197 QCOMPARE(semaphore.available(), 12);
198 QVERIFY(!semaphore.tryAcquire(100));
199 QCOMPARE(semaphore.available(), 12);
200
201 semaphore.release(10);
202 QCOMPARE(semaphore.available(), 22);
203 QVERIFY(!semaphore.tryAcquire(100));
204 QCOMPARE(semaphore.available(), 22);
205
206 QVERIFY(semaphore.tryAcquire());
207 QCOMPARE(semaphore.available(), 21);
208
209 QVERIFY(semaphore.tryAcquire());
210 QCOMPARE(semaphore.available(), 20);
211
212 QVERIFY(semaphore.tryAcquire(10));
213 QCOMPARE(semaphore.available(), 10);
214
215 QVERIFY(semaphore.tryAcquire(10));
216 QCOMPARE(semaphore.available(), 0);
217
218 // should not be able to acquire more
219 QVERIFY(!semaphore.tryAcquire());
220 QCOMPARE(semaphore.available(), 0);
221
222 QVERIFY(!semaphore.tryAcquire());
223 QCOMPARE(semaphore.available(), 0);
224
225 QVERIFY(!semaphore.tryAcquire(10));
226 QCOMPARE(semaphore.available(), 0);
227
228 QVERIFY(!semaphore.tryAcquire(10));
229 QCOMPARE(semaphore.available(), 0);
230 }
231
tryAcquireWithTimeout_data()232 void tst_QSemaphore::tryAcquireWithTimeout_data()
233 {
234 QTest::addColumn<int>("timeout");
235
236 QTest::newRow("1s") << 1000;
237 QTest::newRow("10s") << 10000;
238 }
239
tryAcquireWithTimeout()240 void tst_QSemaphore::tryAcquireWithTimeout()
241 {
242 QFETCH(int, timeout);
243
244 // timers are not guaranteed to be accurate down to the last millisecond,
245 // so we permit the elapsed times to be up to this far from the expected value.
246 int fuzz = 10;
247
248 QSemaphore semaphore;
249 QTime time;
250
251 #define FUZZYCOMPARE(a,e) \
252 do { \
253 int a1 = a; \
254 int e1 = e; \
255 QVERIFY2(qAbs(a1-e1) < fuzz, \
256 qPrintable(QString("(%1=%2) is more than %3 milliseconds different from (%4=%5)") \
257 .arg(#a).arg(a1).arg(fuzz).arg(#e).arg(e1))); \
258 } while (0)
259
260 QCOMPARE(semaphore.available(), 0);
261
262 semaphore.release();
263 QCOMPARE(semaphore.available(), 1);
264 time.start();
265 QVERIFY(!semaphore.tryAcquire(2, timeout));
266 FUZZYCOMPARE(time.elapsed(), timeout);
267 QCOMPARE(semaphore.available(), 1);
268
269 semaphore.release();
270 QCOMPARE(semaphore.available(), 2);
271 time.start();
272 QVERIFY(!semaphore.tryAcquire(3, timeout));
273 FUZZYCOMPARE(time.elapsed(), timeout);
274 QCOMPARE(semaphore.available(), 2);
275
276 semaphore.release(10);
277 QCOMPARE(semaphore.available(), 12);
278 time.start();
279 QVERIFY(!semaphore.tryAcquire(100, timeout));
280 FUZZYCOMPARE(time.elapsed(), timeout);
281 QCOMPARE(semaphore.available(), 12);
282
283 semaphore.release(10);
284 QCOMPARE(semaphore.available(), 22);
285 time.start();
286 QVERIFY(!semaphore.tryAcquire(100, timeout));
287 FUZZYCOMPARE(time.elapsed(), timeout);
288 QCOMPARE(semaphore.available(), 22);
289
290 time.start();
291 QVERIFY(semaphore.tryAcquire(1, timeout));
292 FUZZYCOMPARE(time.elapsed(), 0);
293 QCOMPARE(semaphore.available(), 21);
294
295 time.start();
296 QVERIFY(semaphore.tryAcquire(1, timeout));
297 FUZZYCOMPARE(time.elapsed(), 0);
298 QCOMPARE(semaphore.available(), 20);
299
300 time.start();
301 QVERIFY(semaphore.tryAcquire(10, timeout));
302 FUZZYCOMPARE(time.elapsed(), 0);
303 QCOMPARE(semaphore.available(), 10);
304
305 time.start();
306 QVERIFY(semaphore.tryAcquire(10, timeout));
307 FUZZYCOMPARE(time.elapsed(), 0);
308 QCOMPARE(semaphore.available(), 0);
309
310 // should not be able to acquire more
311 time.start();
312 QVERIFY(!semaphore.tryAcquire(1, timeout));
313 FUZZYCOMPARE(time.elapsed(), timeout);
314 QCOMPARE(semaphore.available(), 0);
315
316 time.start();
317 QVERIFY(!semaphore.tryAcquire(1, timeout));
318 FUZZYCOMPARE(time.elapsed(), timeout);
319 QCOMPARE(semaphore.available(), 0);
320
321 time.start();
322 QVERIFY(!semaphore.tryAcquire(10, timeout));
323 FUZZYCOMPARE(time.elapsed(), timeout);
324 QCOMPARE(semaphore.available(), 0);
325
326 time.start();
327 QVERIFY(!semaphore.tryAcquire(10, timeout));
328 FUZZYCOMPARE(time.elapsed(), timeout);
329 QCOMPARE(semaphore.available(), 0);
330
331 #undef FUZZYCOMPARE
332 }
333
tryAcquireWithTimeoutStarvation()334 void tst_QSemaphore::tryAcquireWithTimeoutStarvation()
335 {
336 class Thread : public QThread
337 {
338 public:
339 QSemaphore startup;
340 QSemaphore *semaphore;
341 int amountToConsume, timeout;
342
343 void run()
344 {
345 startup.release();
346 forever {
347 if (!semaphore->tryAcquire(amountToConsume, timeout))
348 break;
349 semaphore->release(amountToConsume);
350 }
351 }
352 };
353
354 QSemaphore semaphore;
355 semaphore.release(1);
356
357 Thread consumer;
358 consumer.semaphore = &semaphore;
359 consumer.amountToConsume = 1;
360 consumer.timeout = 1000;
361
362 // start the thread and wait for it to start consuming
363 consumer.start();
364 consumer.startup.acquire();
365
366 // try to consume more than the thread we started is, and provide a longer
367 // timeout... we should timeout, not wait indefinitely
368 QVERIFY(!semaphore.tryAcquire(consumer.amountToConsume * 2, consumer.timeout * 2));
369
370 // the consumer should still be running
371 QVERIFY(consumer.isRunning() && !consumer.isFinished());
372
373 // acquire, and wait for smallConsumer to timeout
374 semaphore.acquire();
375 QVERIFY(consumer.wait());
376 }
377
release()378 void tst_QSemaphore::release()
379 { DEPENDS_ON("acquire"); }
380
available()381 void tst_QSemaphore::available()
382 { DEPENDS_ON("acquire"); }
383
384 const char alphabet[] = "ACGTH";
385 const int AlphabetSize = sizeof(alphabet) - 1;
386
387 const int BufferSize = 4096; // GCD of BufferSize and alphabet size must be 1
388 char buffer[BufferSize];
389
390 #ifndef Q_OS_WINCE
391 const int ProducerChunkSize = 3;
392 const int ConsumerChunkSize = 7;
393 const int Multiplier = 10;
394 #else
395 const int ProducerChunkSize = 2;
396 const int ConsumerChunkSize = 5;
397 const int Multiplier = 3;
398 #endif
399
400 // note: the code depends on the fact that DataSize is a multiple of
401 // ProducerChunkSize, ConsumerChunkSize, and BufferSize
402 const int DataSize = ProducerChunkSize * ConsumerChunkSize * BufferSize * Multiplier;
403
404 QSemaphore freeSpace(BufferSize);
405 QSemaphore usedSpace;
406
407 class Producer : public QThread
408 {
409 public:
410 void run();
411 };
412
run()413 void Producer::run()
414 {
415 for (int i = 0; i < DataSize; ++i) {
416 freeSpace.acquire();
417 buffer[i % BufferSize] = alphabet[i % AlphabetSize];
418 usedSpace.release();
419 }
420 for (int i = 0; i < DataSize; ++i) {
421 if ((i % ProducerChunkSize) == 0)
422 freeSpace.acquire(ProducerChunkSize);
423 buffer[i % BufferSize] = alphabet[i % AlphabetSize];
424 if ((i % ProducerChunkSize) == (ProducerChunkSize - 1))
425 usedSpace.release(ProducerChunkSize);
426 }
427 }
428
429 class Consumer : public QThread
430 {
431 public:
432 void run();
433 };
434
run()435 void Consumer::run()
436 {
437 for (int i = 0; i < DataSize; ++i) {
438 usedSpace.acquire();
439 QCOMPARE(buffer[i % BufferSize], alphabet[i % AlphabetSize]);
440 freeSpace.release();
441 }
442 for (int i = 0; i < DataSize; ++i) {
443 if ((i % ConsumerChunkSize) == 0)
444 usedSpace.acquire(ConsumerChunkSize);
445 QCOMPARE(buffer[i % BufferSize], alphabet[i % AlphabetSize]);
446 if ((i % ConsumerChunkSize) == (ConsumerChunkSize - 1))
447 freeSpace.release(ConsumerChunkSize);
448 }
449 }
450
producerConsumer()451 void tst_QSemaphore::producerConsumer()
452 {
453 Producer producer;
454 Consumer consumer;
455 producer.start();
456 consumer.start();
457 producer.wait();
458 consumer.wait();
459 }
460
461 QTEST_MAIN(tst_QSemaphore)
462 #include "tst_qsemaphore.moc"
463