blob: a3ac2ce7103060cad4b57a5825efe5e95f18be0d [file] [log] [blame]
Klement Sekeradc15be22017-06-12 06:49:33 +02001/*
2 *------------------------------------------------------------------
3 * Copyright (c) 2017 Cisco and/or its affiliates.
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.apache.org/licenses/LICENSE-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#ifndef vapi_hpp_included
19#define vapi_hpp_included
20
21#include <cstddef>
22#include <vector>
23#include <mutex>
24#include <queue>
25#include <cassert>
26#include <functional>
27#include <algorithm>
28#include <atomic>
29#include <vppinfra/types.h>
30#include <vapi/vapi.h>
31#include <vapi/vapi_internal.h>
32#include <vapi/vapi_dbg.h>
33#include <vapi/vpe.api.vapi.h>
34
35#if VAPI_CPP_DEBUG_LEAKS
36#include <unordered_set>
37#endif
38
39/**
40 * @file
41 * @brief C++ VPP API
42 */
43
44namespace vapi
45{
46
47class Connection;
48
49template <typename Req, typename Resp, typename... Args> class Request;
50template <typename M> class Msg;
51template <typename M> void vapi_swap_to_be (M *msg);
52template <typename M> void vapi_swap_to_host (M *msg);
53template <typename M, typename... Args>
54M *vapi_alloc (Connection &con, Args...);
55template <typename M> vapi_msg_id_t vapi_get_msg_id_t ();
56template <typename M> class Event_registration;
57
58class Unexpected_msg_id_exception : public std::exception
59{
60public:
61 virtual const char *what () const throw ()
62 {
63 return "unexpected message id";
64 }
65};
66
67class Msg_not_available_exception : public std::exception
68{
69public:
70 virtual const char *what () const throw ()
71 {
72 return "message unavailable";
73 }
74};
75
76typedef enum {
77 /** response not ready yet */
78 RESPONSE_NOT_READY,
79
80 /** response to request is ready */
81 RESPONSE_READY,
82
83 /** no response to request (will never come) */
84 RESPONSE_NO_RESPONSE,
85} vapi_response_state_e;
86
87/**
88 * Class representing common functionality of a request - response state
89 * and context
90 */
91class Common_req
92{
93public:
94 virtual ~Common_req (){};
95
96 Connection &get_connection ()
97 {
98 return con;
99 };
100
101 vapi_response_state_e get_response_state (void) const
102 {
103 return response_state;
104 }
105
106private:
107 Connection &con;
108 Common_req (Connection &con) : con{con}, response_state{RESPONSE_NOT_READY}
109 {
110 }
111
112 void set_response_state (vapi_response_state_e state)
113 {
114 response_state = state;
115 }
116
117 virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id,
118 void *shm_data) = 0;
119
120 void set_context (u32 context)
121 {
122 this->context = context;
123 }
124
125 u32 get_context ()
126 {
127 return context;
128 }
129
130 u32 context;
131 vapi_response_state_e response_state;
132
133 friend class Connection;
134
135 template <typename M> friend class Msg;
136
137 template <typename Req, typename Resp, typename... Args>
138 friend class Request;
139
140 template <typename Req, typename Resp, typename... Args> friend class Dump;
141
142 template <typename M> friend class Event_registration;
143};
144
145/**
146 * Class representing a connection to VPP
147 *
148 * After creating a Connection object, call connect() to actually connect
149 * to VPP. Use is_msg_available to discover whether a specific message is known
150 * and supported by the VPP connected to.
151 */
152class Connection
153{
154public:
155 Connection (void) : vapi_ctx{0}, event_count{0}
156 {
157
158 vapi_error_e rv = VAPI_OK;
159 if (!vapi_ctx)
160 {
161 if (VAPI_OK != (rv = vapi_ctx_alloc (&vapi_ctx)))
162 {
163 throw std::bad_alloc ();
164 }
165 }
166 events.reserve (vapi_get_message_count () + 1);
167 }
168
169 Connection (const Connection &) = delete;
170
171 ~Connection (void)
172 {
173 vapi_ctx_free (vapi_ctx);
174#if VAPI_CPP_DEBUG_LEAKS
175 for (auto x : shm_data_set)
176 {
177 printf ("Leaked shm_data@%p!\n", x);
178 }
179#endif
180 }
181
182 /**
183 * @brief check if message identified by it's message id is known by the
184 * vpp to which the connection is open
185 */
186 bool is_msg_available (vapi_msg_id_t type)
187 {
188 return vapi_is_msg_available (vapi_ctx, type);
189 }
190
191 /**
192 * @brief connect to vpp
193 *
194 * @param name application name
195 * @param chroot_prefix shared memory prefix
196 * @param max_queued_request max number of outstanding requests queued
197 *
198 * @return VAPI_OK on success, other error code on error
199 */
200 vapi_error_e connect (const char *name, const char *chroot_prefix,
201 int max_outstanding_requests, int response_queue_size)
202 {
203 return vapi_connect (vapi_ctx, name, chroot_prefix,
204 max_outstanding_requests, response_queue_size,
205 VAPI_MODE_BLOCKING);
206 }
207
208 /**
209 * @brief disconnect from vpp
210 *
211 * @return VAPI_OK on success, other error code on error
212 */
213 vapi_error_e disconnect ()
214 {
215 auto x = requests.size ();
216 while (x > 0)
217 {
218 VAPI_DBG ("popping request @%p", requests.front ());
219 requests.pop_front ();
220 --x;
221 }
222 return vapi_disconnect (vapi_ctx);
223 };
224
225 /**
226 * @brief get event file descriptor
227 *
228 * @note this file descriptor becomes readable when messages (from vpp)
229 * are waiting in queue
230 *
231 * @param[out] fd pointer to result variable
232 *
233 * @return VAPI_OK on success, other error code on error
234 */
235 vapi_error_e get_fd (int *fd)
236 {
237 return vapi_get_fd (vapi_ctx, fd);
238 }
239
240 /**
241 * @brief wait for responses from vpp and assign them to appropriate objects
242 *
243 * @param limit stop dispatch after the limit object received it's response
244 *
245 * @return VAPI_OK on success, other error code on error
246 */
247 vapi_error_e dispatch (const Common_req *limit = nullptr)
248 {
249 std::lock_guard<std::mutex> lock (dispatch_mutex);
250 vapi_error_e rv = VAPI_OK;
251 bool loop_again = true;
252 while (loop_again)
253 {
254 void *shm_data;
255 size_t shm_data_size;
256 rv = vapi_recv (vapi_ctx, &shm_data, &shm_data_size);
257 if (VAPI_OK != rv)
258 {
259 return rv;
260 }
261#if VAPI_CPP_DEBUG_LEAKS
262 on_shm_data_alloc (shm_data);
263#endif
264 std::lock_guard<std::recursive_mutex> requests_lock (requests_mutex);
265 std::lock_guard<std::recursive_mutex> events_lock (events_mutex);
266 vapi_msg_id_t id = vapi_lookup_vapi_msg_id_t (
267 vapi_ctx, be16toh (*static_cast<u16 *> (shm_data)));
268 bool has_context = vapi_msg_is_with_context (id);
269 bool break_dispatch = false;
270 Common_req *matching_req = nullptr;
271 if (has_context)
272 {
273 u32 context = *reinterpret_cast<u32 *> (
274 (static_cast<u8 *> (shm_data) + vapi_get_context_offset (id)));
275 const auto x = requests.front ();
276 matching_req = x;
277 if (context == x->context)
278 {
279 std::tie (rv, break_dispatch) =
280 x->assign_response (id, shm_data);
281 }
282 else
283 {
284 std::tie (rv, break_dispatch) =
285 x->assign_response (id, nullptr);
286 }
287 if (break_dispatch)
288 {
289 requests.pop_front ();
290 }
291 }
292 else
293 {
294 if (events[id])
295 {
296 std::tie (rv, break_dispatch) =
297 events[id]->assign_response (id, shm_data);
298 matching_req = events[id];
299 }
300 else
301 {
302 msg_free (shm_data);
303 }
304 }
305 if ((matching_req && matching_req == limit && break_dispatch) ||
306 VAPI_OK != rv)
307 {
308 return rv;
309 }
310 loop_again = !requests.empty () || (event_count > 0);
311 }
312 return rv;
313 }
314
315 /**
316 * @brief convenience wrapper function
317 */
318 vapi_error_e dispatch (const Common_req &limit)
319 {
320 return dispatch (&limit);
321 }
322
323 /**
324 * @brief wait for response to a specific request
325 *
326 * @param req request to wait for response for
327 *
328 * @return VAPI_OK on success, other error code on error
329 */
330 vapi_error_e wait_for_response (const Common_req &req)
331 {
332 if (RESPONSE_READY == req.get_response_state ())
333 {
334 return VAPI_OK;
335 }
336 return dispatch (req);
337 }
338
339private:
340 void msg_free (void *shm_data)
341 {
342#if VAPI_CPP_DEBUG_LEAKS
343 on_shm_data_free (shm_data);
344#endif
345 vapi_msg_free (vapi_ctx, shm_data);
346 }
347
348 template <template <typename XReq, typename XResp, typename... XArgs>
349 class X,
350 typename Req, typename Resp, typename... Args>
351 vapi_error_e send (X<Req, Resp, Args...> *req)
352 {
353 if (!req)
354 {
355 return VAPI_EINVAL;
356 }
357 u32 req_context =
358 req_context_counter.fetch_add (1, std::memory_order_relaxed);
359 req->request.shm_data->header.context = req_context;
360 vapi_swap_to_be<Req> (req->request.shm_data);
361 std::lock_guard<std::recursive_mutex> lock (requests_mutex);
362 vapi_error_e rv = vapi_send (vapi_ctx, req->request.shm_data);
363 if (VAPI_OK == rv)
364 {
365 VAPI_DBG ("Push %p", req);
366 requests.emplace_back (req);
367 req->set_context (req_context);
368#if VAPI_CPP_DEBUG_LEAKS
369 on_shm_data_free (req->request.shm_data);
370#endif
371 req->request.shm_data = nullptr; /* consumed by vapi_send */
372 }
373 else
374 {
375 vapi_swap_to_host<Req> (req->request.shm_data);
376 }
377 return rv;
378 }
379
380 template <template <typename XReq, typename XResp, typename... XArgs>
381 class X,
382 typename Req, typename Resp, typename... Args>
383 vapi_error_e send_with_control_ping (X<Req, Resp, Args...> *req)
384 {
385 if (!req)
386 {
387 return VAPI_EINVAL;
388 }
389 u32 req_context =
390 req_context_counter.fetch_add (1, std::memory_order_relaxed);
391 req->request.shm_data->header.context = req_context;
392 vapi_swap_to_be<Req> (req->request.shm_data);
393 std::lock_guard<std::recursive_mutex> lock (requests_mutex);
394 vapi_error_e rv = vapi_send_with_control_ping (
395 vapi_ctx, req->request.shm_data, req_context);
396 if (VAPI_OK == rv)
397 {
398 VAPI_DBG ("Push %p", req);
399 requests.emplace_back (req);
400 req->set_context (req_context);
401#if VAPI_CPP_DEBUG_LEAKS
402 on_shm_data_free (req->request.shm_data);
403#endif
404 req->request.shm_data = nullptr; /* consumed by vapi_send */
405 }
406 else
407 {
408 vapi_swap_to_host<Req> (req->request.shm_data);
409 }
410 return rv;
411 }
412
413 void unregister_request (Common_req *request)
414 {
415 std::lock_guard<std::recursive_mutex> lock (requests_mutex);
416 std::remove (requests.begin (), requests.end (), request);
417 }
418
419 template <typename M> void register_event (Event_registration<M> *event)
420 {
421 const vapi_msg_id_t id = M::get_msg_id ();
422 std::lock_guard<std::recursive_mutex> lock (events_mutex);
423 events[id] = event;
424 ++event_count;
425 }
426
427 template <typename M> void unregister_event (Event_registration<M> *event)
428 {
429 const vapi_msg_id_t id = M::get_msg_id ();
430 std::lock_guard<std::recursive_mutex> lock (events_mutex);
431 events[id] = nullptr;
432 --event_count;
433 }
434
435 vapi_ctx_t vapi_ctx;
436 std::atomic_ulong req_context_counter;
437 std::mutex dispatch_mutex;
438
439 std::recursive_mutex requests_mutex;
440 std::recursive_mutex events_mutex;
441 std::deque<Common_req *> requests;
442 std::vector<Common_req *> events;
443 int event_count;
444
445 template <typename Req, typename Resp, typename... Args>
446 friend class Request;
447
448 template <typename Req, typename Resp, typename... Args> friend class Dump;
449
450 template <typename M> friend class Result_set;
451
452 template <typename M> friend class Event_registration;
453
454 template <typename M, typename... Args>
455 friend M *vapi_alloc (Connection &con, Args...);
456
457 template <typename M> friend class Msg;
458
459#if VAPI_CPP_DEBUG_LEAKS
460 void on_shm_data_alloc (void *shm_data)
461 {
462 if (shm_data)
463 {
464 auto pos = shm_data_set.find (shm_data);
465 if (pos == shm_data_set.end ())
466 {
467 shm_data_set.insert (shm_data);
468 }
469 else
470 {
471 printf ("Double-add shm_data @%p!\n", shm_data);
472 }
473 }
474 }
475
476 void on_shm_data_free (void *shm_data)
477 {
478 auto pos = shm_data_set.find (shm_data);
479 if (pos == shm_data_set.end ())
480 {
481 printf ("Freeing untracked shm_data @%p!\n", shm_data);
482 }
483 else
484 {
485 shm_data_set.erase (pos);
486 }
487 }
488 std::unordered_set<void *> shm_data_set;
489#endif
490};
491
492template <typename Req, typename Resp, typename... Args> class Request;
493
494template <typename Req, typename Resp, typename... Args> class Dump;
495
496template <class, class = void> struct vapi_has_payload_trait : std::false_type
497{
498};
499
500template <class... T> using vapi_void_t = void;
501
502template <class T>
503struct vapi_has_payload_trait<T, vapi_void_t<decltype (&T::payload)>>
504 : std::true_type
505{
506};
507
508template <typename M> void vapi_msg_set_msg_id (vapi_msg_id_t id)
509{
510 Msg<M>::set_msg_id (id);
511}
512
513/**
514 * Class representing a message stored in shared memory
515 */
516template <typename M> class Msg
517{
518public:
519 Msg (const Msg &) = delete;
520
521 ~Msg ()
522 {
523 VAPI_DBG ("Destroy Msg<%s>@%p, shm_data@%p",
524 vapi_get_msg_name (get_msg_id ()), this, shm_data);
525 if (shm_data)
526 {
527 con.get ().msg_free (shm_data);
528 shm_data = nullptr;
529 }
530 }
531
532 static vapi_msg_id_t get_msg_id ()
533 {
534 return *msg_id_holder ();
535 }
536
537 template <typename X = M>
538 typename std::enable_if<vapi_has_payload_trait<X>::value,
539 decltype (X::payload) &>::type
540 get_payload () const
541 {
542 return shm_data->payload;
543 }
544
545private:
546 Msg (Msg<M> &&msg) : con{msg.con}
547 {
548 VAPI_DBG ("Move construct Msg<%s> from msg@%p to msg@%p, shm_data@%p",
549 vapi_get_msg_name (get_msg_id ()), &msg, this, msg.shm_data);
550 shm_data = msg.shm_data;
551 msg.shm_data = nullptr;
552 }
553
554 Msg<M> &operator= (Msg<M> &&msg)
555 {
556 VAPI_DBG ("Move assign Msg<%s> from msg@%p to msg@%p, shm_data@%p",
557 vapi_get_msg_name (get_msg_id ()), &msg, this, msg.shm_data);
558 con.get ().msg_free (shm_data);
559 con = msg.con;
560 shm_data = msg.shm_data;
561 msg.shm_data = nullptr;
562 return *this;
563 }
564
565 struct Msg_allocator : std::allocator<Msg<M>>
566 {
567 template <class U, class... Args> void construct (U *p, Args &&... args)
568 {
569 ::new ((void *)p) U (std::forward<Args> (args)...);
570 }
571
572 template <class U> struct rebind
573 {
574 typedef Msg_allocator other;
575 };
576 };
577
578 static void set_msg_id (vapi_msg_id_t id)
579 {
580 assert ((~0 == *msg_id_holder ()) || (id == *msg_id_holder ()));
581 *msg_id_holder () = id;
582 }
583
584 static vapi_msg_id_t *msg_id_holder ()
585 {
586 static vapi_msg_id_t my_id{~0};
587 return &my_id;
588 }
589
Klement Sekera905e2502017-09-22 07:52:28 +0200590 Msg (Connection &con, void *shm_data) : con{con}
Klement Sekeradc15be22017-06-12 06:49:33 +0200591 {
592 if (!con.is_msg_available (get_msg_id ()))
593 {
594 throw Msg_not_available_exception ();
595 }
596 this->shm_data = static_cast<shm_data_type *> (shm_data);
597 VAPI_DBG ("New Msg<%s>@%p shm_data@%p", vapi_get_msg_name (get_msg_id ()),
598 this, shm_data);
599 }
600
Klement Sekera905e2502017-09-22 07:52:28 +0200601 void assign_response (vapi_msg_id_t resp_id, void *shm_data)
Klement Sekeradc15be22017-06-12 06:49:33 +0200602 {
603 assert (nullptr == this->shm_data);
604 if (resp_id != get_msg_id ())
605 {
606 throw Unexpected_msg_id_exception ();
607 }
608 this->shm_data = static_cast<M *> (shm_data);
609 vapi_swap_to_host<M> (this->shm_data);
610 VAPI_DBG ("Assign response to Msg<%s>@%p shm_data@%p",
611 vapi_get_msg_name (get_msg_id ()), this, shm_data);
612 }
613
614 std::reference_wrapper<Connection> con;
615 using shm_data_type = M;
616 shm_data_type *shm_data;
617
618 friend class Connection;
619
620 template <typename Req, typename Resp, typename... Args>
621 friend class Request;
622
623 template <typename Req, typename Resp, typename... Args> friend class Dump;
624
625 template <typename X> friend class Event_registration;
626
627 template <typename X> friend class Result_set;
628
629 friend struct Msg_allocator;
630
631 template <typename X> friend void vapi_msg_set_msg_id (vapi_msg_id_t id);
632};
633
634/**
635 * Class representing a simple request - with a single response message
636 */
637template <typename Req, typename Resp, typename... Args>
638class Request : public Common_req
639{
640public:
641 Request (Connection &con, Args... args,
642 std::function<vapi_error_e (Request<Req, Resp, Args...> &)>
643 callback = nullptr)
644 : Common_req{con}, callback{callback},
645 request{con, vapi_alloc<Req> (con, args...)}, response{con, nullptr}
646 {
647 }
648
649 Request (const Request &) = delete;
650
651 virtual ~Request ()
652 {
653 if (RESPONSE_NOT_READY == get_response_state ())
654 {
655 con.unregister_request (this);
656 }
657 }
658
659 vapi_error_e execute ()
660 {
661 return con.send (this);
662 }
663
664 const Msg<Req> &get_request (void) const
665 {
666 return request;
667 }
668
669 const Msg<Resp> &get_response (void)
670 {
671 return response;
672 }
673
674private:
675 virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id,
676 void *shm_data)
677 {
678 assert (RESPONSE_NOT_READY == get_response_state ());
679 response.assign_response (id, shm_data);
680 set_response_state (RESPONSE_READY);
681 if (nullptr != callback)
682 {
683 return std::make_pair (callback (*this), true);
684 }
685 return std::make_pair (VAPI_OK, true);
686 }
687 std::function<vapi_error_e (Request<Req, Resp, Args...> &)> callback;
688 Msg<Req> request;
689 Msg<Resp> response;
690
691 friend class Connection;
692};
693
694/**
695 * Class representing iterable set of responses of the same type
696 */
697template <typename M> class Result_set
698{
699public:
700 ~Result_set ()
701 {
702 }
703
704 Result_set (const Result_set &) = delete;
705
706 bool is_complete () const
707 {
708 return complete;
709 }
710
711 size_t size () const
712 {
713 return set.size ();
714 }
715
716 using const_iterator =
717 typename std::vector<Msg<M>,
718 typename Msg<M>::Msg_allocator>::const_iterator;
719
720 const_iterator begin () const
721 {
722 return set.begin ();
723 }
724
725 const_iterator end () const
726 {
727 return set.end ();
728 }
729
730 void free_response (const_iterator pos)
731 {
732 set.erase (pos);
733 }
734
735 void free_all_responses ()
736 {
737 set.clear ();
738 }
739
740private:
741 void mark_complete ()
742 {
743 complete = true;
744 }
745
Klement Sekera905e2502017-09-22 07:52:28 +0200746 void assign_response (vapi_msg_id_t resp_id, void *shm_data)
Klement Sekeradc15be22017-06-12 06:49:33 +0200747 {
748 if (resp_id != Msg<M>::get_msg_id ())
749 {
750 {
751 throw Unexpected_msg_id_exception ();
752 }
753 }
754 else if (shm_data)
755 {
756 vapi_swap_to_host<M> (static_cast<M *> (shm_data));
757 set.emplace_back (con, shm_data);
758 VAPI_DBG ("Result_set@%p emplace_back shm_data@%p", this, shm_data);
759 }
760 }
761
762 Result_set (Connection &con) : con{con}, complete{false}
763 {
764 }
765
766 Connection &con;
767 bool complete;
768 std::vector<Msg<M>, typename Msg<M>::Msg_allocator> set;
769
770 template <typename Req, typename Resp, typename... Args> friend class Dump;
771
772 template <typename X> friend class Event_registration;
773};
774
775/**
776 * Class representing a dump request - zero or more identical responses to a
777 * single request message
778 */
779template <typename Req, typename Resp, typename... Args>
780class Dump : public Common_req
781{
782public:
783 Dump (Connection &con, Args... args,
784 std::function<vapi_error_e (Dump<Req, Resp, Args...> &)> callback =
785 nullptr)
786 : Common_req{con}, request{con, vapi_alloc<Req> (con, args...)},
787 result_set{con}, callback{callback}
788 {
789 }
790
791 Dump (const Dump &) = delete;
792
793 virtual ~Dump ()
794 {
795 }
796
797 virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id,
798 void *shm_data)
799 {
800 if (id == vapi_msg_id_control_ping_reply)
801 {
802 con.msg_free (shm_data);
803 result_set.mark_complete ();
804 set_response_state (RESPONSE_READY);
805 if (nullptr != callback)
806 {
807 return std::make_pair (callback (*this), true);
808 }
809 return std::make_pair (VAPI_OK, true);
810 }
811 else
812 {
813 result_set.assign_response (id, shm_data);
814 }
815 return std::make_pair (VAPI_OK, false);
816 }
817
818 vapi_error_e execute ()
819 {
820 return con.send_with_control_ping (this);
821 }
822
823 Msg<Req> &get_request (void)
824 {
825 return request;
826 }
827
828 using resp_type = typename Msg<Resp>::shm_data_type;
829
830 const Result_set<Resp> &get_result_set (void) const
831 {
832 return result_set;
833 }
834
835private:
836 Msg<Req> request;
837 Result_set<resp_type> result_set;
838 std::function<vapi_error_e (Dump<Req, Resp, Args...> &)> callback;
839
840 friend class Connection;
841};
842
843/**
844 * Class representing event registration - incoming events (messages) from
845 * vpp as a result of a subscription (typically a want_* simple request)
846 */
847template <typename M> class Event_registration : public Common_req
848{
849public:
850 Event_registration (
851 Connection &con,
Klement Sekera905e2502017-09-22 07:52:28 +0200852 std::function<vapi_error_e (Event_registration<M> &)> callback = nullptr)
Klement Sekeradc15be22017-06-12 06:49:33 +0200853 : Common_req{con}, result_set{con}, callback{callback}
854 {
855 if (!con.is_msg_available (M::get_msg_id ()))
856 {
857 throw Msg_not_available_exception ();
858 }
859 con.register_event (this);
860 }
861
862 Event_registration (const Event_registration &) = delete;
863
864 virtual ~Event_registration ()
865 {
866 con.unregister_event (this);
867 }
868
869 virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id,
870 void *shm_data)
871 {
872 result_set.assign_response (id, shm_data);
873 if (nullptr != callback)
874 {
875 return std::make_pair (callback (*this), true);
876 }
877 return std::make_pair (VAPI_OK, true);
878 }
879
880 using resp_type = typename M::shm_data_type;
881
882 Result_set<resp_type> &get_result_set (void)
883 {
884 return result_set;
885 }
886
887private:
888 Result_set<resp_type> result_set;
889 std::function<vapi_error_e (Event_registration<M> &)> callback;
890};
891};
892
893#endif
894
895/*
896 * fd.io coding-style-patch-verification: ON
897 *
898 * Local Variables:
899 * eval: (c-set-style "gnu")
900 * End:
901 */