Line data Source code
1 : /*
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 :
44 : namespace vapi
45 : {
46 :
47 : class Connection;
48 :
49 : template <typename Req, typename Resp, typename... Args> class Request;
50 : template <typename M> class Msg;
51 : template <typename M> void vapi_swap_to_be (M *msg);
52 : template <typename M> void vapi_swap_to_host (M *msg);
53 : template <typename M, typename... Args>
54 : M *vapi_alloc (Connection &con, Args...);
55 : template <typename M> vapi_msg_id_t vapi_get_msg_id_t ();
56 : template <typename M> class Event_registration;
57 :
58 : class Unexpected_msg_id_exception : public std::exception
59 : {
60 : public:
61 0 : virtual const char *what () const throw ()
62 : {
63 0 : return "unexpected message id";
64 : }
65 : };
66 :
67 : class Msg_not_available_exception : public std::exception
68 : {
69 : public:
70 0 : virtual const char *what () const throw ()
71 : {
72 0 : return "message unavailable";
73 : }
74 : };
75 :
76 : typedef 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 : */
91 : class Common_req
92 : {
93 : public:
94 29 : virtual ~Common_req (){};
95 :
96 : Connection &get_connection ()
97 : {
98 : return con;
99 : };
100 :
101 59 : vapi_response_state_e get_response_state (void) const
102 : {
103 59 : return response_state;
104 : }
105 :
106 : private:
107 : Connection &con;
108 29 : Common_req (Connection &con)
109 29 : : con (con), context{0}, response_state{RESPONSE_NOT_READY}
110 : {
111 29 : }
112 :
113 26 : void set_response_state (vapi_response_state_e state)
114 : {
115 26 : response_state = state;
116 26 : }
117 :
118 : virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id,
119 : void *shm_data) = 0;
120 :
121 26 : void set_context (u32 context)
122 : {
123 26 : this->context = context;
124 26 : }
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 : */
153 : class Connection
154 : {
155 : public:
156 6 : Connection (void) : vapi_ctx{0}, event_count{0}
157 : {
158 :
159 6 : vapi_error_e rv = VAPI_OK;
160 6 : if (!vapi_ctx)
161 : {
162 6 : if (VAPI_OK != (rv = vapi_ctx_alloc (&vapi_ctx)))
163 : {
164 0 : throw std::bad_alloc ();
165 : }
166 : }
167 6 : events.reserve (vapi_get_message_count () + 1);
168 6 : }
169 :
170 : Connection (const Connection &) = delete;
171 :
172 6 : ~Connection (void)
173 6 : {
174 6 : 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 6 : }
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 65 : bool is_msg_available (vapi_msg_id_t type)
188 : {
189 65 : 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 : * @param handle_keepalives handle memclnt_keepalive automatically
199 : *
200 : * @return VAPI_OK on success, other error code on error
201 : */
202 5 : vapi_error_e connect (const char *name, const char *chroot_prefix,
203 : int max_outstanding_requests, int response_queue_size,
204 : bool handle_keepalives = true)
205 : {
206 5 : return vapi_connect (vapi_ctx, name, chroot_prefix,
207 : max_outstanding_requests, response_queue_size,
208 5 : VAPI_MODE_BLOCKING, handle_keepalives);
209 : }
210 :
211 : /**
212 : * @brief disconnect from vpp
213 : *
214 : * @return VAPI_OK on success, other error code on error
215 : */
216 5 : vapi_error_e disconnect ()
217 : {
218 5 : auto x = requests.size ();
219 5 : while (x > 0)
220 : {
221 : VAPI_DBG ("popping request @%p", requests.front ());
222 0 : requests.pop_front ();
223 0 : --x;
224 : }
225 5 : 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 : */
250 18 : vapi_error_e dispatch (const Common_req *limit = nullptr, u32 time = 5)
251 : {
252 36 : std::lock_guard<std::mutex> lock (dispatch_mutex);
253 18 : vapi_error_e rv = VAPI_OK;
254 18 : bool loop_again = true;
255 42 : while (loop_again)
256 : {
257 : void *shm_data;
258 : size_t shm_data_size;
259 40 : rv = vapi_recv (vapi_ctx, &shm_data, &shm_data_size, SVM_Q_TIMEDWAIT,
260 : time);
261 40 : if (VAPI_OK != rv)
262 : {
263 16 : return rv;
264 : }
265 : #if VAPI_CPP_DEBUG_LEAKS
266 : on_shm_data_alloc (shm_data);
267 : #endif
268 40 : std::lock_guard<std::recursive_mutex> requests_lock (requests_mutex);
269 40 : std::lock_guard<std::recursive_mutex> events_lock (events_mutex);
270 40 : vapi_msg_id_t id = vapi_lookup_vapi_msg_id_t (
271 40 : vapi_ctx, be16toh (*static_cast<u16 *> (shm_data)));
272 40 : bool has_context = vapi_msg_is_with_context (id);
273 40 : bool break_dispatch = false;
274 40 : Common_req *matching_req = nullptr;
275 40 : if (has_context)
276 : {
277 80 : u32 context = *reinterpret_cast<u32 *> (
278 40 : (static_cast<u8 *> (shm_data) + vapi_get_context_offset (id)));
279 40 : const auto x = requests.front ();
280 40 : matching_req = x;
281 40 : if (context == x->context)
282 : {
283 40 : std::tie (rv, break_dispatch) =
284 80 : x->assign_response (id, shm_data);
285 : }
286 : else
287 : {
288 0 : std::tie (rv, break_dispatch) =
289 0 : x->assign_response (id, nullptr);
290 : }
291 40 : if (break_dispatch)
292 : {
293 26 : requests.pop_front ();
294 : }
295 : }
296 : else
297 : {
298 0 : if (events[id])
299 : {
300 0 : std::tie (rv, break_dispatch) =
301 0 : events[id]->assign_response (id, shm_data);
302 0 : matching_req = events[id];
303 : }
304 : else
305 : {
306 0 : msg_free (shm_data);
307 : }
308 : }
309 40 : if ((matching_req && matching_req == limit && break_dispatch) ||
310 24 : VAPI_OK != rv)
311 : {
312 16 : return rv;
313 : }
314 24 : loop_again = !requests.empty () || (event_count > 0);
315 : }
316 2 : return rv;
317 : }
318 :
319 : /**
320 : * @brief convenience wrapper function
321 : */
322 16 : vapi_error_e dispatch (const Common_req &limit)
323 : {
324 16 : 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 15 : vapi_error_e wait_for_response (const Common_req &req)
335 : {
336 15 : if (RESPONSE_READY == req.get_response_state ())
337 : {
338 0 : return VAPI_OK;
339 : }
340 15 : return dispatch (req);
341 : }
342 :
343 : private:
344 40 : void msg_free (void *shm_data)
345 : {
346 : #if VAPI_CPP_DEBUG_LEAKS
347 : on_shm_data_free (shm_data);
348 : #endif
349 40 : vapi_msg_free (vapi_ctx, shm_data);
350 40 : }
351 :
352 : template <template <typename XReq, typename XResp, typename... XArgs>
353 : class X,
354 : typename Req, typename Resp, typename... Args>
355 22 : vapi_error_e send (X<Req, Resp, Args...> *req)
356 : {
357 22 : if (!req)
358 : {
359 0 : return VAPI_EINVAL;
360 : }
361 22 : u32 req_context =
362 22 : req_context_counter.fetch_add (1, std::memory_order_relaxed);
363 22 : req->request.shm_data->header.context = req_context;
364 22 : vapi_swap_to_be<Req> (req->request.shm_data);
365 22 : std::lock_guard<std::recursive_mutex> lock (requests_mutex);
366 22 : vapi_error_e rv = vapi_send (vapi_ctx, req->request.shm_data);
367 22 : if (VAPI_OK == rv)
368 : {
369 : VAPI_DBG ("Push %p", req);
370 22 : requests.emplace_back (req);
371 22 : req->set_context (req_context);
372 : #if VAPI_CPP_DEBUG_LEAKS
373 : on_shm_data_free (req->request.shm_data);
374 : #endif
375 22 : req->request.shm_data = nullptr; /* consumed by vapi_send */
376 : }
377 : else
378 : {
379 0 : vapi_swap_to_host<Req> (req->request.shm_data);
380 : }
381 22 : return rv;
382 : }
383 :
384 : template <template <typename XReq, typename XResp, typename... XArgs>
385 : class X,
386 : typename Req, typename Resp, typename... Args>
387 4 : vapi_error_e send_with_control_ping (X<Req, Resp, Args...> *req)
388 : {
389 4 : if (!req)
390 : {
391 0 : return VAPI_EINVAL;
392 : }
393 4 : u32 req_context =
394 4 : req_context_counter.fetch_add (1, std::memory_order_relaxed);
395 4 : req->request.shm_data->header.context = req_context;
396 4 : vapi_swap_to_be<Req> (req->request.shm_data);
397 4 : std::lock_guard<std::recursive_mutex> lock (requests_mutex);
398 8 : vapi_error_e rv = vapi_send_with_control_ping (
399 4 : vapi_ctx, req->request.shm_data, req_context);
400 4 : if (VAPI_OK == rv)
401 : {
402 : VAPI_DBG ("Push %p", req);
403 4 : requests.emplace_back (req);
404 4 : req->set_context (req_context);
405 : #if VAPI_CPP_DEBUG_LEAKS
406 : on_shm_data_free (req->request.shm_data);
407 : #endif
408 4 : req->request.shm_data = nullptr; /* consumed by vapi_send */
409 : }
410 : else
411 : {
412 0 : vapi_swap_to_host<Req> (req->request.shm_data);
413 : }
414 4 : return rv;
415 : }
416 :
417 0 : void unregister_request (Common_req *request)
418 : {
419 0 : std::lock_guard<std::recursive_mutex> lock (requests_mutex);
420 0 : std::remove (requests.begin (), requests.end (), request);
421 0 : }
422 :
423 0 : template <typename M> void register_event (Event_registration<M> *event)
424 : {
425 0 : const vapi_msg_id_t id = M::get_msg_id ();
426 0 : std::lock_guard<std::recursive_mutex> lock (events_mutex);
427 0 : events[id] = event;
428 0 : ++event_count;
429 0 : }
430 :
431 0 : template <typename M> void unregister_event (Event_registration<M> *event)
432 : {
433 0 : const vapi_msg_id_t id = M::get_msg_id ();
434 0 : std::lock_guard<std::recursive_mutex> lock (events_mutex);
435 0 : events[id] = nullptr;
436 0 : --event_count;
437 0 : }
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 :
496 : template <typename Req, typename Resp, typename... Args> class Request;
497 :
498 : template <typename Req, typename Resp, typename... Args> class Dump;
499 :
500 : template <class, class = void> struct vapi_has_payload_trait : std::false_type
501 : {
502 : };
503 :
504 : template <class... T> using vapi_void_t = void;
505 :
506 : template <class T>
507 : struct vapi_has_payload_trait<T, vapi_void_t<decltype (&T::payload)>>
508 : : std::true_type
509 : {
510 : };
511 :
512 474 : template <typename M> void vapi_msg_set_msg_id (vapi_msg_id_t id)
513 : {
514 474 : Msg<M>::set_msg_id (id);
515 474 : }
516 :
517 : /**
518 : * Class representing a message stored in shared memory
519 : */
520 : template <typename M> class Msg
521 : {
522 : public:
523 : Msg (const Msg &) = delete;
524 :
525 76 : ~Msg ()
526 : {
527 : VAPI_DBG ("Destroy Msg<%s>@%p, shm_data@%p",
528 : vapi_get_msg_name (get_msg_id ()), this, shm_data);
529 76 : if (shm_data)
530 : {
531 36 : con.get ().msg_free (shm_data);
532 36 : shm_data = nullptr;
533 : }
534 76 : }
535 :
536 101 : static vapi_msg_id_t get_msg_id ()
537 : {
538 101 : 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 56 : get_payload () const
545 : {
546 56 : return shm_data->payload;
547 : }
548 :
549 : private:
550 14 : 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 14 : shm_data = msg.shm_data;
555 14 : msg.shm_data = nullptr;
556 14 : }
557 :
558 0 : 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 0 : con.get ().msg_free (shm_data);
563 0 : con = msg.con;
564 0 : shm_data = msg.shm_data;
565 0 : msg.shm_data = nullptr;
566 0 : return *this;
567 : }
568 :
569 : struct Msg_allocator : std::allocator<Msg<M>>
570 : {
571 28 : template <class U, class... Args> void construct (U *p, Args &&... args)
572 : {
573 28 : ::new ((void *)p) U (std::forward<Args> (args)...);
574 28 : }
575 :
576 : template <class U> struct rebind
577 : {
578 : typedef Msg_allocator other;
579 : };
580 : };
581 :
582 474 : static void set_msg_id (vapi_msg_id_t id)
583 : {
584 474 : assert ((VAPI_INVALID_MSG_ID == *msg_id_holder ()) ||
585 : (id == *msg_id_holder ()));
586 474 : *msg_id_holder () = id;
587 474 : }
588 :
589 1049 : static vapi_msg_id_t *msg_id_holder ()
590 : {
591 : static vapi_msg_id_t my_id{VAPI_INVALID_MSG_ID};
592 1049 : return &my_id;
593 : }
594 :
595 64 : Msg (Connection &con, void *shm_data) : con{con}
596 : {
597 64 : if (!con.is_msg_available (get_msg_id ()))
598 : {
599 2 : throw Msg_not_available_exception ();
600 : }
601 62 : 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 62 : }
605 :
606 22 : void assign_response (vapi_msg_id_t resp_id, void *shm_data)
607 : {
608 22 : assert (nullptr == this->shm_data);
609 22 : if (resp_id != get_msg_id ())
610 : {
611 0 : throw Unexpected_msg_id_exception ();
612 : }
613 22 : this->shm_data = static_cast<M *> (shm_data);
614 22 : 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 22 : }
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 : */
642 : template <typename Req, typename Resp, typename... Args>
643 : class Request : public Common_req
644 : {
645 : public:
646 23 : 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 24 : request{con, vapi_alloc<Req> (con, args...)}, response{con, nullptr}
651 : {
652 22 : }
653 :
654 : Request (const Request &) = delete;
655 :
656 32 : virtual ~Request ()
657 : {
658 22 : if (RESPONSE_NOT_READY == get_response_state ())
659 : {
660 0 : con.unregister_request (this);
661 : }
662 54 : }
663 :
664 22 : vapi_error_e execute ()
665 : {
666 22 : return con.send (this);
667 : }
668 :
669 20 : const Msg<Req> &get_request (void) const
670 : {
671 20 : return request;
672 : }
673 :
674 22 : const Msg<Resp> &get_response (void)
675 : {
676 22 : return response;
677 : }
678 :
679 : private:
680 22 : virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id,
681 : void *shm_data)
682 : {
683 22 : assert (RESPONSE_NOT_READY == get_response_state ());
684 22 : response.assign_response (id, shm_data);
685 22 : set_response_state (RESPONSE_READY);
686 22 : if (nullptr != callback)
687 : {
688 22 : return std::make_pair (callback (*this), true);
689 : }
690 22 : 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 : */
702 : template <typename M> class Result_set
703 : {
704 : public:
705 5 : ~Result_set ()
706 : {
707 5 : }
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 4 : const_iterator begin () const
726 : {
727 4 : return set.begin ();
728 : }
729 :
730 4 : const_iterator end () const
731 : {
732 4 : 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 :
745 : private:
746 4 : void mark_complete ()
747 : {
748 4 : complete = true;
749 4 : }
750 :
751 14 : void assign_response (vapi_msg_id_t resp_id, void *shm_data)
752 : {
753 14 : if (resp_id != Msg<M>::get_msg_id ())
754 : {
755 : {
756 0 : throw Unexpected_msg_id_exception ();
757 : }
758 : }
759 14 : else if (shm_data)
760 : {
761 14 : vapi_swap_to_host<M> (static_cast<M *> (shm_data));
762 14 : set.emplace_back (con, shm_data);
763 : VAPI_DBG ("Result_set@%p emplace_back shm_data@%p", this, shm_data);
764 : }
765 14 : }
766 :
767 5 : Result_set (Connection &con) : con (con), complete{false}
768 : {
769 5 : }
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 : */
784 : template <typename Req, typename Resp, typename... Args>
785 : class Dump : public Common_req
786 : {
787 : public:
788 5 : Dump (Connection &con, Args... args,
789 : std::function<vapi_error_e (Dump<Req, Resp, Args...> &)> callback =
790 : nullptr)
791 10 : : Common_req{con}, request{con, vapi_alloc<Req> (con, args...)},
792 5 : result_set{con}, callback{callback}
793 : {
794 4 : }
795 :
796 : Dump (const Dump &) = delete;
797 :
798 4 : virtual ~Dump ()
799 : {
800 4 : }
801 :
802 18 : virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id,
803 : void *shm_data)
804 : {
805 18 : if (id == vapi_msg_id_control_ping_reply)
806 : {
807 4 : con.msg_free (shm_data);
808 4 : result_set.mark_complete ();
809 4 : set_response_state (RESPONSE_READY);
810 4 : if (nullptr != callback)
811 : {
812 2 : return std::make_pair (callback (*this), true);
813 : }
814 6 : return std::make_pair (VAPI_OK, true);
815 : }
816 : else
817 : {
818 14 : result_set.assign_response (id, shm_data);
819 : }
820 28 : return std::make_pair (VAPI_OK, false);
821 : }
822 :
823 4 : vapi_error_e execute ()
824 : {
825 4 : return con.send_with_control_ping (this);
826 : }
827 :
828 0 : Msg<Req> &get_request (void)
829 : {
830 0 : return request;
831 : }
832 :
833 : using resp_type = typename Msg<Resp>::shm_data_type;
834 :
835 4 : const Result_set<Resp> &get_result_set (void) const
836 : {
837 4 : return result_set;
838 : }
839 :
840 : private:
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 : */
852 : template <typename M> class Event_registration : public Common_req
853 : {
854 : public:
855 1 : Event_registration (
856 : Connection &con,
857 : std::function<vapi_error_e (Event_registration<M> &)> callback = nullptr)
858 4 : : Common_req{con}, result_set{con}, callback{callback}
859 : {
860 1 : if (!con.is_msg_available (M::get_msg_id ()))
861 : {
862 1 : throw Msg_not_available_exception ();
863 : }
864 0 : con.register_event (this);
865 0 : }
866 :
867 : Event_registration (const Event_registration &) = delete;
868 :
869 0 : virtual ~Event_registration ()
870 : {
871 0 : con.unregister_event (this);
872 0 : }
873 :
874 0 : virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id,
875 : void *shm_data)
876 : {
877 0 : result_set.assign_response (id, shm_data);
878 0 : if (nullptr != callback)
879 : {
880 0 : return std::make_pair (callback (*this), true);
881 : }
882 0 : 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 :
892 : private:
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 : */
|