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