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 35 : virtual ~Common_req (){};
95 :
96 : Connection &get_connection ()
97 : {
98 : return con;
99 : };
100 :
101 75 : vapi_response_state_e get_response_state (void) const
102 : {
103 75 : return response_state;
104 : }
105 :
106 : private:
107 : Connection &con;
108 35 : Common_req (Connection &con)
109 35 : : con (con), context{0}, response_state{RESPONSE_NOT_READY}
110 : {
111 35 : }
112 :
113 32 : void set_response_state (vapi_response_state_e state)
114 : {
115 32 : response_state = state;
116 32 : }
117 :
118 : virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id,
119 : void *shm_data) = 0;
120 :
121 32 : void set_context (u32 context)
122 : {
123 32 : this->context = context;
124 32 : }
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 Req, typename Resp, typename StreamMessage,
144 : typename... Args>
145 : friend class Stream;
146 :
147 : 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 : */
157 : class Connection
158 : {
159 : public:
160 6 : Connection (void) : vapi_ctx{0}, event_count{0}
161 : {
162 :
163 6 : vapi_error_e rv = VAPI_OK;
164 6 : if (!vapi_ctx)
165 : {
166 6 : if (VAPI_OK != (rv = vapi_ctx_alloc (&vapi_ctx)))
167 : {
168 0 : throw std::bad_alloc ();
169 : }
170 : }
171 6 : events.reserve (vapi_get_message_count () + 1);
172 6 : }
173 :
174 : Connection (const Connection &) = delete;
175 :
176 6 : ~Connection (void)
177 6 : {
178 6 : 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 6 : }
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 82 : bool is_msg_available (vapi_msg_id_t type)
192 : {
193 82 : 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
202 : * @param handle_keepalives handle memclnt_keepalive automatically
203 : *
204 : * @return VAPI_OK on success, other error code on error
205 : */
206 5 : vapi_error_e connect (const char *name, const char *chroot_prefix,
207 : int max_outstanding_requests, int response_queue_size,
208 : bool handle_keepalives = true)
209 : {
210 5 : return vapi_connect (vapi_ctx, name, chroot_prefix,
211 : max_outstanding_requests, response_queue_size,
212 5 : VAPI_MODE_BLOCKING, handle_keepalives);
213 : }
214 :
215 : /**
216 : * @brief disconnect from vpp
217 : *
218 : * @return VAPI_OK on success, other error code on error
219 : */
220 5 : vapi_error_e disconnect ()
221 : {
222 5 : auto x = requests.size ();
223 5 : while (x > 0)
224 : {
225 : VAPI_DBG ("popping request @%p", requests.front ());
226 0 : requests.pop_front ();
227 0 : --x;
228 : }
229 5 : 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 : */
254 24 : vapi_error_e dispatch (const Common_req *limit = nullptr, u32 time = 5)
255 : {
256 48 : std::lock_guard<std::mutex> lock (dispatch_mutex);
257 24 : vapi_error_e rv = VAPI_OK;
258 24 : bool loop_again = true;
259 53 : while (loop_again)
260 : {
261 : void *shm_data;
262 : size_t shm_data_size;
263 51 : rv = vapi_recv (vapi_ctx, &shm_data, &shm_data_size, SVM_Q_TIMEDWAIT,
264 : time);
265 51 : if (VAPI_OK != rv)
266 : {
267 22 : return rv;
268 : }
269 : #if VAPI_CPP_DEBUG_LEAKS
270 : on_shm_data_alloc (shm_data);
271 : #endif
272 51 : std::lock_guard<std::recursive_mutex> requests_lock (requests_mutex);
273 51 : std::lock_guard<std::recursive_mutex> events_lock (events_mutex);
274 51 : vapi_msg_id_t id = vapi_lookup_vapi_msg_id_t (
275 51 : vapi_ctx, be16toh (*static_cast<u16 *> (shm_data)));
276 51 : bool has_context = vapi_msg_is_with_context (id);
277 51 : bool break_dispatch = false;
278 51 : Common_req *matching_req = nullptr;
279 51 : if (has_context)
280 : {
281 102 : u32 context = *reinterpret_cast<u32 *> (
282 51 : (static_cast<u8 *> (shm_data) + vapi_get_context_offset (id)));
283 51 : const auto x = requests.front ();
284 51 : matching_req = x;
285 51 : if (context == x->context)
286 : {
287 51 : std::tie (rv, break_dispatch) =
288 102 : x->assign_response (id, shm_data);
289 : }
290 : else
291 : {
292 0 : std::tie (rv, break_dispatch) =
293 0 : x->assign_response (id, nullptr);
294 : }
295 51 : if (break_dispatch)
296 : {
297 32 : requests.pop_front ();
298 : }
299 : }
300 : else
301 : {
302 0 : if (events[id])
303 : {
304 0 : std::tie (rv, break_dispatch) =
305 0 : events[id]->assign_response (id, shm_data);
306 0 : matching_req = events[id];
307 : }
308 : else
309 : {
310 0 : msg_free (shm_data);
311 : }
312 : }
313 51 : if ((matching_req && matching_req == limit && break_dispatch) ||
314 29 : VAPI_OK != rv)
315 : {
316 22 : return rv;
317 : }
318 29 : loop_again = !requests.empty () || (event_count > 0);
319 : }
320 2 : return rv;
321 : }
322 :
323 : /**
324 : * @brief convenience wrapper function
325 : */
326 22 : vapi_error_e dispatch (const Common_req &limit)
327 : {
328 22 : 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 21 : vapi_error_e wait_for_response (const Common_req &req)
339 : {
340 21 : if (RESPONSE_READY == req.get_response_state ())
341 : {
342 0 : return VAPI_OK;
343 : }
344 21 : return dispatch (req);
345 : }
346 :
347 : private:
348 51 : void msg_free (void *shm_data)
349 : {
350 : #if VAPI_CPP_DEBUG_LEAKS
351 : on_shm_data_free (shm_data);
352 : #endif
353 51 : vapi_msg_free (vapi_ctx, shm_data);
354 51 : }
355 :
356 : template <template <typename XReq, typename XResp, typename... XArgs>
357 : class X,
358 : typename Req, typename Resp, typename... Args>
359 28 : vapi_error_e send (X<Req, Resp, Args...> *req)
360 : {
361 28 : if (!req)
362 : {
363 0 : return VAPI_EINVAL;
364 : }
365 28 : u32 req_context =
366 28 : req_context_counter.fetch_add (1, std::memory_order_relaxed);
367 28 : req->request.shm_data->header.context = req_context;
368 28 : vapi_swap_to_be<Req> (req->request.shm_data);
369 28 : std::lock_guard<std::recursive_mutex> lock (requests_mutex);
370 28 : vapi_error_e rv = vapi_send (vapi_ctx, req->request.shm_data);
371 28 : if (VAPI_OK == rv)
372 : {
373 : VAPI_DBG ("Push %p", req);
374 28 : requests.emplace_back (req);
375 28 : req->set_context (req_context);
376 : #if VAPI_CPP_DEBUG_LEAKS
377 : on_shm_data_free (req->request.shm_data);
378 : #endif
379 28 : req->request.shm_data = nullptr; /* consumed by vapi_send */
380 : }
381 : else
382 : {
383 0 : vapi_swap_to_host<Req> (req->request.shm_data);
384 : }
385 28 : return rv;
386 : }
387 :
388 : template <template <typename XReq, typename XResp, typename... XArgs>
389 : class X,
390 : typename Req, typename Resp, typename... Args>
391 4 : vapi_error_e send_with_control_ping (X<Req, Resp, Args...> *req)
392 : {
393 4 : if (!req)
394 : {
395 0 : return VAPI_EINVAL;
396 : }
397 4 : u32 req_context =
398 4 : req_context_counter.fetch_add (1, std::memory_order_relaxed);
399 4 : req->request.shm_data->header.context = req_context;
400 4 : vapi_swap_to_be<Req> (req->request.shm_data);
401 4 : std::lock_guard<std::recursive_mutex> lock (requests_mutex);
402 8 : vapi_error_e rv = vapi_send_with_control_ping (
403 4 : vapi_ctx, req->request.shm_data, req_context);
404 4 : if (VAPI_OK == rv)
405 : {
406 : VAPI_DBG ("Push %p", req);
407 4 : requests.emplace_back (req);
408 4 : req->set_context (req_context);
409 : #if VAPI_CPP_DEBUG_LEAKS
410 : on_shm_data_free (req->request.shm_data);
411 : #endif
412 4 : req->request.shm_data = nullptr; /* consumed by vapi_send */
413 : }
414 : else
415 : {
416 0 : vapi_swap_to_host<Req> (req->request.shm_data);
417 : }
418 4 : return rv;
419 : }
420 :
421 0 : void unregister_request (Common_req *request)
422 : {
423 0 : std::lock_guard<std::recursive_mutex> lock (requests_mutex);
424 0 : std::remove (requests.begin (), requests.end (), request);
425 0 : }
426 :
427 0 : template <typename M> void register_event (Event_registration<M> *event)
428 : {
429 0 : const vapi_msg_id_t id = M::get_msg_id ();
430 0 : std::lock_guard<std::recursive_mutex> lock (events_mutex);
431 0 : events[id] = event;
432 0 : ++event_count;
433 0 : }
434 :
435 0 : template <typename M> void unregister_event (Event_registration<M> *event)
436 : {
437 0 : const vapi_msg_id_t id = M::get_msg_id ();
438 0 : std::lock_guard<std::recursive_mutex> lock (events_mutex);
439 0 : events[id] = nullptr;
440 0 : --event_count;
441 0 : }
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 :
458 : template <typename Req, typename Resp, typename StreamMessage,
459 : typename... Args>
460 : friend class Stream;
461 :
462 : 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 :
504 : template <typename Req, typename Resp, typename... Args> class Request;
505 :
506 : template <typename Req, typename Resp, typename... Args> class Dump;
507 :
508 : template <typename Req, typename Resp, typename StreamMessage,
509 : typename... Args>
510 : class Stream;
511 :
512 : template <class, class = void> struct vapi_has_payload_trait : std::false_type
513 : {
514 : };
515 :
516 : template <class... T> using vapi_void_t = void;
517 :
518 : template <class T>
519 : struct vapi_has_payload_trait<T, vapi_void_t<decltype (&T::payload)>>
520 : : std::true_type
521 : {
522 : };
523 :
524 522 : template <typename M> void vapi_msg_set_msg_id (vapi_msg_id_t id)
525 : {
526 522 : Msg<M>::set_msg_id (id);
527 522 : }
528 :
529 : /**
530 : * Class representing a message stored in shared memory
531 : */
532 : template <typename M> class Msg
533 : {
534 : public:
535 : Msg (const Msg &) = delete;
536 :
537 100 : ~Msg ()
538 : {
539 : VAPI_DBG ("Destroy Msg<%s>@%p, shm_data@%p",
540 : vapi_get_msg_name (get_msg_id ()), this, shm_data);
541 100 : if (shm_data)
542 : {
543 47 : con.get ().msg_free (shm_data);
544 47 : shm_data = nullptr;
545 : }
546 100 : }
547 :
548 135 : static vapi_msg_id_t get_msg_id ()
549 : {
550 135 : 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 67 : get_payload () const
557 : {
558 67 : return shm_data->payload;
559 : }
560 :
561 : private:
562 21 : 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 21 : shm_data = msg.shm_data;
567 21 : msg.shm_data = nullptr;
568 21 : }
569 :
570 0 : 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 0 : con.get ().msg_free (shm_data);
575 0 : con = msg.con;
576 0 : shm_data = msg.shm_data;
577 0 : msg.shm_data = nullptr;
578 0 : return *this;
579 : }
580 :
581 : struct Msg_allocator : std::allocator<Msg<M>>
582 : {
583 40 : template <class U, class... Args> void construct (U *p, Args &&... args)
584 : {
585 40 : ::new ((void *)p) U (std::forward<Args> (args)...);
586 40 : }
587 :
588 : template <class U> struct rebind
589 : {
590 : typedef Msg_allocator other;
591 : };
592 : };
593 :
594 522 : static void set_msg_id (vapi_msg_id_t id)
595 : {
596 522 : assert ((VAPI_INVALID_MSG_ID == *msg_id_holder ()) ||
597 : (id == *msg_id_holder ()));
598 522 : *msg_id_holder () = id;
599 522 : }
600 :
601 1179 : static vapi_msg_id_t *msg_id_holder ()
602 : {
603 : static vapi_msg_id_t my_id{VAPI_INVALID_MSG_ID};
604 1179 : return &my_id;
605 : }
606 :
607 81 : Msg (Connection &con, void *shm_data) : con{con}
608 : {
609 81 : if (!con.is_msg_available (get_msg_id ()))
610 : {
611 2 : throw Msg_not_available_exception ();
612 : }
613 79 : 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 79 : }
617 :
618 28 : void assign_response (vapi_msg_id_t resp_id, void *shm_data)
619 : {
620 28 : assert (nullptr == this->shm_data);
621 28 : if (resp_id != get_msg_id ())
622 : {
623 0 : throw Unexpected_msg_id_exception ();
624 : }
625 28 : this->shm_data = static_cast<M *> (shm_data);
626 28 : 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 28 : }
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 :
642 : template <typename Req, typename Resp, typename StreamMessage,
643 : typename... Args>
644 : friend class Stream;
645 :
646 : 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 : */
658 : template <typename Req, typename Resp, typename... Args>
659 : class Request : public Common_req
660 : {
661 : public:
662 28 : Request (Connection &con, Args... args,
663 : std::function<vapi_error_e (Request<Req, Resp, Args...> &)>
664 : callback = nullptr)
665 28 : : Common_req{ con }, callback{ std::move (callback) },
666 56 : request{ con, vapi_alloc<Req> (con, args...) }, response{ con,
667 29 : nullptr }
668 : {
669 27 : }
670 :
671 : Request (const Request &) = delete;
672 :
673 37 : virtual ~Request ()
674 : {
675 27 : if (RESPONSE_NOT_READY == get_response_state ())
676 : {
677 0 : con.unregister_request (this);
678 : }
679 64 : }
680 :
681 27 : vapi_error_e execute ()
682 : {
683 27 : return con.send (this);
684 : }
685 :
686 25 : const Msg<Req> &get_request (void) const
687 : {
688 25 : return request;
689 : }
690 :
691 22 : const Msg<Resp> &get_response (void)
692 : {
693 22 : return response;
694 : }
695 :
696 : private:
697 27 : virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id,
698 : void *shm_data)
699 : {
700 27 : assert (RESPONSE_NOT_READY == get_response_state ());
701 27 : response.assign_response (id, shm_data);
702 27 : set_response_state (RESPONSE_READY);
703 27 : if (nullptr != callback)
704 : {
705 22 : return std::make_pair (callback (*this), true);
706 : }
707 32 : return std::make_pair (VAPI_OK, true);
708 : }
709 : std::function<vapi_error_e (Request<Req, Resp, Args...> &)> callback;
710 : Msg<Req> request;
711 : Msg<Resp> response;
712 :
713 : friend class Connection;
714 : };
715 :
716 : /**
717 : * Class representing iterable set of responses of the same type
718 : */
719 : template <typename M> class Result_set
720 : {
721 : public:
722 6 : ~Result_set ()
723 : {
724 6 : }
725 :
726 : Result_set (const Result_set &) = delete;
727 :
728 : bool is_complete () const
729 : {
730 : return complete;
731 : }
732 :
733 : size_t size () const
734 : {
735 : return set.size ();
736 : }
737 :
738 : using const_iterator =
739 : typename std::vector<Msg<M>,
740 : typename Msg<M>::Msg_allocator>::const_iterator;
741 :
742 5 : const_iterator begin () const
743 : {
744 5 : return set.begin ();
745 : }
746 :
747 5 : const_iterator end () const
748 : {
749 5 : return set.end ();
750 : }
751 :
752 : void free_response (const_iterator pos)
753 : {
754 : set.erase (pos);
755 : }
756 :
757 : void free_all_responses ()
758 : {
759 : set.clear ();
760 : }
761 :
762 : private:
763 5 : void mark_complete ()
764 : {
765 5 : complete = true;
766 5 : }
767 :
768 19 : void assign_response (vapi_msg_id_t resp_id, void *shm_data)
769 : {
770 19 : if (resp_id != Msg<M>::get_msg_id ())
771 : {
772 : {
773 0 : throw Unexpected_msg_id_exception ();
774 : }
775 : }
776 19 : else if (shm_data)
777 : {
778 19 : vapi_swap_to_host<M> (static_cast<M *> (shm_data));
779 19 : set.emplace_back (con, shm_data);
780 : VAPI_DBG ("Result_set@%p emplace_back shm_data@%p", this, shm_data);
781 : }
782 19 : }
783 :
784 6 : Result_set (Connection &con) : con (con), complete{false}
785 : {
786 6 : }
787 :
788 : Connection &con;
789 : bool complete;
790 : std::vector<Msg<M>, typename Msg<M>::Msg_allocator> set;
791 :
792 : template <typename Req, typename Resp, typename StreamMessage,
793 : typename... Args>
794 : friend class Stream;
795 :
796 : template <typename Req, typename Resp, typename... Args> friend class Dump;
797 :
798 : template <typename X> friend class Event_registration;
799 : };
800 :
801 : /**
802 : * Class representing a RPC request - zero or more identical responses to a
803 : * single request message with a response
804 : */
805 : template <typename Req, typename Resp, typename StreamMessage,
806 : typename... Args>
807 : class Stream : public Common_req
808 : {
809 : public:
810 1 : Stream (
811 : Connection &con, Args... args,
812 : std::function<vapi_error_e (Stream<Req, Resp, StreamMessage, Args...> &)>
813 : cb = nullptr)
814 2 : : Common_req{ con }, request{ con, vapi_alloc<Req> (con, args...) },
815 1 : response{ con, nullptr }, result_set{ con }, callback{ std::move (cb) }
816 : {
817 1 : }
818 :
819 : Stream (const Stream &) = delete;
820 :
821 1 : virtual ~Stream () {}
822 :
823 : virtual std::tuple<vapi_error_e, bool>
824 6 : assign_response (vapi_msg_id_t id, void *shm_data)
825 : {
826 6 : if (id == response.get_msg_id ())
827 : {
828 1 : response.assign_response (id, shm_data);
829 1 : result_set.mark_complete ();
830 1 : set_response_state (RESPONSE_READY);
831 1 : if (nullptr != callback)
832 : {
833 0 : return std::make_pair (callback (*this), true);
834 : }
835 2 : return std::make_pair (VAPI_OK, true);
836 : }
837 : else
838 : {
839 5 : result_set.assign_response (id, shm_data);
840 : }
841 10 : return std::make_pair (VAPI_OK, false);
842 : }
843 :
844 : vapi_error_e
845 1 : execute ()
846 : {
847 1 : return con.send (this);
848 : }
849 :
850 : const Msg<Req> &
851 1 : get_request (void)
852 : {
853 1 : return request;
854 : }
855 :
856 : const Msg<Resp> &
857 0 : get_response (void)
858 : {
859 0 : return response;
860 : }
861 :
862 : using resp_type = typename Msg<StreamMessage>::shm_data_type;
863 :
864 : const Result_set<StreamMessage> &
865 1 : get_result_set (void) const
866 : {
867 1 : return result_set;
868 : }
869 :
870 : private:
871 : Msg<Req> request;
872 : Msg<Resp> response;
873 : Result_set<StreamMessage> result_set;
874 : std::function<vapi_error_e (Stream<Req, Resp, StreamMessage, Args...> &)>
875 : callback;
876 :
877 : friend class Connection;
878 : friend class Result_set<StreamMessage>;
879 : };
880 :
881 : /**
882 : * Class representing a dump request - zero or more identical responses to a
883 : * single request message
884 : */
885 : template <typename Req, typename Resp, typename... Args>
886 : class Dump : public Common_req
887 : {
888 : public:
889 5 : Dump (Connection &con, Args... args,
890 : std::function<vapi_error_e (Dump<Req, Resp, Args...> &)> callback =
891 : nullptr)
892 10 : : Common_req{ con }, request{ con, vapi_alloc<Req> (con, args...) },
893 5 : result_set{ con }, callback{ std::move (callback) }
894 : {
895 4 : }
896 :
897 : Dump (const Dump &) = delete;
898 :
899 4 : virtual ~Dump ()
900 : {
901 4 : }
902 :
903 18 : virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id,
904 : void *shm_data)
905 : {
906 18 : if (id == vapi_msg_id_control_ping_reply)
907 : {
908 4 : con.msg_free (shm_data);
909 4 : result_set.mark_complete ();
910 4 : set_response_state (RESPONSE_READY);
911 4 : if (nullptr != callback)
912 : {
913 2 : return std::make_pair (callback (*this), true);
914 : }
915 6 : return std::make_pair (VAPI_OK, true);
916 : }
917 : else
918 : {
919 14 : result_set.assign_response (id, shm_data);
920 : }
921 28 : return std::make_pair (VAPI_OK, false);
922 : }
923 :
924 4 : vapi_error_e execute ()
925 : {
926 4 : return con.send_with_control_ping (this);
927 : }
928 :
929 0 : Msg<Req> &get_request (void)
930 : {
931 0 : return request;
932 : }
933 :
934 : using resp_type = typename Msg<Resp>::shm_data_type;
935 :
936 4 : const Result_set<Resp> &get_result_set (void) const
937 : {
938 4 : return result_set;
939 : }
940 :
941 : private:
942 : Msg<Req> request;
943 : Result_set<resp_type> result_set;
944 : std::function<vapi_error_e (Dump<Req, Resp, Args...> &)> callback;
945 :
946 : friend class Connection;
947 : };
948 :
949 : /**
950 : * Class representing event registration - incoming events (messages) from
951 : * vpp as a result of a subscription (typically a want_* simple request)
952 : */
953 : template <typename M> class Event_registration : public Common_req
954 : {
955 : public:
956 1 : Event_registration (
957 : Connection &con,
958 : std::function<vapi_error_e (Event_registration<M> &)> callback = nullptr)
959 4 : : Common_req{ con }, result_set{ con }, callback{ std::move (callback) }
960 : {
961 1 : if (!con.is_msg_available (M::get_msg_id ()))
962 : {
963 1 : throw Msg_not_available_exception ();
964 : }
965 0 : con.register_event (this);
966 0 : }
967 :
968 : Event_registration (const Event_registration &) = delete;
969 :
970 0 : virtual ~Event_registration ()
971 : {
972 0 : con.unregister_event (this);
973 0 : }
974 :
975 0 : virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id,
976 : void *shm_data)
977 : {
978 0 : result_set.assign_response (id, shm_data);
979 0 : if (nullptr != callback)
980 : {
981 0 : return std::make_pair (callback (*this), true);
982 : }
983 0 : return std::make_pair (VAPI_OK, true);
984 : }
985 :
986 : using resp_type = typename M::shm_data_type;
987 :
988 : Result_set<resp_type> &get_result_set (void)
989 : {
990 : return result_set;
991 : }
992 :
993 : private:
994 : Result_set<resp_type> result_set;
995 : std::function<vapi_error_e (Event_registration<M> &)> callback;
996 : };
997 : };
998 :
999 : #endif
1000 :
1001 : /*
1002 : * fd.io coding-style-patch-verification: ON
1003 : *
1004 : * Local Variables:
1005 : * eval: (c-set-style "gnu")
1006 : * End:
1007 : */
|