18 #ifndef vapi_hpp_included 19 #define vapi_hpp_included 33 #include <vapi/vpe.api.vapi.h> 35 #if VAPI_CPP_DEBUG_LEAKS 36 #include <unordered_set> 49 template <
typename Req,
typename Resp,
typename... Args>
class Request;
50 template <
typename M>
class Msg;
53 template <
typename M,
typename... Args>
61 virtual const char *
what ()
const throw ()
63 return "unexpected message id";
70 virtual const char *
what ()
const throw ()
72 return "message unavailable";
103 return response_state;
113 void set_response_state (vapi_response_state_e
state)
115 response_state =
state;
118 virtual std::tuple<vapi_error_e, bool> assign_response (
vapi_msg_id_t id,
121 void set_context (
u32 context)
123 this->context = context;
132 vapi_response_state_e response_state;
136 template <
typename M>
friend class Msg;
138 template <
typename Req,
typename Resp,
typename... Args>
141 template <
typename Req,
typename Resp,
typename... Args>
friend class Dump;
164 throw std::bad_alloc ();
175 #if VAPI_CPP_DEBUG_LEAKS 176 for (
auto x : shm_data_set)
178 printf (
"Leaked shm_data@%p!\n", x);
202 int max_outstanding_requests,
int response_queue_size)
205 max_outstanding_requests, response_queue_size,
216 auto x = requests.size ();
219 VAPI_DBG (
"popping request @%p", requests.front ());
220 requests.pop_front ();
250 std::lock_guard<std::mutex> lock (dispatch_mutex);
252 bool loop_again =
true;
256 size_t shm_data_size;
257 rv =
vapi_recv (vapi_ctx, &shm_data, &shm_data_size);
262 #if VAPI_CPP_DEBUG_LEAKS 263 on_shm_data_alloc (shm_data);
265 std::lock_guard<std::recursive_mutex> requests_lock (requests_mutex);
266 std::lock_guard<std::recursive_mutex> events_lock (events_mutex);
268 vapi_ctx, be16toh (*static_cast<u16 *> (shm_data)));
270 bool break_dispatch =
false;
274 u32 context = *
reinterpret_cast<u32 *
> (
276 const auto x = requests.front ();
278 if (context == x->context)
280 std::tie (rv, break_dispatch) =
281 x->assign_response (
id, shm_data);
285 std::tie (rv, break_dispatch) =
286 x->assign_response (
id,
nullptr);
290 requests.pop_front ();
297 std::tie (rv, break_dispatch) =
298 events[id]->assign_response (
id, shm_data);
299 matching_req = events[id];
306 if ((matching_req && matching_req == limit && break_dispatch) ||
311 loop_again = !requests.empty () || (event_count > 0);
321 return dispatch (&limit);
337 return dispatch (req);
341 void msg_free (
void *shm_data)
343 #if VAPI_CPP_DEBUG_LEAKS 344 on_shm_data_free (shm_data);
349 template <
template <
typename XReq,
typename XResp,
typename... XArgs>
351 typename Req, typename Resp, typename... Args>
352 vapi_error_e send (X<Req, Resp, Args...> *req)
359 req_context_counter.fetch_add (1, std::memory_order_relaxed);
360 req->request.shm_data->header.context = req_context;
361 vapi_swap_to_be<Req> (req->request.shm_data);
362 std::lock_guard<std::recursive_mutex> lock (requests_mutex);
363 vapi_error_e rv =
vapi_send (vapi_ctx, req->request.shm_data);
367 requests.emplace_back (req);
368 req->set_context (req_context);
369 #if VAPI_CPP_DEBUG_LEAKS 370 on_shm_data_free (req->request.shm_data);
372 req->request.shm_data =
nullptr;
376 vapi_swap_to_host<Req> (req->request.shm_data);
381 template <
template <
typename XReq,
typename XResp,
typename... XArgs>
383 typename Req, typename Resp, typename... Args>
384 vapi_error_e send_with_control_ping (X<Req, Resp, Args...> *req)
391 req_context_counter.fetch_add (1, std::memory_order_relaxed);
392 req->request.shm_data->header.context = req_context;
393 vapi_swap_to_be<Req> (req->request.shm_data);
394 std::lock_guard<std::recursive_mutex> lock (requests_mutex);
395 vapi_error_e rv = vapi_send_with_control_ping (
396 vapi_ctx, req->request.shm_data, req_context);
400 requests.emplace_back (req);
401 req->set_context (req_context);
402 #if VAPI_CPP_DEBUG_LEAKS 403 on_shm_data_free (req->request.shm_data);
405 req->request.shm_data =
nullptr;
409 vapi_swap_to_host<Req> (req->request.shm_data);
416 std::lock_guard<std::recursive_mutex> lock (requests_mutex);
417 std::remove (requests.begin (), requests.end (),
request);
423 std::lock_guard<std::recursive_mutex> lock (events_mutex);
431 std::lock_guard<std::recursive_mutex> lock (events_mutex);
432 events[id] =
nullptr;
437 std::atomic_ulong req_context_counter;
438 std::mutex dispatch_mutex;
440 std::recursive_mutex requests_mutex;
441 std::recursive_mutex events_mutex;
442 std::deque<Common_req *> requests;
443 std::vector<Common_req *> events;
446 template <
typename Req,
typename Resp,
typename... Args>
449 template <
typename Req,
typename Resp,
typename... Args>
friend class Dump;
455 template <
typename M,
typename... Args>
458 template <
typename M>
friend class Msg;
460 #if VAPI_CPP_DEBUG_LEAKS 461 void on_shm_data_alloc (
void *shm_data)
465 auto pos = shm_data_set.find (shm_data);
466 if (pos == shm_data_set.end ())
468 shm_data_set.insert (shm_data);
472 printf (
"Double-add shm_data @%p!\n", shm_data);
477 void on_shm_data_free (
void *shm_data)
479 auto pos = shm_data_set.find (shm_data);
480 if (pos == shm_data_set.end ())
482 printf (
"Freeing untracked shm_data @%p!\n", shm_data);
486 shm_data_set.erase (pos);
489 std::unordered_set<void *> shm_data_set;
493 template <
typename Req,
typename Resp,
typename... Args>
class Request;
495 template <
typename Req,
typename Resp,
typename... Args>
class Dump;
517 template <
typename M>
class Msg 520 Msg (
const Msg &) =
delete;
524 VAPI_DBG (
"Destroy Msg<%s>@%p, shm_data@%p",
528 con.get ().msg_free (shm_data);
535 return *msg_id_holder ();
538 template <
typename X = M>
539 typename std::enable_if<vapi_has_payload_trait<X>::value,
540 decltype (X::payload) &>::type
543 return shm_data->payload;
549 VAPI_DBG (
"Move construct Msg<%s> from msg@%p to msg@%p, shm_data@%p",
551 shm_data = msg.shm_data;
552 msg.shm_data =
nullptr;
557 VAPI_DBG (
"Move assign Msg<%s> from msg@%p to msg@%p, shm_data@%p",
559 con.get ().msg_free (shm_data);
561 shm_data = msg.shm_data;
562 msg.shm_data =
nullptr;
566 struct Msg_allocator : std::allocator<Msg<M>>
568 template <
class U,
class... Args>
void construct (U *p, Args &&... args)
570 ::new ((
void *)p) U (std::forward<Args> (args)...);
582 (
id == *msg_id_holder ()));
583 *msg_id_holder () = id;
598 this->shm_data =
static_cast<shm_data_type *
> (shm_data);
605 assert (
nullptr == this->shm_data);
606 if (resp_id != get_msg_id ())
610 this->shm_data =
static_cast<M *
> (shm_data);
611 vapi_swap_to_host<M> (this->shm_data);
612 VAPI_DBG (
"Assign response to Msg<%s>@%p shm_data@%p",
616 std::reference_wrapper<Connection> con;
617 using shm_data_type =
M;
618 shm_data_type *shm_data;
622 template <
typename Req,
typename Resp,
typename... Args>
625 template <
typename Req,
typename Resp,
typename... Args>
friend class Dump;
631 friend struct Msg_allocator;
639 template <
typename Req,
typename Resp,
typename... Args>
647 request{con, vapi_alloc<Req> (con, args...)}, response{con,
nullptr}
657 con.unregister_request (
this);
663 return con.send (
this);
677 virtual std::tuple<vapi_error_e, bool> assign_response (
vapi_msg_id_t id,
681 response.assign_response (
id, shm_data);
683 if (
nullptr != callback)
685 return std::make_pair (callback (*
this),
true);
687 return std::make_pair (
VAPI_OK,
true);
719 typename std::vector<Msg<M>,
743 void mark_complete ()
758 vapi_swap_to_host<M> (
static_cast<M *
> (shm_data));
759 set.emplace_back (con, shm_data);
760 VAPI_DBG (
"Result_set@%p emplace_back shm_data@%p",
this, shm_data);
772 template <
typename Req,
typename Resp,
typename... Args>
friend class Dump;
781 template <
typename Req,
typename Resp,
typename... Args>
788 :
Common_req{con}, request{con, vapi_alloc<Req> (con, args...)},
789 result_set{con}, callback{callback}
804 con.msg_free (shm_data);
805 result_set.mark_complete ();
807 if (
nullptr != callback)
809 return std::make_pair (callback (*
this),
true);
811 return std::make_pair (
VAPI_OK,
true);
815 result_set.assign_response (
id, shm_data);
817 return std::make_pair (
VAPI_OK,
false);
822 return con.send_with_control_ping (
this);
855 :
Common_req{con}, result_set{con}, callback{callback}
861 con.register_event (
this);
868 con.unregister_event (
this);
874 result_set.assign_response (
id, shm_data);
875 if (
nullptr != callback)
877 return std::make_pair (callback (*
this),
true);
879 return std::make_pair (
VAPI_OK,
true);
891 std::function<vapi_error_e (Event_registration<M> &)> callback;
no response to request (will never come)
void vapi_msg_set_msg_id(vapi_msg_id_t id)
vapi_error_e dispatch(const Common_req &limit)
convenience wrapper function
operations block until response received
static vapi_msg_id_t get_msg_id()
vapi_error_e connect(const char *name, const char *chroot_prefix, int max_outstanding_requests, int response_queue_size)
connect to vpp
size_t vapi_get_message_count()
vapi_msg_id_t vapi_get_msg_id_t()
Connection & get_connection()
vapi_error_e vapi_send(vapi_ctx_t ctx, void *msg)
low-level api for sending messages to vpp
virtual const char * what() const
typename Msg< Resp >::shm_data_type resp_type
void vapi_swap_to_be(M *msg)
Class representing event registration - incoming events (messages) from vpp as a result of a subscrip...
vapi_error_e vapi_recv(vapi_ctx_t ctx, void **msg, size_t *msg_size)
low-level api for reading messages from vpp
vapi_response_state_e get_response_state(void) const
vapi_error_e vapi_disconnect(vapi_ctx_t ctx)
disconnect from vpp
const Result_set< Resp > & get_result_set(void) const
Class representing iterable set of responses of the same type.
const Msg< Resp > & get_response(void)
size_t vapi_get_context_offset(vapi_msg_id_t id)
response to request is ready
vapi_msg_id_t vapi_msg_id_control_ping_reply
const Msg< Req > & get_request(void) const
const char * vapi_get_msg_name(vapi_msg_id_t id)
vapi_error_e disconnect()
disconnect from vpp
void free_all_responses()
vapi_error_e get_fd(int *fd)
get event file descriptor
invalid value encountered
common vpp api C declarations
virtual std::tuple< vapi_error_e, bool > assign_response(vapi_msg_id_t id, void *shm_data)
bool vapi_msg_is_with_context(vapi_msg_id_t id)
Result_set< resp_type > & get_result_set(void)
M * vapi_alloc(Connection &con, Args...)
vapi_msg_id_t vapi_lookup_vapi_msg_id_t(vapi_ctx_t ctx, u16 vl_msg_id)
virtual ~Event_registration()
Class representing a message stored in shared memory.
Dump(Connection &con, Args...args, std::function< vapi_error_e(Dump< Req, Resp, Args... > &)> callback=nullptr)
Msg< Req > & get_request(void)
typename std::vector< Msg< resp_type >, typename Msg< resp_type >::Msg_allocator >::const_iterator const_iterator
void vapi_swap_to_host(M *msg)
Class representing a connection to VPP.
internal vpp api C declarations
void free_response(const_iterator pos)
bool vapi_is_msg_available(vapi_ctx_t ctx, vapi_msg_id_t id)
check if message identified by it's message id is known by the vpp to which the connection is open ...
virtual std::tuple< vapi_error_e, bool > assign_response(vapi_msg_id_t id, void *shm_data)
const_iterator end() const
vhost_vring_state_t state
virtual const char * what() const
Request(Connection &con, Args...args, std::function< vapi_error_e(Request< Req, Resp, Args... > &)> callback=nullptr)
Event_registration(Connection &con, std::function< vapi_error_e(Event_registration< M > &)> callback=nullptr)
vapi_error_e wait_for_response(const Common_req &req)
wait for response to a specific request
void vapi_ctx_free(vapi_ctx_t ctx)
free vapi context
std::enable_if< vapi_has_payload_trait< X >::value, decltype(X::payload)& >::type get_payload() const
Class representing a simple request - with a single response message.
Class representing a dump request - zero or more identical responses to a single request message...
bool is_msg_available(vapi_msg_id_t type)
check if message identified by it's message id is known by the vpp to which the connection is open ...
typename M::shm_data_type resp_type
vapi_error_e vapi_get_fd(vapi_ctx_t ctx, int *fd)
get event file descriptor
void vapi_msg_free(vapi_ctx_t ctx, void *msg)
free a vapi message
Class representing common functionality of a request - response state and context.
const_iterator begin() const
vapi_error_e vapi_connect(vapi_ctx_t ctx, const char *name, const char *chroot_prefix, int max_outstanding_requests, int response_queue_size, vapi_mode_e mode)
connect to vpp
vapi_error_e vapi_ctx_alloc(vapi_ctx_t *result)
allocate vapi context
unsigned int vapi_msg_id_t
vapi_error_e dispatch(const Common_req *limit=nullptr)
wait for responses from vpp and assign them to appropriate objects