|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the test suite of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 |
|
43 #include <QtCore> |
|
44 #include <QtTest/QtTest> |
|
45 |
|
46 |
|
47 enum { OneMinute = 60 * 1000, |
|
48 TwoMinutes = OneMinute * 2, |
|
49 TenMinutes = OneMinute * 10, |
|
50 TwentyFiveMinutes = OneMinute * 25 }; |
|
51 |
|
52 class tst_QObjectRace: public QObject |
|
53 { |
|
54 Q_OBJECT |
|
55 private slots: |
|
56 void moveToThreadRace(); |
|
57 void destroyRace(); |
|
58 }; |
|
59 |
|
60 class RaceObject : public QObject |
|
61 { |
|
62 Q_OBJECT |
|
63 QList<QThread *> threads; |
|
64 int count; |
|
65 |
|
66 public: |
|
67 RaceObject() |
|
68 : count(0) |
|
69 { } |
|
70 |
|
71 void addThread(QThread *thread) |
|
72 { threads.append(thread); } |
|
73 |
|
74 public slots: |
|
75 void theSlot() |
|
76 { |
|
77 enum { step = 35 }; |
|
78 if ((++count % step) == 0) { |
|
79 QThread *nextThread = threads.at((count / step) % threads.size()); |
|
80 moveToThread(nextThread); |
|
81 } |
|
82 } |
|
83 |
|
84 void destroSlot() { |
|
85 emit theSignal(); |
|
86 } |
|
87 signals: |
|
88 void theSignal(); |
|
89 }; |
|
90 |
|
91 class RaceThread : public QThread |
|
92 { |
|
93 Q_OBJECT |
|
94 RaceObject *object; |
|
95 QTime stopWatch; |
|
96 |
|
97 public: |
|
98 RaceThread() |
|
99 : object(0) |
|
100 { } |
|
101 |
|
102 void setObject(RaceObject *o) |
|
103 { |
|
104 object = o; |
|
105 object->addThread(this); |
|
106 } |
|
107 |
|
108 void start() { |
|
109 stopWatch.start(); |
|
110 QThread::start(); |
|
111 } |
|
112 |
|
113 void run() { |
|
114 QTimer zeroTimer; |
|
115 connect(&zeroTimer, SIGNAL(timeout()), object, SLOT(theSlot())); |
|
116 connect(&zeroTimer, SIGNAL(timeout()), this, SLOT(checkStopWatch()), Qt::DirectConnection); |
|
117 zeroTimer.start(0); |
|
118 (void) exec(); |
|
119 } |
|
120 |
|
121 signals: |
|
122 void theSignal(); |
|
123 |
|
124 private slots: |
|
125 void checkStopWatch() |
|
126 { |
|
127 #if defined(Q_OS_WINCE) || defined(Q_OS_VXWORKS) |
|
128 if (stopWatch.elapsed() >= OneMinute / 2) |
|
129 #else |
|
130 if (stopWatch.elapsed() >= OneMinute) |
|
131 #endif |
|
132 quit(); |
|
133 |
|
134 QObject o; |
|
135 connect(&o, SIGNAL(destroyed()) , object, SLOT(destroSlot())); |
|
136 connect(object, SIGNAL(destroyed()) , &o, SLOT(deleteLater())); |
|
137 } |
|
138 }; |
|
139 |
|
140 void tst_QObjectRace::moveToThreadRace() |
|
141 { |
|
142 #if defined(Q_OS_SYMBIAN) |
|
143 // ### FIXME: task 257411 - remove xfail once this is fixed |
|
144 QEXPECT_FAIL("", "Symbian event dispatcher can't handle this kind of race, see task: 257411", Abort); |
|
145 QVERIFY(false); |
|
146 #endif |
|
147 RaceObject *object = new RaceObject; |
|
148 |
|
149 enum { ThreadCount = 6 }; |
|
150 RaceThread *threads[ThreadCount]; |
|
151 for (int i = 0; i < ThreadCount; ++i) { |
|
152 threads[i] = new RaceThread; |
|
153 threads[i]->setObject(object); |
|
154 } |
|
155 |
|
156 object->moveToThread(threads[0]); |
|
157 |
|
158 for (int i = 0; i < ThreadCount; ++i) |
|
159 threads[i]->start(); |
|
160 |
|
161 while(!threads[0]->isFinished()) { |
|
162 QPointer<RaceObject> foo (object); |
|
163 QObject o; |
|
164 connect(&o, SIGNAL(destroyed()) , object, SLOT(destroSlot())); |
|
165 connect(object, SIGNAL(destroyed()) , &o, SLOT(deleteLater())); |
|
166 QTest::qWait(10); |
|
167 } |
|
168 // the other threads should finish pretty quickly now |
|
169 for (int i = 1; i < ThreadCount; ++i) |
|
170 QVERIFY(threads[i]->wait(300)); |
|
171 |
|
172 for (int i = 0; i < ThreadCount; ++i) |
|
173 delete threads[i]; |
|
174 delete object; |
|
175 } |
|
176 |
|
177 |
|
178 class MyObject : public QObject |
|
179 { Q_OBJECT |
|
180 public slots: |
|
181 void slot1() { emit signal1(); } |
|
182 void slot2() { emit signal2(); } |
|
183 void slot3() { emit signal3(); } |
|
184 void slot4() { emit signal4(); } |
|
185 void slot5() { emit signal5(); } |
|
186 void slot6() { emit signal6(); } |
|
187 void slot7() { emit signal7(); } |
|
188 signals: |
|
189 void signal1(); |
|
190 void signal2(); |
|
191 void signal3(); |
|
192 void signal4(); |
|
193 void signal5(); |
|
194 void signal6(); |
|
195 void signal7(); |
|
196 }; |
|
197 |
|
198 |
|
199 |
|
200 class DestroyThread : public QThread |
|
201 { |
|
202 Q_OBJECT |
|
203 QObject **objects; |
|
204 int number; |
|
205 |
|
206 public: |
|
207 void setObjects(QObject **o, int n) |
|
208 { |
|
209 objects = o; |
|
210 number = n; |
|
211 for(int i = 0; i < number; i++) |
|
212 objects[i]->moveToThread(this); |
|
213 } |
|
214 |
|
215 void run() { |
|
216 for(int i = 0; i < number; i++) |
|
217 delete objects[i]; |
|
218 } |
|
219 }; |
|
220 |
|
221 #if defined(Q_OS_SYMBIAN) |
|
222 // Symbian needs "a bit" more time |
|
223 # define EXTRA_THREAD_WAIT TenMinutes |
|
224 # define MAIN_THREAD_WAIT TwentyFiveMinutes |
|
225 #else |
|
226 # define EXTRA_THREAD_WAIT 3000 |
|
227 # define MAIN_THREAD_WAIT TwoMinutes |
|
228 #endif |
|
229 |
|
230 void tst_QObjectRace::destroyRace() |
|
231 { |
|
232 #if defined(Q_OS_SYMBIAN) && defined(Q_CC_NOKIAX86) |
|
233 // ### FIXME: task 257411 - remove xfail once this is fixed. |
|
234 // Oddly enough, this seems to work properly in HW, if given enough time and memory. |
|
235 QEXPECT_FAIL("", "Symbian event dispatcher can't handle this kind of race on emulator, see task: 257411", Abort); |
|
236 QVERIFY(false); |
|
237 #endif |
|
238 |
|
239 enum { ThreadCount = 10, ObjectCountPerThread = 733, |
|
240 ObjectCount = ThreadCount * ObjectCountPerThread }; |
|
241 |
|
242 const char *_slots[] = { SLOT(slot1()) , SLOT(slot2()) , SLOT(slot3()), |
|
243 SLOT(slot4()) , SLOT(slot5()) , SLOT(slot6()), |
|
244 SLOT(slot7()) }; |
|
245 |
|
246 const char *_signals[] = { SIGNAL(signal1()), SIGNAL(signal2()), SIGNAL(signal3()), |
|
247 SIGNAL(signal4()), SIGNAL(signal5()), SIGNAL(signal6()), |
|
248 SIGNAL(signal7()) }; |
|
249 |
|
250 QObject *objects[ObjectCount]; |
|
251 for (int i = 0; i < ObjectCount; ++i) |
|
252 objects[i] = new MyObject; |
|
253 |
|
254 |
|
255 for (int i = 0; i < ObjectCount * 11; ++i) { |
|
256 connect(objects[(i*13) % ObjectCount], _signals[(2*i)%7], |
|
257 objects[((i+2)*17) % ObjectCount], _slots[(3*i+2)%7] ); |
|
258 connect(objects[((i+6)*23) % ObjectCount], _signals[(5*i+4)%7], |
|
259 objects[((i+8)*41) % ObjectCount], _slots[(i+6)%7] ); |
|
260 } |
|
261 |
|
262 DestroyThread *threads[ThreadCount]; |
|
263 for (int i = 0; i < ThreadCount; ++i) { |
|
264 threads[i] = new DestroyThread; |
|
265 threads[i]->setObjects(objects + i*ObjectCountPerThread, ObjectCountPerThread); |
|
266 } |
|
267 |
|
268 for (int i = 0; i < ThreadCount; ++i) |
|
269 threads[i]->start(); |
|
270 |
|
271 QVERIFY(threads[0]->wait(MAIN_THREAD_WAIT)); |
|
272 // the other threads should finish pretty quickly now |
|
273 for (int i = 1; i < ThreadCount; ++i) |
|
274 QVERIFY(threads[i]->wait(EXTRA_THREAD_WAIT)); |
|
275 |
|
276 for (int i = 0; i < ThreadCount; ++i) |
|
277 delete threads[i]; |
|
278 } |
|
279 |
|
280 |
|
281 QTEST_MAIN(tst_QObjectRace) |
|
282 #include "tst_qobjectrace.moc" |