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