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