misc.cpp 11 KB

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