1 /*  $Id: test_param_mt.cpp 550022 2017-10-30 16:18:25Z grichenk $
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:  Aleksey Grichenko
27  *
28  * File Description:
29  *   Test for parameters in multithreaded environment
30  *
31  */
32 
33 #include <ncbi_pch.hpp>
34 #include <corelib/test_mt.hpp>
35 #include <corelib/ncbi_param.hpp>
36 
37 #include <common/test_assert.h>  /* This header must go last */
38 
39 USING_NCBI_SCOPE;
40 
41 const char*    kStrParam_Default    = "StrParam Default";
42 const bool     kBoolParam_Default   = false;
43 const unsigned kUIntParam_Default   = 123;
44 const char*    kStaticStr_Default   = "StaticStringParam";
45 const double   kDoubleParam_Default = 123.456;
46 const int      kInitFuncParam_Default = 123;
47 const char*    kInitFuncParam_SetStr  = "456";
48 const int      kInitFuncParam_SetInt  = 456;
49 // Structure literals can not be passed to NCBI_PARAM_DEF* macros directly.
50 const int kTestStruct_Default = 123456; // {123, 456}
51 
52 NCBI_PARAM_DECL(int, ParamTest, ThreadIdx);
53 NCBI_PARAM_DECL(string, ParamTest, StrParam);
54 NCBI_PARAM_DECL(bool, ParamTest, BoolParam);
55 NCBI_PARAM_DECL(unsigned int, ParamTest, UIntParam);
56 NCBI_PARAM_DECL(string, ParamTest, StaticStr);
57 NCBI_PARAM_DECL(int, ParamTest, NoThreadParam);
58 NCBI_PARAM_DECL(int, ParamTest, NoLoadParam);
59 NCBI_PARAM_DECL(double, ParamTest, DoubleParam);
60 
61 NCBI_PARAM_DEF(int, ParamTest, ThreadIdx, 0);
62 NCBI_PARAM_DEF(string, ParamTest, StrParam, kStrParam_Default);
63 NCBI_PARAM_DEF(bool, ParamTest, BoolParam, kBoolParam_Default);
64 NCBI_PARAM_DEF(unsigned int, ParamTest, UIntParam, kUIntParam_Default);
65 NCBI_PARAM_DEF(string, ParamTest, StaticStr, kStaticStr_Default);
66 NCBI_PARAM_DEF_EX(int, ParamTest, NoThreadParam, 0, eParam_NoThread, 0);
67 NCBI_PARAM_DEF_EX(int, ParamTest, NoLoadParam, 0, eParam_NoLoad, 0);
68 NCBI_PARAM_DEF(double, ParamTest, DoubleParam, kDoubleParam_Default);
69 
70 string InitIntParam(void);
71 NCBI_PARAM_DECL(int, ParamTest, InitFuncParam);
72 NCBI_PARAM_DEF_WITH_INIT(int, ParamTest, InitFuncParam, kInitFuncParam_Default, InitIntParam);
InitIntParam(void)73 string InitIntParam(void)
74 {
75     return kInitFuncParam_SetStr;
76 }
77 
78 // User-defined types
79 
80 // Classes/structures require CSafeStatic_Proxy wrappers to be initialized statically.
81 // It's impossible to pass a non-scalar literal to nested macros, so the class/structure
82 // should have a constructor with a single scalar argument.
83 // CSafeStatic_Proxy also requires the wrapped type to have a default constructor.
84 struct STestStruct
85 {
STestStructSTestStruct86     STestStruct(void) : first(0), second(0) {}
STestStructSTestStruct87     STestStruct(int init) : first(init % 1000), second(init / 1000) {}
operator ==STestStruct88     bool operator==(const STestStruct& val)
89     { return first == val.first  &&  second == val.second; }
90 
91     int first;
92     int second;
93 };
94 
operator >>(CNcbiIstream & in,STestStruct & val)95 CNcbiIstream& operator>>(CNcbiIstream& in, STestStruct& val)
96 {
97     in >> val.first >> val.second;
98     return in;
99 }
100 
operator <<(CNcbiOstream & out,const STestStruct val)101 CNcbiOstream& operator<<(CNcbiOstream& out, const STestStruct val)
102 {
103     out << val.first << ' ' << val.second;
104     return out;
105 }
106 
107 
108 namespace ncbi {
109 NCBI_PARAM_STATIC_PROXY(STestStruct, int);
110 };
111 NCBI_PARAM_DECL(STestStruct, ParamTest, Struct);
112 
113 
114 typedef NCBI_PARAM_TYPE(ParamTest, ThreadIdx) TParam_ThreadIdx;
115 typedef NCBI_PARAM_TYPE(ParamTest, StrParam) TParam_StrParam;
116 typedef NCBI_PARAM_TYPE(ParamTest, BoolParam) TParam_BoolParam;
117 typedef NCBI_PARAM_TYPE(ParamTest, UIntParam) TParam_UIntParam;
118 typedef NCBI_PARAM_TYPE(ParamTest, NoThreadParam) TParam_NoThread;
119 typedef NCBI_PARAM_TYPE(ParamTest, NoLoadParam) TParam_NoLoad;
120 typedef NCBI_PARAM_TYPE(ParamTest, DoubleParam) TParam_DoubleParam;
121 typedef NCBI_PARAM_TYPE(ParamTest, Struct) TParam_Struct;
122 typedef NCBI_PARAM_TYPE(ParamTest, InitFuncParam) TParam_InitFunc;
123 typedef NCBI_PARAM_TYPE(ParamTest, StaticStr) TParam_StaticStr;
124 
125 // Static instance of string param. Must be initialized before CStaticParamTester
126 // instance below.
127 TParam_StaticStr s_StaticStrParam;
128 
129 // Access parameters before they have been fully initialized.
130 // The scalar default values passed to NCBI_PARAM_DEF* should be returned.
131 class CStaticParamTester
132 {
133 public:
CStaticParamTester(void)134     CStaticParamTester(void)
135     {
136         _ASSERT(TParam_InitFunc::GetDefault() == kInitFuncParam_SetInt);
137         _ASSERT(TParam_StrParam::GetDefault() == kStrParam_Default);
138         _ASSERT(TParam_BoolParam::GetDefault() == kBoolParam_Default);
139         _ASSERT(TParam_UIntParam::GetDefault() == kUIntParam_Default);
140         _ASSERT(TParam_DoubleParam::GetDefault() == kDoubleParam_Default);
141         _ASSERT(TParam_Struct::GetDefault() == kTestStruct_Default);
142         _ASSERT(s_StaticStrParam.Get() == kStaticStr_Default);
143     }
144 
~CStaticParamTester(void)145     ~CStaticParamTester(void)
146     {
147         _ASSERT(TParam_InitFunc::GetDefault() == kInitFuncParam_SetInt);
148         _ASSERT(TParam_StrParam::GetDefault() == kStrParam_Default);
149         // Skip bool param - it may have any value after being toggled by each thread.
150         // _ASSERT(TParam_BoolParam::GetDefault());
151         _ASSERT(TParam_UIntParam::GetDefault() == kUIntParam_Default);
152         _ASSERT(TParam_DoubleParam::GetDefault() == kDoubleParam_Default);
153         _ASSERT(TParam_Struct::GetDefault() == kTestStruct_Default);
154         // Static param instance have been destroyed by now, but should not fail
155         // when trying to get its value (the value itself may be empty).
156         s_StaticStrParam.Get();
157         _ASSERT(TParam_StaticStr::GetDefault() == kStaticStr_Default);
158     }
159 };
160 
161 // The tester should be initialized before static params below.
162 static const CStaticParamTester s_StaticParamTester;
163 
164 NCBI_PARAM_DEF(STestStruct, ParamTest, Struct, kTestStruct_Default);
165 
166 
167 /////////////////////////////////////////////////////////////////////////////
168 //  Test application
169 
170 class CTestParamApp : public CThreadedApp
171 {
172 public:
173     virtual bool Thread_Run(int idx);
174 protected:
175     virtual bool TestApp_Init(void);
176     virtual bool TestApp_Exit(void);
177 };
178 
179 
Thread_Run(int idx)180 bool CTestParamApp::Thread_Run(int idx)
181 {
182     // Check params initialized by a function
183     _ASSERT(TParam_InitFunc::GetDefault() == 456);
184 
185     // Set thread default value
186     TParam_ThreadIdx::SetThreadDefault(idx);
187     string str_idx = NStr::IntToString(idx);
188 
189     _ASSERT(TParam_StrParam::GetDefault() == kStrParam_Default);
190     _ASSERT(s_StaticStrParam.Get() == kStaticStr_Default);
191 
192     // Non-initializable param must always have the same value
193     _ASSERT(TParam_NoLoad::GetDefault() == 0);
194     // No-thread param has no thread local value
195     bool error = false;
196     try {
197         TParam_NoThread::SetThreadDefault(idx + 1000);
198     }
199     catch (CParamException&) {
200         error = true;
201     }
202     _ASSERT(error);
203     TParam_NoThread::SetDefault(idx);
204     _ASSERT(TParam_NoThread::GetThreadDefault() != idx + 1000);
205 
206     // Initialize parameter with global default
207     TParam_StrParam str_param1;
208     // Set new thread default
209     TParam_StrParam::SetThreadDefault(kStrParam_Default + str_idx);
210     // Initialize parameter with thread default
211     TParam_StrParam str_param2;
212 
213     // Parameters should keep the value set during initialization
214     _ASSERT(str_param1.Get() == kStrParam_Default);
215     _ASSERT(str_param2.Get() == kStrParam_Default + str_idx);
216     // Test local reset
217     str_param2.Reset();
218     _ASSERT(str_param2.Get() == TParam_StrParam::GetThreadDefault());
219     // Test thread reset
220     TParam_StrParam::ResetThreadDefault();
221     _ASSERT(TParam_StrParam::GetThreadDefault() == kStrParam_Default);
222     str_param2.Reset();
223     _ASSERT(str_param2.Get() == str_param1.Get());
224     // Restore thread default to global default for testing in ST mode
225     TParam_StrParam::SetThreadDefault(kStrParam_Default);
226 
227     // Set thread default value
228     bool odd = idx % 2 != 0;
229     TParam_BoolParam::SetThreadDefault(odd);
230     TParam_BoolParam bool_param1;
231     // Check if the value is correct
232     _ASSERT(bool_param1.Get() == odd);
233     // Change global default not to match the thread default
234     TParam_BoolParam::SetDefault(!bool_param1.Get());
235     // The parameter should use thread default rather than global default
236     TParam_BoolParam bool_param2;
237     _ASSERT(bool_param2.Get() == odd);
238 
239     _ASSERT(TParam_Struct::GetDefault() == kTestStruct_Default);
240     TParam_Struct struct_param;
241     _ASSERT(struct_param.Get() == kTestStruct_Default);
242 
243     // Thread default value should not change
244     _ASSERT(TParam_ThreadIdx::GetThreadDefault() == idx);
245 
246     double dbl = TParam_DoubleParam::GetDefault();
247     _ASSERT(dbl == kDoubleParam_Default);
248     return true;
249 }
250 
251 
TestApp_Init(void)252 bool CTestParamApp::TestApp_Init(void)
253 {
254     NcbiCout << NcbiEndl
255              << "Testing parameters with "
256              << NStr::IntToString(s_NumThreads)
257              << " threads..."
258              << NcbiEndl;
259 
260     return true;
261 }
262 
TestApp_Exit(void)263 bool CTestParamApp::TestApp_Exit(void)
264 {
265     // Test resets in ST mode
266     TParam_StrParam::SetDefault(string(kStrParam_Default) + "-test1");
267     TParam_StrParam::ResetThreadDefault();
268     _ASSERT(TParam_StrParam::GetThreadDefault() ==
269         TParam_StrParam::GetDefault());
270     TParam_StrParam::SetDefault(string(kStrParam_Default) + "-test2");
271     // the global value must be used
272     _ASSERT(TParam_StrParam::GetThreadDefault() ==
273         TParam_StrParam::GetDefault());
274     // Global reset
275     TParam_StrParam::ResetDefault();
276     _ASSERT(TParam_StrParam::GetDefault() == kStrParam_Default);
277 
278     NcbiCout << "Test completed successfully!"
279              << NcbiEndl << NcbiEndl;
280     return true;
281 }
282 
283 
284 ///////////////////////////////////
285 // MAIN
286 //
287 
main(int argc,const char * argv[])288 int main(int argc, const char* argv[])
289 {
290     return CTestParamApp().AppMain(argc, argv);
291 }
292