blob: 75f43490cf6d6154c7bee5da85901f717ff9b8c6 [file] [log] [blame]
Roger Meier2be7f242012-05-10 09:01:45 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19#ifndef _THRIFT_PROCESSOR_TEST_HANDLERS_H_
20#define _THRIFT_PROCESSOR_TEST_HANDLERS_H_ 1
21
Roger Meier2b1a5282012-05-11 10:12:39 +000022#include "EventLog.h"
Roger Meier2be7f242012-05-10 09:01:45 +000023#include "gen-cpp/ParentService.h"
24#include "gen-cpp/ChildService.h"
25
26namespace apache { namespace thrift { namespace test {
27
28class ParentHandler : virtual public ParentServiceIf {
29 public:
30 ParentHandler(const boost::shared_ptr<EventLog>& log) :
31 triggerMonitor(&mutex_),
32 generation_(0),
33 wait_(false),
34 log_(log) { }
35
36 int32_t incrementGeneration() {
37 concurrency::Guard g(mutex_);
38 log_->append(EventLog::ET_CALL_INCREMENT_GENERATION, 0, 0);
39 return ++generation_;
40 }
41
42 int32_t getGeneration() {
43 concurrency::Guard g(mutex_);
44 log_->append(EventLog::ET_CALL_GET_GENERATION, 0, 0);
45 return generation_;
46 }
47
48 void addString(const std::string& s) {
49 concurrency::Guard g(mutex_);
50 log_->append(EventLog::ET_CALL_ADD_STRING, 0, 0);
51 strings_.push_back(s);
52 }
53
54 void getStrings(std::vector<std::string>& _return) {
55 concurrency::Guard g(mutex_);
56 log_->append(EventLog::ET_CALL_GET_STRINGS, 0, 0);
57 _return = strings_;
58 }
59
60 void getDataWait(std::string& _return, int32_t length) {
61 concurrency::Guard g(mutex_);
62 log_->append(EventLog::ET_CALL_GET_DATA_WAIT, 0, 0);
63
64 blockUntilTriggered();
65
66 _return.append(length, 'a');
67 }
68
69 void onewayWait() {
70 concurrency::Guard g(mutex_);
71 log_->append(EventLog::ET_CALL_ONEWAY_WAIT, 0, 0);
72
73 blockUntilTriggered();
74 }
75
76 void exceptionWait(const std::string& message) {
77 concurrency::Guard g(mutex_);
78 log_->append(EventLog::ET_CALL_EXCEPTION_WAIT, 0, 0);
79
80 blockUntilTriggered();
81
82 MyError e;
83 e.message = message;
84 throw e;
85 }
86
87 void unexpectedExceptionWait(const std::string& message) {
88 concurrency::Guard g(mutex_);
89 log_->append(EventLog::ET_CALL_UNEXPECTED_EXCEPTION_WAIT, 0, 0);
90
91 blockUntilTriggered();
92
93 MyError e;
94 e.message = message;
95 throw e;
96 }
97
98 /**
99 * After prepareTriggeredCall() is invoked, calls to any of the *Wait()
100 * functions won't return until triggerPendingCalls() is invoked
101 *
102 * This has to be a separate function invoked by the main test thread
103 * in order to to avoid race conditions.
104 */
105 void prepareTriggeredCall() {
106 concurrency::Guard g(mutex_);
107 wait_ = true;
108 }
109
110 /**
111 * Wake up all calls waiting in blockUntilTriggered()
112 */
113 void triggerPendingCalls() {
114 concurrency::Guard g(mutex_);
115 wait_ = false;
116 triggerMonitor.notifyAll();
117 }
118
119 protected:
120 /**
121 * blockUntilTriggered() won't return until triggerPendingCalls() is invoked
122 * in another thread.
123 *
124 * This should only be called when already holding mutex_.
125 */
126 void blockUntilTriggered() {
127 while (wait_) {
128 triggerMonitor.waitForever();
129 }
130
131 // Log an event when we return
132 log_->append(EventLog::ET_WAIT_RETURN, 0, 0);
133 }
134
135 concurrency::Mutex mutex_;
136 concurrency::Monitor triggerMonitor;
137 int32_t generation_;
138 bool wait_;
139 std::vector<std::string> strings_;
140 boost::shared_ptr<EventLog> log_;
141};
142
143class ChildHandler : public ParentHandler, virtual public ChildServiceIf {
144 public:
145 ChildHandler(const boost::shared_ptr<EventLog>& log) :
146 ParentHandler(log),
147 value_(0) {}
148
149 int32_t setValue(int32_t value) {
150 concurrency::Guard g(mutex_);
151 log_->append(EventLog::ET_CALL_SET_VALUE, 0, 0);
152
153 int32_t oldValue = value_;
154 value_ = value;
155 return oldValue;
156 }
157
158 int32_t getValue() {
159 concurrency::Guard g(mutex_);
160 log_->append(EventLog::ET_CALL_GET_VALUE, 0, 0);
161
162 return value_;
163 }
164
165 protected:
166 int32_t value_;
167};
168
169struct ConnContext {
170 public:
171 ConnContext(boost::shared_ptr<protocol::TProtocol> in,
172 boost::shared_ptr<protocol::TProtocol> out,
173 uint32_t id) :
174 input(in),
175 output(out),
176 id(id) {}
177
178 boost::shared_ptr<protocol::TProtocol> input;
179 boost::shared_ptr<protocol::TProtocol> output;
180 uint32_t id;
181};
182
183struct CallContext {
184 public:
185 CallContext(ConnContext *context, uint32_t id, const std::string& name) :
186 connContext(context),
187 name(name),
188 id(id) {}
189
190 ConnContext *connContext;
191 std::string name;
192 uint32_t id;
193};
194
195class ServerEventHandler : public server::TServerEventHandler {
196 public:
197 ServerEventHandler(const boost::shared_ptr<EventLog>& log) :
198 nextId_(1),
199 log_(log) {}
200
201 virtual void preServe() {}
202
203 virtual void* createContext(boost::shared_ptr<protocol::TProtocol> input,
204 boost::shared_ptr<protocol::TProtocol> output) {
205 ConnContext* context = new ConnContext(input, output, nextId_);
206 ++nextId_;
207 log_->append(EventLog::ET_CONN_CREATED, context->id, 0);
208 return context;
209 }
210
211 virtual void deleteContext(void* serverContext,
212 boost::shared_ptr<protocol::TProtocol>input,
213 boost::shared_ptr<protocol::TProtocol>output) {
214 ConnContext* context = reinterpret_cast<ConnContext*>(serverContext);
215
216 if (input != context->input) {
217 abort();
218 }
219 if (output != context->output) {
220 abort();
221 }
222
223 log_->append(EventLog::ET_CONN_DESTROYED, context->id, 0);
224
225 delete context;
226 }
227
228 virtual void processContext(
229 void* serverContext,
230 boost::shared_ptr<transport::TTransport> transport) {
231 // TODO: We currently don't test the behavior of the processContext()
232 // calls. The various server implementations call processContext() at
233 // slightly different times, and it is too annoying to try and account for
234 // their various differences.
235 //
236 // TThreadedServer, TThreadPoolServer, and TSimpleServer usually wait until
237 // they see the first byte of a request before calling processContext().
238 // However, they don't wait for the first byte of the very first request,
239 // and instead immediately call processContext() before any data is
240 // received.
241 //
242 // TNonblockingServer always waits until receiving the full request before
243 // calling processContext().
244#if 0
245 ConnContext* context = reinterpret_cast<ConnContext*>(serverContext);
246 log_->append(EventLog::ET_PROCESS, context->id, 0);
Konrad Grochowskib3f5ffc2014-11-06 19:32:59 +0100247#else
248 THRIFT_UNUSED_VARIABLE(serverContext);
249 THRIFT_UNUSED_VARIABLE(transport);
Roger Meier2be7f242012-05-10 09:01:45 +0000250#endif
251 }
252
253 protected:
254 uint32_t nextId_;
255 boost::shared_ptr<EventLog> log_;
256};
257
258class ProcessorEventHandler : public TProcessorEventHandler {
259 public:
260 ProcessorEventHandler(const boost::shared_ptr<EventLog>& log) :
261 nextId_(1),
262 log_(log) {}
263
264 void* getContext(const char* fnName, void* serverContext) {
265 ConnContext* connContext = reinterpret_cast<ConnContext*>(serverContext);
266
267 CallContext* context = new CallContext(connContext, nextId_, fnName);
268 ++nextId_;
269
270 log_->append(EventLog::ET_CALL_STARTED, connContext->id, context->id,
271 fnName);
272 return context;
273 }
274
275 void freeContext(void* ctx, const char* fnName) {
276 CallContext* context = reinterpret_cast<CallContext*>(ctx);
277 checkName(context, fnName);
278 log_->append(EventLog::ET_CALL_FINISHED, context->connContext->id,
279 context->id, fnName);
280 delete context;
281 }
282
283 void preRead(void* ctx, const char* fnName) {
284 CallContext* context = reinterpret_cast<CallContext*>(ctx);
285 checkName(context, fnName);
286 log_->append(EventLog::ET_PRE_READ, context->connContext->id, context->id,
287 fnName);
288 }
289
290 void postRead(void* ctx, const char* fnName, uint32_t bytes) {
Konrad Grochowskib3f5ffc2014-11-06 19:32:59 +0100291 THRIFT_UNUSED_VARIABLE(bytes);
Roger Meier2be7f242012-05-10 09:01:45 +0000292 CallContext* context = reinterpret_cast<CallContext*>(ctx);
293 checkName(context, fnName);
294 log_->append(EventLog::ET_POST_READ, context->connContext->id, context->id,
295 fnName);
296 }
297
298 void preWrite(void* ctx, const char* fnName) {
299 CallContext* context = reinterpret_cast<CallContext*>(ctx);
300 checkName(context, fnName);
301 log_->append(EventLog::ET_PRE_WRITE, context->connContext->id, context->id,
302 fnName);
303 }
304
305 void postWrite(void* ctx, const char* fnName, uint32_t bytes) {
Konrad Grochowskib3f5ffc2014-11-06 19:32:59 +0100306 THRIFT_UNUSED_VARIABLE(bytes);
Roger Meier2be7f242012-05-10 09:01:45 +0000307 CallContext* context = reinterpret_cast<CallContext*>(ctx);
308 checkName(context, fnName);
309 log_->append(EventLog::ET_POST_WRITE, context->connContext->id,
310 context->id, fnName);
311 }
312
313 void asyncComplete(void* ctx, const char* fnName) {
314 CallContext* context = reinterpret_cast<CallContext*>(ctx);
315 checkName(context, fnName);
316 log_->append(EventLog::ET_ASYNC_COMPLETE, context->connContext->id,
317 context->id, fnName);
318 }
319
320 void handlerError(void* ctx, const char* fnName) {
321 CallContext* context = reinterpret_cast<CallContext*>(ctx);
322 checkName(context, fnName);
323 log_->append(EventLog::ET_HANDLER_ERROR, context->connContext->id,
324 context->id, fnName);
325 }
326
327 protected:
328 void checkName(const CallContext* context, const char* fnName) {
329 // Note: we can't use BOOST_CHECK_EQUAL here, since the handler runs in a
330 // different thread from the test functions. Just abort if the names are
331 // different
332 if (context->name != fnName) {
333 fprintf(stderr, "call context name mismatch: \"%s\" != \"%s\"\n",
334 context->name.c_str(), fnName);
335 fflush(stderr);
336 abort();
337 }
338 }
339
340 uint32_t nextId_;
341 boost::shared_ptr<EventLog> log_;
342};
343
344}}} // apache::thrift::test
345
346#endif // _THRIFT_PROCESSOR_TEST_HANDLERS_H_