1 /* $Id: test_semaphore_mt.cpp 567764 2018-07-24 12:05:33Z ivanov $
2 * ===========================================================================
3 *
4 * PUBLIC DOMAIN NOTICE
5 * National Center for Biotechnology Information
6 *
7 * This software/database is a "United States Government Work" under the
8 * terms of the United States Copyright Act. It was written as part of
9 * the author's official duties as a United States Government employee and
10 * thus cannot be copyrighted. This software/database is freely available
11 * to the public for use. The National Library of Medicine and the U.S.
12 * Government have not placed any restriction on its use or reproduction.
13 *
14 * Although all reasonable efforts have been taken to ensure the accuracy
15 * and reliability of the software and data, the NLM and the U.S.
16 * Government do not and cannot warrant the performance or results that
17 * may be obtained by using this software or data. The NLM and the U.S.
18 * Government disclaim all warranties, express or implied, including
19 * warranties of performance, merchantability or fitness for any particular
20 * purpose.
21 *
22 * Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * Author: Andrei Gourianov, gouriano@ncbi.nlm.nih.gov
27 *
28 * File Description:
29 * Test CSemaphore class in multithreaded environment
30 * NOTE: in order to run correctly the number of threads MUST be even!
31 *
32 * the test is a very simple producer/consumer model
33 * one thread produces "items" (increments integer counter)
34 * next thread consumes the same amount of items (decrements integer counter)
35 * "Content" semaphore is used to notify consumers of how many items are
36 * available.
37 *
38 */
39
40 #include <ncbi_pch.hpp>
41 #include <corelib/ncbithr.hpp>
42 #include <corelib/ncbimtx.hpp>
43 #include <corelib/ncbi_system.hpp>
44 #include <corelib/test_mt.hpp>
45
46 #include <common/test_assert.h> /* This header must go last */
47
48 USING_NCBI_SCOPE;
49
50
51 /////////////////////////////////////////////////////////////////////////////
52 // Test application
53
54 class CTestSemaphoreApp : public CThreadedApp
55 {
56 public:
57 virtual bool Thread_Init(int idx);
58 virtual bool Thread_Run(int idx);
59 protected:
60 virtual bool TestApp_Init(void);
61 virtual bool TestApp_Exit(void);
62
63 private:
64 // produce Num items
65 void Produce(int Num);
66 // consume Num items
67 void Consume(int Num);
68
69 static CSemaphore s_semContent, s_semStorage;
70 static int s_Counter, s_Id;
71 };
72
73
74 /////////////////////////////////////////////////////////////////////////////
75
76 CSemaphore CTestSemaphoreApp::s_semContent(0,10);
77 CSemaphore CTestSemaphoreApp::s_semStorage(1,1);
78
79 int CTestSemaphoreApp::s_Counter=0;
80 int CTestSemaphoreApp::s_Id=0;
81
82
83 /////////////////////////////////////////////////////////////////////////////
84 // IMPLEMENTATION
85
Produce(int Num)86 void CTestSemaphoreApp::Produce(int Num)
87 {
88 // Storage semaphore acts as a kind of mutex - its only purpose
89 // is to protect Counter
90 s_semStorage.Wait();
91 s_Counter += Num;
92 NcbiCout << "+" << Num << "=" << s_Counter << NcbiEndl;
93 s_semStorage.Post();
94
95 // Content semaphore notifies consumer threads of how many items can be
96 // consumed. Slow consumption with fast production causes Content semaphore
97 // to overflow from time to time. We catch exception and wait for consumers
98 // to consume something
99 for (bool Posted=false; !Posted;) {
100 try {
101 s_semContent.Post(Num);
102 Posted = true;
103 }
104 catch (exception& e) {
105 NcbiCout << e.what() << NcbiEndl;
106 SleepMilliSec(500);
107 }
108 }
109 }
110
111
Consume(int Num)112 void CTestSemaphoreApp::Consume(int Num)
113 {
114 for (int i = Num; i > 0; --i ) {
115 // we can only consume one by one
116 s_semContent.Wait();
117 s_semStorage.Wait();
118 --s_Counter;
119 NcbiCout << "-1=" << s_Counter << NcbiEndl;
120 s_semStorage.Post();
121 SleepMilliSec(500);
122 }
123 }
124
125
Thread_Init(int)126 bool CTestSemaphoreApp::Thread_Init(int /*idx*/)
127 {
128 return true;
129 }
130
131
Thread_Run(int idx)132 bool CTestSemaphoreApp::Thread_Run(int idx)
133 {
134 // One thread produces, next - consumes;
135 // production is fast, consumption is slow (because of Sleep).
136 // NOTE: In order to run correctly the number of threads MUST be even!
137
138 xncbi_SetValidateAction(eValidate_Throw);
139
140 if ( idx % 2 != 1) {
141 Produce((idx/2)%3 + 1);
142 }
143 else {
144 Consume((idx/2)%3 + 1);
145 }
146 return true;
147 }
148
149
TestApp_Init(void)150 bool CTestSemaphoreApp::TestApp_Init(void)
151 {
152 NcbiCout
153 << NcbiEndl
154 << "Testing semaphores with "
155 << NStr::UIntToString(s_NumThreads)
156 << " threads"
157 << NcbiEndl;
158 if ( s_NumThreads%2 != 0 ) {
159 #ifdef NCBI_THREADS
160 throw runtime_error("The number of threads MUST be even");
161 #else
162 ++s_NumThreads;
163 #endif
164 }
165 return true;
166 }
167
168
TestApp_Exit(void)169 bool CTestSemaphoreApp::TestApp_Exit(void)
170 {
171 NcbiCout
172 << "Test completed"
173 << NcbiEndl
174 << " counter = " << s_Counter
175 << NcbiEndl;
176 // storage must be available
177 assert( s_semStorage.TryWait() );
178 // content must be empty
179 assert( !s_semContent.TryWait() );
180 assert( s_Counter == 0 );
181 return true;
182 }
183
184
185
186 /////////////////////////////////////////////////////////////////////////////
187 // MAIN
188
main(int argc,const char * argv[])189 int main(int argc, const char* argv[])
190 {
191 return CTestSemaphoreApp().AppMain(argc, argv);
192 }
193