Branch data Line data Source code
1 : : /*
2 : : * Copyright 2006-2008 Savarese Software Research Corporation
3 : : *
4 : : * Licensed under the Apache License, Version 2.0 (the "License");
5 : : * you may not use this file except in compliance with the License.
6 : : * You may obtain a copy of the License at
7 : : *
8 : : * http://www.savarese.com/software/ApacheLicense-2.0
9 : : *
10 : : * Unless required by applicable law or agreed to in writing, software
11 : : * distributed under the License is distributed on an "AS IS" BASIS,
12 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 : : * See the License for the specific language governing permissions and
14 : : * limitations under the License.
15 : : */
16 : :
17 : : /**
18 : : * @file
19 : : * This header defines the Service class.
20 : : */
21 : :
22 : : #ifndef __SSRC_WISP_SERVICE_SERVICE_H
23 : : #define __SSRC_WISP_SERVICE_SERVICE_H
24 : :
25 : : #include <ssrc/wisp/protocol/ContinuationCaller.h>
26 : : #include <ssrc/wisp/service/EventLoop.h>
27 : :
28 : : #include <boost/bind.hpp>
29 : :
30 : : #ifdef WISP_DEBUG
31 : : #include <iostream>
32 : : #endif
33 : :
34 : : __BEGIN_NS_SSRC_WISP_SERVICE
35 : :
36 : : using std::string;
37 : : using NS_SSRC_SPREAD::MembershipInfo;
38 : : using NS_SSRC_SPREAD::Message;
39 : : using NS_SSRC_SPREAD::GroupList;
40 : : using NS_SSRC_WISP_PROTOCOL::MessageInfo;
41 : : using NS_SSRC_WISP_PROTOCOL::message_info_ptr;
42 : : using NS_SSRC_WISP_PROTOCOL::CallException;
43 : : using NS_SSRC_WISP_PROTOCOL::GroupMembershipDisable;
44 : : using NS_SSRC_WISP_PROTOCOL::wisp_message_protocol;
45 : : using NS_SSRC_WISP_PROTOCOL::wisp_message_id;
46 : :
47 : : #define WISP_SERVICE_REQUEST(MessageType) \
48 : : set_request_handler<MessageType>(*this)
49 : : #define WISP_SERVICE_RESPONSE(MessageType) \
50 : : set_response_handler<MessageType>(*this)
51 : : #define WISP_SERVICE_REQUEST_T(MessageType) \
52 : : this->template set_request_handler<MessageType>(*this)
53 : : #define WISP_SERVICE_RESPONSE_T(MessageType) \
54 : : this->template set_response_handler<MessageType>(*this)
55 : :
56 : : #define WISP_SERVICE_REQUEST_BUFFERED(MessageType, msg) \
57 : : set_request_handler<MessageType>(*this, msg)
58 : : #define WISP_SERVICE_RESPONSE_BUFFERED(MessageType, msg) \
59 : : set_response_handler<MessageType>(*this, msg)
60 : : #define WISP_SERVICE_REQUEST_BUFFERED_T(MessageType, msg) \
61 : : this->template set_request_handler<MessageType>(*this, msg)
62 : : #define WISP_SERVICE_RESPONSE_BUFFERED_T(MessageType, msg) \
63 : : this->template set_response_handler<MessageType>(*this, msg)
64 : :
65 : :
66 : : typedef boost::function<void (const EventInfo &)> timeout_handler;
67 : : // We expose message_handler_type, message_handler_entry, and
68 : : // message_handler_map specifically to support dynamically loaded handlers.
69 : : typedef boost::function<void (MessageInfo &)> message_handler_type;
70 : :
71 : 18 : struct message_handler_entry {
72 : : wisp_message_protocol protocol;
73 : : wisp_message_id id;
74 : : message_handler_type handle_message;
75 : :
76 : 6 : message_handler_entry(const wisp_message_protocol protocol,
77 : : const wisp_message_id id,
78 : : const message_handler_type & message_handler) :
79 : 6 : protocol(protocol), id(id), handle_message(message_handler)
80 : 6 : { }
81 : : };
82 : :
83 : : typedef boost::multi_index_container<
84 : : message_handler_entry,
85 : : boost::multi_index::indexed_by<
86 : : boost::multi_index::hashed_unique<
87 : : boost::multi_index::composite_key<
88 : : message_handler_entry,
89 : : boost::multi_index::member<message_handler_entry,
90 : : wisp_message_protocol,
91 : : &message_handler_entry::protocol>,
92 : : boost::multi_index::member<message_handler_entry, wisp_message_id,
93 : : &message_handler_entry::id> >
94 : : >
95 : : > > message_handler_map;
96 : :
97 : : /* BEGIN EXPERIMENTAL CODE */
98 : : // TODO: This is all one big kluge and should be re-implemented, but it works..
99 [ + - ]: 9 : class ServiceContext {
100 : : public:
101 : : class TimeoutHandler : public EventHandler {
102 : : ServiceContext & _context;
103 : : bool _once;
104 : : timeout_handler _handler;
105 : :
106 : : public:
107 : :
108 : : TimeoutHandler(ServiceContext & context,
109 : : const timeout_handler & handler, bool once) :
110 : : _context(context), _once(once), _handler(handler)
111 : : { }
112 : :
113 [ # # ][ # # ]: 0 : virtual ~TimeoutHandler() { }
114 : :
115 : 0 : virtual void handle_timeout(const EventInfo & info) {
116 : 0 : _handler(info);
117 : : // TODO: WARNING: This could blow up because the current object
118 : : // may be deleted on the call to remove_timeout. This works only
119 : : // because the removal happens at the end of the function and we
120 : : // don't access 'this' again. Seek an alternative solution.
121 [ # # ]: 0 : if(_once)
122 : 0 : _context.remove_timeout(this);
123 : 0 : }
124 : :
125 : : void execute() {
126 : : handle_timeout(EventInfo(_context.event_loop(), EventLoop::None, false));
127 : : }
128 : :
129 : : bool once() {
130 : : return _once;
131 : : }
132 : :
133 : 0 : const TimeoutHandler *address() const {
134 : 0 : return this;
135 : : }
136 : : };
137 : :
138 : : typedef boost::shared_ptr<TimeoutHandler> timeout_handler_ptr;
139 : :
140 : : typedef boost::multi_index_container<
141 : : timeout_handler_ptr,
142 : : boost::multi_index::indexed_by<
143 : : boost::multi_index::hashed_unique<
144 : : boost::multi_index::const_mem_fun<TimeoutHandler,
145 : : const TimeoutHandler *,
146 : : &TimeoutHandler::address> >
147 : : > > timeout_map;
148 : :
149 : : private:
150 : : EventLoop *loop;
151 : : timeout_map _timeouts;
152 : :
153 : : public:
154 : :
155 [ + - ][ + - ]: 6 : ServiceContext(EventLoop * loop = 0) : loop(loop) { }
156 : :
157 : : timeout_map::size_type count_timeouts() const {
158 : : return _timeouts.size();
159 : : }
160 : :
161 : 3 : void clear_timeouts() {
162 [ - + ]: 3 : for(timeout_map::iterator it = _timeouts.begin(), end = _timeouts.end();
163 : : it != end; ++it)
164 : 0 : remove_timeout(it->get());
165 : 3 : }
166 : :
167 : : timeout_handler_ptr add_timeout(const timeout_handler & handler_fun,
168 : : const TimeValue & timeout,
169 : : const bool once)
170 : : {
171 : : timeout_handler_ptr handler(new TimeoutHandler(*this, handler_fun, once));
172 : : _timeouts.insert(handler);
173 : : loop->add_handler(*handler, EventLoop::None, timeout);
174 : : return handler;
175 : : }
176 : :
177 : 0 : void remove_timeout(TimeoutHandler *timeout) {
178 : : // must remove from loop first or we lose pointer!!!
179 : 0 : loop->remove_handler(*timeout);
180 : 0 : _timeouts.erase(timeout);
181 : 0 : }
182 : :
183 : : EventLoop & event_loop() { return *loop; }
184 : : };
185 : :
186 : :
187 : : typedef ServiceContext::timeout_handler_ptr timeout_ptr;
188 : :
189 : : /* END EXPERIMENTAL CODE */
190 : :
191 : : template<typename PackingTraits = BinaryPackingTraits>
192 : : class ServiceProtocolProcessor {
193 : : public:
194 : : typedef PackingTraits packing_traits;
195 : : typedef typename protocol::ContinuationCaller<packing_traits> caller_type;
196 : :
197 : : enum State { Starting, Started, Stopping, Stopped };
198 : :
199 : : /**
200 : : * Subclasses should redefine this variable to override behavior.
201 : : * We don't reference the template parameter so that you can subclass a
202 : : * protocol processor that is a concrete class that disables group
203 : : * membership and enable it in the subclass. Otherwise, every
204 : : * protocol processor would have to be a template class. The variable
205 : : * is used only by the Service template to initialize _caller.
206 : : */
207 : : static const bool GroupMembership = GroupMembershipDisable;
208 : :
209 : : private:
210 : : State _state;
211 : :
212 : : private:
213 : :
214 : : message_handler_map _request_handlers;
215 : : message_handler_map _response_handlers;
216 : :
217 : : protected:
218 : : caller_type & _caller;
219 : :
220 : : // WARNING!! This is highly experimental and only made available so
221 : : // a subclass may add handlers to the event loop for I/O on other
222 : : // descriptors. The context is only valid in the Starting and
223 : : // Started states.
224 : : ServiceContext & context() { return _context; };
225 : :
226 : 0 : virtual void process_membership_message(const MessageInfo & msginfo,
227 : : const MembershipInfo & meminfo)
228 : 0 : { }
229 : :
230 : : // So you can reuse message types that are expensive to create on each call.
231 : : template<typename MessageType, typename Impl>
232 : 6 : void request(Impl & impl, MessageType & msg, MessageInfo & msginfo)
233 : : SSRC_DECL_THROW(boost::archive::archive_exception, std::ios_base::failure,
234 : : CallException)
235 : : {
236 : 6 : _caller.unpack(msg, msginfo);
237 : : // Cast forces msginfo to be const arg in process_request
238 : 6 : impl.process_request(msg, static_cast<const MessageInfo &>(msginfo));
239 : 6 : }
240 : :
241 : : template<typename MessageType, typename Impl>
242 : 6 : void request(Impl & impl, MessageInfo & msginfo)
243 : : SSRC_DECL_THROW(boost::archive::archive_exception, std::ios_base::failure)
244 : : {
245 : 6 : MessageType msg;
246 [ + - ]: 6 : request(impl, msg, msginfo);
247 : 6 : }
248 : :
249 : : template<typename MessageType, typename Impl>
250 : : void respond(Impl & impl, MessageType & msg, MessageInfo & msginfo)
251 : : SSRC_DECL_THROW(boost::archive::archive_exception, std::ios_base::failure,
252 : : CallException)
253 : : {
254 : : _caller.unpack(msg, msginfo);
255 : : // Cast forces msginfo to be const arg in process_response
256 : : impl.process_response(msg, static_cast<const MessageInfo &>(msginfo));
257 : : }
258 : :
259 : : template<typename MessageType, typename Impl>
260 : : void respond(Impl & impl, MessageInfo & msginfo)
261 : : SSRC_DECL_THROW(boost::archive::archive_exception, std::ios_base::failure)
262 : : {
263 : : MessageType msg;
264 : : respond(impl, msg, msginfo);
265 : : }
266 : :
267 : 6 : bool set_request_handler(const message_handler_entry & handler) {
268 : 6 : return _request_handlers.insert(handler).second;
269 : : }
270 : :
271 : : message_handler_map::size_type
272 : : remove_request_handler(const message_handler_entry & handler) {
273 : : return
274 : : _request_handlers.erase(_request_handlers.key_extractor()((handler)));
275 : : }
276 : :
277 : : void clear_request_handlers() {
278 : : _request_handlers.clear();
279 : : }
280 : :
281 : : template<typename MessageType, typename Impl>
282 : 6 : bool set_request_handler(Impl & impl) {
283 : : return set_request_handler(message_handler_entry(MessageType::protocol,
284 : : MessageType::id,
285 [ + - ][ + - ]: 6 : boost::bind(&ServiceProtocolProcessor::template request<MessageType, Impl>, this, std::ref(impl), _1)));
[ + - ][ + - ]
[ + - ][ + - ]
286 : : }
287 : :
288 : : template<typename MessageType, typename Impl>
289 : : bool set_request_handler(Impl & impl, MessageType & buffer) {
290 : : return set_request_handler(message_handler_entry(MessageType::protocol,
291 : : MessageType::id,
292 : : boost::bind(&ServiceProtocolProcessor::template request<MessageType, Impl>, this, std::ref(impl), std::ref(buffer), _1)));
293 : : }
294 : :
295 : : bool set_response_handler(const message_handler_entry & handler) {
296 : : return _response_handlers.insert(handler).second;
297 : : }
298 : :
299 : : message_handler_map::size_type
300 : : remove_response_handler(const message_handler_entry & handler) {
301 : : return
302 : : _response_handlers.erase(_response_handlers.key_extractor()((handler)));
303 : : }
304 : :
305 : : void clear_response_handlers() {
306 : : _response_handlers.clear();
307 : : }
308 : :
309 : : template<typename MessageType, typename Impl>
310 : : bool set_response_handler(Impl & impl) {
311 : : return set_response_handler(message_handler_entry(MessageType::protocol,
312 : : MessageType::id,
313 : : boost::bind(&ServiceProtocolProcessor::template respond<MessageType, Impl>, this, std::ref(impl), _1))); }
314 : :
315 : : template<typename MessageType, typename Impl>
316 : : bool set_response_handler(Impl & impl, MessageType & buffer) {
317 : : return set_response_handler(message_handler_entry(MessageType::protocol,
318 : : MessageType::id,
319 : : boost::bind(&ServiceProtocolProcessor::template respond<MessageType, Impl>, this, std::ref(impl), std::ref(buffer), _1)));
320 : : }
321 : :
322 : 6 : virtual void transition(State state) {
323 : 6 : _state = state;
324 : 6 : }
325 : :
326 : : public:
327 : :
328 : 3 : explicit ServiceProtocolProcessor(caller_type & caller) :
329 : : _state(Stopped),
330 [ + - ][ + - ]: 3 : _request_handlers(), _response_handlers(), _caller(caller)
[ + - ][ + - ]
[ + - ]
331 : 3 : { }
332 : :
333 [ + - ][ + - ]: 3 : virtual ~ServiceProtocolProcessor() { }
[ - + ]
334 : :
335 : 0 : string name() const {
336 : 0 : return _caller.name();
337 : : }
338 : :
339 : 27 : State state() const {
340 : 27 : return _state;
341 : : }
342 : :
343 : 3 : void start() {
344 : 3 : transition(Starting);
345 : 3 : }
346 : :
347 : : // Services listening for membership messages should wait for self-leave
348 : : // before transitioning to Stopped.
349 : 3 : void stop() {
350 : 3 : transition(Stopping);
351 : 3 : }
352 : :
353 : 0 : void membership(const MessageInfo & msginfo,
354 : : const MembershipInfo & meminfo)
355 : : {
356 : 0 : process_membership_message(msginfo, meminfo);
357 : 0 : }
358 : :
359 : 6 : void request(MessageInfo & msginfo) {
360 : : message_handler_map::iterator it =
361 : : _request_handlers.find(boost::make_tuple(msginfo.protocol(),
362 : 6 : msginfo.id()));
363 [ + - ]: 6 : if(it != _request_handlers.end())
364 : 6 : it->handle_message(msginfo);
365 : : // TODO: add an else log unhandled message
366 : 6 : }
367 : :
368 : : /**
369 : : *
370 : : */
371 : 2 : void response(MessageInfo & msginfo) {
372 [ - + ]: 2 : if(!_caller.resume(msginfo)) {
373 : : message_handler_map::iterator it =
374 : : _response_handlers.find(boost::make_tuple(msginfo.protocol(),
375 : 0 : msginfo.id()));
376 [ # # ]: 0 : if(it != _response_handlers.end())
377 : 0 : it->handle_message(msginfo);
378 : : // TODO: add an else log unhandled message
379 : : }
380 : 2 : }
381 : :
382 : : /* BEGIN EXPERIMENTAL CODE */
383 : : // TODO: This is all one big kluge and should be re-implemented (which
384 : : // is why this is stuffed at the end of the class), but it works.
385 : :
386 : : timeout_ptr schedule_timeout(const timeout_handler & handler,
387 : : const TimeValue & timeout,
388 : : const bool once = EventLoop::Persist)
389 : : {
390 : : return _context.add_timeout(handler, timeout, once);
391 : : }
392 : :
393 : : void cancel_timeout(const timeout_ptr & timeout) {
394 : : _context.remove_timeout(timeout.get());
395 : : }
396 : :
397 : 3 : void clear_timeouts() {
398 : 3 : _context.clear_timeouts();
399 : 3 : }
400 : :
401 : : ServiceContext::timeout_map::size_type count_timeouts() const {
402 : : return _context.count_timeouts();
403 : : }
404 : :
405 : : private:
406 : : template<typename PP> friend class ServiceEventHandler;
407 : :
408 : : ServiceContext _context;
409 : :
410 : 3 : void set_service_context(const ServiceContext & context) {
411 : 3 : _context = context;
412 : 3 : }
413 : :
414 : : /* END EXPERIMENTAL CODE */
415 : : };
416 : :
417 : : template<typename PP>
418 : : class ServiceEventHandler : public EventHandler {
419 : : public:
420 : : typedef PP protocol_processor;
421 : : WISP_IMPORT_T(protocol_processor, caller_type);
422 : :
423 : : private:
424 : : caller_type & _caller;
425 : : protocol_processor _protocol;
426 : : // Scratch variables for handle_event().
427 : : message_info_ptr _message_info;
428 : : MembershipInfo _membership_info;
429 : : protocol::wisp_call_token _min_token, _max_token;
430 : : std::vector<typename caller_type::jumbo_message_key_type> _jumbo_message_keys;
431 : :
432 : : protected:
433 : :
434 : 3 : virtual void remove_handler(EventLoop & loop) {
435 : : /* BEGIN EXPERIMENTAL CODE */
436 : : // TODO: This is a kluge and should be re-implemented, but it works.
437 : 3 : _protocol.clear_timeouts();
438 : : /* END EXPERIMENTAL CODE */
439 : 3 : loop.remove_handler(*this);
440 : 3 : _caller.cancel_all();
441 : 3 : }
442 : :
443 : : public:
444 : :
445 : 3 : explicit ServiceEventHandler(caller_type & caller) :
446 : : _caller(caller), _protocol(caller),
447 : : _message_info(new MessageInfo(caller.message_capacity())),
448 : : _membership_info(),
449 : : _min_token(0), _max_token(0),
450 [ + - ][ + + ]: 3 : _jumbo_message_keys(4)
[ + + ][ + + ]
[ + + ][ + + ]
[ + - ][ - ]
[ - ][ - ][ - ]
[ - ]
451 : 3 : { }
452 : :
453 : : template<typename Initializer>
454 : : explicit ServiceEventHandler(caller_type & caller,
455 : : const Initializer & initializer) :
456 : : _caller(caller), _protocol(caller, initializer),
457 : : _message_info(),
458 : : _membership_info(),
459 : : _min_token(0), _max_token(0),
460 : : _jumbo_message_keys(4)
461 : : { }
462 : :
463 [ + - ][ + - ]: 3 : virtual ~ServiceEventHandler() { }
[ + - ][ + - ]
[ - + ]
464 : :
465 : 16 : typename protocol_processor::State state() const {
466 : 16 : return _protocol.state();
467 : : }
468 : :
469 : 20 : virtual int event_descriptor() const {
470 : 20 : return _caller.mbox().descriptor();
471 : : }
472 : :
473 : 8 : virtual void handle_read(const EventInfo & info) {
474 : : try {
475 [ + - ]: 8 : if(state() != protocol_processor::Stopped) {
476 [ + - ]: 8 : _caller.receive(_message_info);
477 : :
478 [ + - ][ + - ]: 8 : if(_message_info->message.is_regular() &&
[ + - ]
479 : : _protocol.state() < protocol_processor::Stopping)
480 : : {
481 [ + + ]: 8 : if(_message_info->role() == protocol::TwoWayResponse)
482 [ + - ]: 2 : _protocol.response(*_message_info);
483 : : else
484 [ + - ]: 6 : _protocol.request(*_message_info);
485 [ # # ]: 0 : } else if(_message_info->message.is_membership()) {
486 [ # # ]: 0 : _message_info->message.get_membership_info(_membership_info);
487 [ # # ]: 0 : _protocol.membership(*_message_info, _membership_info);
488 : : }
489 : :
490 [ + + ]: 8 : if(state() == protocol_processor::Stopped)
491 [ + - ]: 3 : remove_handler(info.event_loop());
492 : : }
493 : 0 : } catch(const boost::archive::archive_exception & ae) {
494 : : // TODO: log
495 : : #ifdef WISP_DEBUG
496 [ # # ][ # # ]: 0 : std::cerr << _protocol.name()
[ # # ][ # # ]
[ # # ][ # # ]
497 : : << ": Caught boost::archive::archive_exception: "
498 : : << ae.what() << "\nContinuing.";
499 : : #endif
500 [ # # # ]: 0 : } catch(const std::ios_base::failure & iof) {
501 : : // TODO: log. probably don't want to try again
502 : : #ifdef WISP_DEBUG
503 [ # # ][ # # ]: 0 : std::cerr << _protocol.name() << ": Caught std::ios_base::failure: "
[ # # ][ # # ]
[ # # ][ # # ]
504 : : << iof.what() << "\nContinuing.";
505 : : #endif
506 : : }
507 : 8 : }
508 : :
509 : 0 : virtual void handle_timeout(const EventInfo & info) {
510 : : // Cancel continuations
511 : 0 : _caller.cancel_range(_min_token, _max_token);
512 : 0 : _min_token = _max_token;
513 : 0 : _max_token = _caller.call_token();
514 : :
515 : : // Clear out incomplete jumbo messages
516 [ # # ]: 0 : if(!_jumbo_message_keys.empty()) {
517 [ # # ]: 0 : if(_caller.count_jumbo_messages() > 0) {
518 : 0 : _caller.erase_jumbo_messages(_jumbo_message_keys.begin(),
519 : : _jumbo_message_keys.end());
520 : : }
521 : 0 : _jumbo_message_keys.clear();
522 : : }
523 : :
524 [ # # ]: 0 : if(_caller.count_jumbo_messages() > 0) {
525 : 0 : _caller.collect_jumbo_message_keys(_jumbo_message_keys);
526 : : }
527 : 0 : }
528 : :
529 : 6 : protocol_processor & protocol() {
530 : 6 : return _protocol;
531 : : }
532 : :
533 : 3 : void start(EventLoop & loop, const unsigned int call_timeout) {
534 : 3 : _min_token = _max_token = _caller.call_token();
535 : 3 : loop.add_handler(*this, EventLoop::Read, TimeValue(call_timeout, 0));
536 : : /* BEGIN EXPERIMENTAL CODE */
537 : : // TODO: This is a kluge and should be re-implemented, but it works.
538 [ + - ]: 3 : _protocol.set_service_context(ServiceContext(&loop));
539 : : /* END EXPERIMENTAL CODE */
540 : 3 : _protocol.start();
541 : 3 : }
542 : :
543 : : void stop() {
544 : : _protocol.stop();
545 : : }
546 : : };
547 : :
548 : :
549 : : /**
550 : : *
551 : : */
552 : : template<typename EH>
553 [ + - ]: 3 : class Service {
554 : : public:
555 : : typedef EH event_handler;
556 : : WISP_IMPORT_T(event_handler, caller_type);
557 : : WISP_IMPORT_T(event_handler, protocol_processor);
558 : :
559 : : private:
560 : : caller_type _caller;
561 : : event_handler _handler;
562 : :
563 : : protected:
564 : :
565 : 6 : protocol_processor & protocol() {
566 : 6 : return _handler.protocol();
567 : : }
568 : :
569 : : public:
570 : :
571 : 3 : explicit Service(const string & connection = "",
572 : : const string & name = "",
573 : : const unsigned int message_capacity =
574 : : Message::DefaultCapacity) :
575 : : _caller(connection, name, message_capacity,
576 : : protocol_processor::GroupMembership),
577 [ + - ]: 3 : _handler(_caller)
578 : 3 : { }
579 : :
580 : : template<typename Initializer>
581 : : explicit Service(const Initializer & initializer,
582 : : const string & connection = "",
583 : : const string & name = "",
584 : : const unsigned int message_capacity =
585 : : Message::DefaultCapacity) :
586 : : _caller(connection, name, message_capacity,
587 : : protocol_processor::GroupMembership),
588 : : _handler(_caller, initializer)
589 : : { }
590 : :
591 : 6 : const string & name() const {
592 : 6 : return _caller.name();
593 : : }
594 : :
595 : 3 : typename protocol_processor::State state() {
596 : 3 : return protocol().state();
597 : : }
598 : :
599 : 3 : void start(EventLoop & loop, unsigned int call_timeout) {
600 : 3 : _handler.start(loop, call_timeout);
601 : 3 : }
602 : :
603 : : void stop() {
604 : : _handler.stop();
605 : : }
606 : : };
607 : :
608 : : __END_NS_SSRC_WISP_SERVICE
609 : :
610 : : #endif
|