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