Change license from LGPL to GPL version 2 + linking exception. This fixes C++ templat...
[libt2n] / test / reentrant.cpp
1 /*
2 Copyright (C) 2004 by Intra2net AG
3
4 The software in this package is distributed under the GNU General
5 Public License version 2 (with a special exception described below).
6
7 A copy of GNU General Public License (GPL) is included in this distribution,
8 in the file COPYING.GPL.
9
10 As a special exception, if other files instantiate templates or use macros
11 or inline functions from this file, or you compile this file and link it
12 with other works to produce a work based on this file, this file
13 does not by itself cause the resulting work to be covered
14 by the GNU General Public License.
15
16 However the source code for this file must still be made available
17 in accordance with section (3) of the GNU General Public License.
18
19 This exception does not invalidate any other reasons why a work based
20 on this file might be covered by the GNU General Public License.
21 */
22 #include <sys/types.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <signal.h>
26 #include <stdio.h>
27
28 #include <iostream>
29 #include <string>
30 #include <sstream>
31 #include <stdexcept>
32
33 #include <cppunit/extensions/TestFactoryRegistry.h>
34 #include <cppunit/ui/text/TestRunner.h>
35 #include <cppunit/extensions/HelperMacros.h>
36
37 #include <boost/archive/binary_oarchive.hpp>
38 #include <boost/archive/binary_iarchive.hpp>
39 #include <boost/archive/xml_oarchive.hpp>
40 #include <boost/archive/xml_iarchive.hpp>
41 #include <boost/serialization/serialization.hpp>
42
43 #include <container.hxx>
44 #include <socket_client.hxx>
45 #include <socket_server.hxx>
46 #include <command_client.hxx>
47 #include <command_server.hxx>
48
49 using namespace std;
50 using namespace CppUnit;
51 using namespace libt2n;
52
53 namespace reentrant
54 {
55
56 command_server *global_server = NULL;
57
58 int fork_count = 3;
59 int requests_per_child = 100;
60 int all_requests = (2 << (fork_count-1)) * requests_per_child;
61
62 int seen_client_requests = 0;
63
64 string testfunc(const string& str)
65 {
66     string ret;
67     ret=str+", testfunc() was here";
68
69     // call handle, eventually reentrant
70     if (global_server)
71         global_server->handle(10000);
72
73     ++seen_client_requests;
74
75     return ret;
76 }
77
78 class testfunc_res : public libt2n::result
79 {
80     private:
81         string res;
82
83         friend class boost::serialization::access;
84         template<class Archive>
85         void serialize(Archive & ar, const unsigned int version)
86         {
87             ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::result);
88             ar & BOOST_SERIALIZATION_NVP(res);
89         }
90
91     public:
92         testfunc_res()
93             {
94             }
95
96         testfunc_res(const string& str)
97         {
98             res=str;
99         }
100
101         string get_data()
102         {
103             return res;
104         }
105 };
106
107
108 class testfunc_cmd : public libt2n::command
109 {
110     private:
111         string param;
112
113         friend class boost::serialization::access;
114         template<class Archive>
115         void serialize(Archive & ar, const unsigned int version)
116         {
117             ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::command);
118             ar & BOOST_SERIALIZATION_NVP(param);
119         }
120
121     public:
122         testfunc_cmd()
123             {
124             }
125
126         testfunc_cmd(const string& str)
127         {
128             param=str;
129         }
130
131         libt2n::result* operator()()
132         {
133             return new testfunc_res(testfunc(param));
134         }
135 };
136
137 }
138
139
140 #include <boost/serialization/export.hpp>
141
142 BOOST_CLASS_EXPORT(reentrant::testfunc_cmd)
143 BOOST_CLASS_EXPORT(reentrant::testfunc_res)
144
145 using namespace reentrant;
146
147 class test_reentrant : public TestFixture
148 {
149     CPPUNIT_TEST_SUITE(test_reentrant);
150
151     CPPUNIT_TEST(ReentrantServer);
152
153     CPPUNIT_TEST_SUITE_END();
154
155     public:
156
157     void setUp()
158     { }
159
160     void tearDown()
161     { }
162
163     void ReentrantServer()
164     {
165         switch(fork())
166         {
167             case -1:
168             {
169                 CPPUNIT_FAIL("fork error");
170                 break;
171             }
172             case 0:
173             // child
174             {
175                 // wait till server is up
176                 sleep(2);
177
178                 // hammer the server
179                 for (int i = 0; i < fork_count; i++)
180                     fork();
181
182                 try
183                 {
184                     for (int i=0; i < requests_per_child; i++)
185                     {
186                         socket_client_connection sc("./socket");
187                         // sc.set_logging(&cerr,debug);
188                         command_client cc(&sc);
189
190                         result_container rc;
191                         cc.send_command(new testfunc_cmd("hello"),rc);
192
193                         testfunc_res *res = dynamic_cast<testfunc_res*>(rc.get_result());
194                         if (res)
195                         {
196                             string ret = res->get_data();
197                             if (ret != "hello, testfunc() was here")
198                                 std::cout << "ERROR reentrant server testfunc_res failed, res: \"" << ret << "\"\n";
199                         }
200                         else
201                         {
202                             std::cout << "ERROR result from reentrant server empty (" << rc.get_result() << ")\n";
203                         }
204                     }
205                 } catch (exception &e)
206                 {
207                     cerr << "caught exception: " << e.what() << endl;
208                 } catch(...)
209                 {
210                     std::cerr << "exception in child. ignoring\n";
211                 }
212
213                 // don't call atexit and stuff
214                 _exit(0);
215             }
216
217             default:
218             // parent
219             {
220                 // don't kill us on broken pipe
221                 signal(SIGPIPE, SIG_IGN);
222
223                 socket_server ss("./socket");
224                 command_server cs(ss);
225
226                 global_server=&cs;
227
228                 // Wait until all requests have successed
229                 int safety_check = 0;
230                 while (seen_client_requests < all_requests)
231                 {
232                     ++safety_check;
233                     if (safety_check > 10) {
234                         std::cerr << "reached safety check, aborting.\n";
235                         break;
236                     }
237
238                     long long maxtime=1000000;
239                     while(maxtime > 0)
240                         cs.handle(maxtime,&maxtime);
241                 }
242
243                 global_server = NULL;
244             }
245
246             // we are still alive, everything is ok
247             CPPUNIT_ASSERT_EQUAL(all_requests, seen_client_requests);
248         }
249     }
250
251 };
252
253
254 CPPUNIT_TEST_SUITE_REGISTRATION(test_reentrant);