]> git.saurik.com Git - wxWidgets.git/blob - tests/thread/misc.cpp
Fix wxComboCtrlBase::DoGetSizeFromTextSize() performance regression.
[wxWidgets.git] / tests / thread / misc.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: tests/thread/misc.cpp
3 // Purpose: Miscellaneous wxThread test cases
4 // Author: Francesco Montorsi (extracted from console sample)
5 // Created: 2010-05-10
6 // Copyright: (c) 2010 wxWidgets team
7 ///////////////////////////////////////////////////////////////////////////////
8
9 // ----------------------------------------------------------------------------
10 // headers
11 // ----------------------------------------------------------------------------
12
13 #include "testprec.h"
14
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18
19 #ifndef WX_PRECOMP
20 #endif // WX_PRECOMP
21
22 #include "wx/thread.h"
23 #include "wx/utils.h"
24
25 // ----------------------------------------------------------------------------
26 // globals
27 // ----------------------------------------------------------------------------
28
29 static size_t gs_counter = (size_t)-1;
30 static wxCriticalSection gs_critsect;
31 static wxSemaphore gs_cond;
32
33 class MyJoinableThread : public wxThread
34 {
35 public:
36 MyJoinableThread(size_t n) : wxThread(wxTHREAD_JOINABLE)
37 { m_n = n; Create(); }
38
39 // thread execution starts here
40 virtual ExitCode Entry();
41
42 private:
43 size_t m_n;
44 };
45
46 wxThread::ExitCode MyJoinableThread::Entry()
47 {
48 unsigned long res = 1;
49 for ( size_t n = 1; n < m_n; n++ )
50 {
51 res *= n;
52
53 // it's a loooong calculation :-)
54 wxMilliSleep(100);
55 }
56
57 return (ExitCode)res;
58 }
59
60 class MyDetachedThread : public wxThread
61 {
62 public:
63 MyDetachedThread(size_t n, wxChar ch)
64 {
65 m_n = n;
66 m_ch = ch;
67 m_cancelled = false;
68
69 Create();
70 }
71
72 // thread execution starts here
73 virtual ExitCode Entry();
74
75 // and stops here
76 virtual void OnExit();
77
78 private:
79 size_t m_n; // number of characters to write
80 wxChar m_ch; // character to write
81
82 bool m_cancelled; // false if we exit normally
83 };
84
85 wxThread::ExitCode MyDetachedThread::Entry()
86 {
87 {
88 wxCriticalSectionLocker lock(gs_critsect);
89 if ( gs_counter == (size_t)-1 )
90 gs_counter = 1;
91 else
92 gs_counter++;
93 }
94
95 for ( size_t n = 0; n < m_n; n++ )
96 {
97 if ( TestDestroy() )
98 {
99 m_cancelled = true;
100
101 break;
102 }
103
104 //wxPutchar(m_ch);
105 //fflush(stdout);
106
107 wxMilliSleep(100);
108 }
109
110 return 0;
111 }
112
113 void MyDetachedThread::OnExit()
114 {
115 //wxLogTrace(wxT("thread"), wxT("Thread %ld is in OnExit"), GetId());
116
117 wxCriticalSectionLocker lock(gs_critsect);
118 if ( !--gs_counter && !m_cancelled )
119 gs_cond.Post();
120 }
121
122 class MyWaitingThread : public wxThread
123 {
124 public:
125 MyWaitingThread( wxMutex *mutex, wxCondition *condition )
126 {
127 m_mutex = mutex;
128 m_condition = condition;
129
130 Create();
131 }
132
133 virtual ExitCode Entry()
134 {
135 //wxPrintf(wxT("Thread %lu has started running.\n"), GetId());
136 gs_cond.Post();
137
138 //wxPrintf(wxT("Thread %lu starts to wait...\n"), GetId());
139
140 m_mutex->Lock();
141 m_condition->Wait();
142 m_mutex->Unlock();
143
144 //wxPrintf(wxT("Thread %lu finished to wait, exiting.\n"), GetId());
145
146 return 0;
147 }
148
149 private:
150 wxMutex *m_mutex;
151 wxCondition *m_condition;
152 };
153
154 // semaphore tests
155 #include "wx/datetime.h"
156
157 class MySemaphoreThread : public wxThread
158 {
159 public:
160 MySemaphoreThread(int i, wxSemaphore *sem)
161 : wxThread(wxTHREAD_JOINABLE),
162 m_sem(sem),
163 m_i(i)
164 {
165 Create();
166 }
167
168 virtual ExitCode Entry()
169 {
170 wxUnusedVar(m_i);
171
172 //wxPrintf(wxT("%s: Thread #%d (%ld) starting to wait for semaphore...\n"),
173 // wxDateTime::Now().FormatTime().c_str(), m_i, (long)GetId());
174
175 m_sem->Wait();
176
177 //wxPrintf(wxT("%s: Thread #%d (%ld) acquired the semaphore.\n"),
178 // wxDateTime::Now().FormatTime().c_str(), m_i, (long)GetId());
179
180 Sleep(1000);
181
182 //wxPrintf(wxT("%s: Thread #%d (%ld) releasing the semaphore.\n"),
183 // wxDateTime::Now().FormatTime().c_str(), m_i, (long)GetId());
184
185 m_sem->Post();
186
187 return 0;
188 }
189
190 private:
191 wxSemaphore *m_sem;
192 int m_i;
193 };
194
195 WX_DEFINE_ARRAY_PTR(wxThread *, ArrayThreads);
196
197 // ----------------------------------------------------------------------------
198 // test class
199 // ----------------------------------------------------------------------------
200
201 class MiscThreadTestCase : public CppUnit::TestCase
202 {
203 public:
204 MiscThreadTestCase();
205
206 private:
207 CPPUNIT_TEST_SUITE( MiscThreadTestCase );
208 CPPUNIT_TEST( TestJoinable );
209 CPPUNIT_TEST( TestDetached );
210 CPPUNIT_TEST( TestThreadSuspend );
211 CPPUNIT_TEST( TestThreadDelete );
212 CPPUNIT_TEST( TestThreadRun );
213 CPPUNIT_TEST( TestThreadConditions );
214 CPPUNIT_TEST( TestSemaphore );
215 CPPUNIT_TEST_SUITE_END();
216
217 void TestJoinable();
218 void TestDetached();
219 void TestSemaphore();
220
221 void TestThreadSuspend();
222 void TestThreadDelete();
223 void TestThreadRun();
224 void TestThreadConditions();
225
226 DECLARE_NO_COPY_CLASS(MiscThreadTestCase)
227 };
228
229 // register in the unnamed registry so that these tests are run by default
230 CPPUNIT_TEST_SUITE_REGISTRATION( MiscThreadTestCase );
231
232 // also include in its own registry so that these tests can be run alone
233 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( MiscThreadTestCase, "MiscThreadTestCase" );
234
235 MiscThreadTestCase::MiscThreadTestCase()
236 {
237 int nCPUs = wxThread::GetCPUCount();
238 if ( nCPUs != -1 )
239 wxThread::SetConcurrency(nCPUs);
240 }
241
242 void MiscThreadTestCase::TestJoinable()
243 {
244 // calc 10! in the background
245 MyJoinableThread thread(10);
246 CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread.Run() );
247 CPPUNIT_ASSERT_EQUAL( 362880, (unsigned long)thread.Wait() );
248 }
249
250 void MiscThreadTestCase::TestDetached()
251 {
252 static const size_t nThreads = 3;
253 MyDetachedThread *threads[nThreads];
254
255 size_t n;
256 for ( n = 0; n < nThreads; n++ )
257 {
258 threads[n] = new MyDetachedThread(10, 'A' + n);
259 }
260
261 threads[0]->SetPriority(wxPRIORITY_MIN);
262 threads[1]->SetPriority(wxPRIORITY_MAX);
263
264 for ( n = 0; n < nThreads; n++ )
265 {
266 CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, threads[n]->Run() );
267 }
268
269 // wait until all threads terminate
270 CPPUNIT_ASSERT_EQUAL( wxSEMA_NO_ERROR, gs_cond.Wait() );
271 }
272
273 void MiscThreadTestCase::TestSemaphore()
274 {
275 static const int SEM_LIMIT = 3;
276
277 wxSemaphore sem(SEM_LIMIT, SEM_LIMIT);
278 ArrayThreads threads;
279
280 for ( int i = 0; i < 3*SEM_LIMIT; i++ )
281 {
282 threads.Add(new MySemaphoreThread(i, &sem));
283 CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, threads.Last()->Run() );
284 }
285
286 for ( size_t n = 0; n < threads.GetCount(); n++ )
287 {
288 CPPUNIT_ASSERT_EQUAL( 0, (long)threads[n]->Wait() );
289 delete threads[n];
290 }
291 }
292
293 void MiscThreadTestCase::TestThreadSuspend()
294 {
295 MyDetachedThread *thread = new MyDetachedThread(15, 'X');
296
297 CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread->Run() );
298
299 // this is for this demo only, in a real life program we'd use another
300 // condition variable which would be signaled from wxThread::Entry() to
301 // tell us that the thread really started running - but here just wait a
302 // bit and hope that it will be enough (the problem is, of course, that
303 // the thread might still not run when we call Pause() which will result
304 // in an error)
305 wxMilliSleep(300);
306
307 for ( size_t n = 0; n < 3; n++ )
308 {
309 thread->Pause();
310
311 if ( n > 0 )
312 {
313 // don't sleep but resume immediately the first time
314 wxMilliSleep(300);
315 }
316
317 CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread->Resume() );
318 }
319
320 // wait until the thread terminates
321 CPPUNIT_ASSERT_EQUAL( wxSEMA_NO_ERROR, gs_cond.Wait() );
322 }
323
324 void MiscThreadTestCase::TestThreadDelete()
325 {
326 // FIXME:
327 // As above, using Sleep() is only for testing here - we must use some
328 // synchronisation object instead to ensure that the thread is still
329 // running when we delete it - deleting a detached thread which already
330 // terminated will lead to a crash!
331
332 MyDetachedThread *thread0 = new MyDetachedThread(30, 'W');
333 CPPUNIT_ASSERT_EQUAL( wxTHREAD_MISC_ERROR, thread0->Delete() );
334 // delete a thread which didn't start to run yet.
335
336 MyDetachedThread *thread1 = new MyDetachedThread(30, 'Y');
337 CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread1->Run() );
338 wxMilliSleep(300);
339 CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread1->Delete() );
340 // delete a running thread
341
342 MyDetachedThread *thread2 = new MyDetachedThread(30, 'Z');
343 CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread2->Run() );
344 wxMilliSleep(300);
345 CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread2->Pause() );
346 CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread2->Delete() );
347 // delete a sleeping thread
348
349 MyJoinableThread thread3(20);
350 CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread3.Run() );
351 CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread3.Delete() );
352 // delete a joinable running thread
353
354 MyJoinableThread thread4(2);
355 CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread4.Run() );
356 wxMilliSleep(300);
357 CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread4.Delete() );
358 // delete a joinable thread which already terminated
359 }
360
361 void MiscThreadTestCase::TestThreadRun()
362 {
363 MyJoinableThread thread1(2);
364 CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, thread1.Run() );
365 thread1.Wait(); // wait until the thread ends
366
367 // verify that running twice the same thread fails
368 WX_ASSERT_FAILS_WITH_ASSERT( thread1.Run() );
369 }
370
371 void MiscThreadTestCase::TestThreadConditions()
372 {
373 wxMutex mutex;
374 wxCondition condition(mutex);
375
376 // otherwise its difficult to understand which log messages pertain to
377 // which condition
378 //wxLogTrace(wxT("thread"), wxT("Local condition var is %08x, gs_cond = %08x"),
379 // condition.GetId(), gs_cond.GetId());
380
381 // create and launch threads
382 MyWaitingThread *threads[10];
383
384 size_t n;
385 for ( n = 0; n < WXSIZEOF(threads); n++ )
386 {
387 threads[n] = new MyWaitingThread( &mutex, &condition );
388 }
389
390 for ( n = 0; n < WXSIZEOF(threads); n++ )
391 {
392 CPPUNIT_ASSERT_EQUAL( wxTHREAD_NO_ERROR, threads[n]->Run() );
393 }
394
395 // wait until all threads run
396 // NOTE: main thread is waiting for the other threads to start
397 size_t nRunning = 0;
398 while ( nRunning < WXSIZEOF(threads) )
399 {
400 CPPUNIT_ASSERT_EQUAL( wxSEMA_NO_ERROR, gs_cond.Wait() );
401
402 nRunning++;
403
404 // note that main thread is already running
405 }
406
407 wxMilliSleep(500);
408
409 #if 1
410 // now wake one of them up
411 CPPUNIT_ASSERT_EQUAL( wxCOND_NO_ERROR, condition.Signal() );
412 #endif
413
414 wxMilliSleep(200);
415
416 // wake all the (remaining) threads up, so that they can exit
417 CPPUNIT_ASSERT_EQUAL( wxCOND_NO_ERROR, condition.Broadcast() );
418
419 // give them time to terminate (dirty!)
420 wxMilliSleep(500);
421 }