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