1 /* $Id: test_message_mt.cpp 474401 2015-07-28 18:08:58Z vakatov $
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 IMessage/IMessageListener in multithreaded environment
30 *
31 */
32
33 #include <ncbi_pch.hpp>
34 #include <corelib/test_mt.hpp>
35 #include <corelib/ncbi_message.hpp>
36
37 #include <common/test_assert.h> /* This header must go last */
38
39 USING_NCBI_SCOPE;
40
41 /////////////////////////////////////////////////////////////////////////////
42 // Test application
43
44 class CTestMessageApp : public CThreadedApp
45 {
46 public:
47 virtual bool Thread_Run(int idx);
48 void DoPostMessages(int idx);
49
50 protected:
51 virtual bool TestApp_Init(void);
52 virtual bool TestApp_Exit(void);
53 };
54
55
56 class CTestListener : public CMessageListener_Basic
57 {
58 public:
CTestListener(const string & handle_prefix)59 CTestListener(const string& handle_prefix) : m_HandlePrefix(handle_prefix) {}
60
PostMessage(const IMessage & message)61 virtual EPostResult PostMessage(const IMessage& message)
62 {
63 if ( NStr::StartsWith(message.GetText(), m_HandlePrefix) ) {
64 return CMessageListener_Basic::PostMessage(message);
65 }
66 return eUnhandled;
67 }
68
69 private:
70 string m_HandlePrefix;
71 };
72
73
74 static const int kMsgCount = 1000;
75
DoPostMessages(int idx)76 void CTestMessageApp::DoPostMessages(int idx)
77 {
78 string idx_str = NStr::NumericToString(idx);
79
80 _ASSERT(IMessageListener::HaveListeners());
81
82 for (int i = 0; i < kMsgCount; i++) {
83 string s = idx_str + "-" + NStr::NumericToString(i);
84 IMessageListener::Post(CMessage_Basic("msg2-" + s, eDiag_Error));
85 IMessageListener::Post(CMessage_Basic("msg3-" + s, eDiag_Error));
86 IMessageListener::Post(CMessage_Basic("msg4-" + s, eDiag_Error));
87 }
88 }
89
90
Thread_Run(int idx)91 bool CTestMessageApp::Thread_Run(int idx)
92 {
93 // This one should receive all messages, both handled and unhandled.
94 CRef<CMessageListener_Basic> ml1(new CMessageListener_Basic());
95 /*size_t ml1_depth = */
96 IMessageListener::PushListener(*ml1, IMessageListener::eListen_All);
97
98 // Receive unhandled messages only.
99 CRef<CMessageListener_Basic> ml2(new CMessageListener_Basic());
100 size_t ml2_depth = IMessageListener::PushListener(*ml2);
101
102 // Receive all messages, handle only those starting with 'msg3'
103 CRef<CTestListener> ml3(new CTestListener("msg3"));
104 /*size_t ml3_depth = */
105 IMessageListener::PushListener(*ml3, IMessageListener::eListen_All);
106
107 // Handle messages starting with 'msg4'
108 CRef<CTestListener> ml4(new CTestListener("msg4"));
109 size_t ml4_depth = IMessageListener::PushListener(*ml4);
110
111 // Handle nothing. Used to test popping lost listeners.
112 CRef<CTestListener> ml5(new CTestListener("---"));
113 size_t ml5_depth = IMessageListener::PushListener(*ml5);
114
115 DoPostMessages(idx);
116
117 // Pop both ml4 and ml5. Warns about lost listener once per process.
118 IMessageListener::PopListener(ml4_depth);
119 // Once-per-process warning about already removed listener.
120 IMessageListener::PopListener(ml5_depth);
121 // Remove just the topmost listener.
122 IMessageListener::PopListener();
123 // Only one listener should be removed.
124 IMessageListener::PopListener(ml2_depth);
125 IMessageListener::PopListener();
126 _ASSERT(!IMessageListener::HaveListeners());
127
128 _ASSERT(ml1->Count() == kMsgCount*3); // All messages
129 _ASSERT(ml2->Count() == kMsgCount); // 'msg2' only
130 _ASSERT(ml3->Count() == kMsgCount); // 'msg3' only
131 _ASSERT(ml4->Count() == kMsgCount); // 'msg4' only
132 _ASSERT(ml5->Count() == 0);
133
134 return true;
135 }
136
137
TestApp_Init(void)138 bool CTestMessageApp::TestApp_Init(void)
139 {
140 NcbiCout << NcbiEndl
141 << "Testing message listeners with "
142 << NStr::IntToString(s_NumThreads)
143 << " threads..."
144 << NcbiEndl;
145
146 return true;
147 }
148
TestApp_Exit(void)149 bool CTestMessageApp::TestApp_Exit(void)
150 {
151 NcbiCout << "Test completed successfully!"
152 << NcbiEndl << NcbiEndl;
153 return true;
154 }
155
156
157
158 ///////////////////////////////////
159 // MAIN
160 //
161
main(int argc,const char * argv[])162 int main(int argc, const char* argv[])
163 {
164 return CTestMessageApp().AppMain(argc, argv);
165 }
166