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