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