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