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;
263 #if VAPI_CPP_DEBUG_LEAKS 264 on_shm_data_alloc (shm_data);
266 std::lock_guard<std::recursive_mutex> requests_lock (requests_mutex);
267 std::lock_guard<std::recursive_mutex> events_lock (events_mutex);
269 vapi_ctx, be16toh (*static_cast<u16 *> (shm_data)));
271 bool break_dispatch =
false;
275 u32 context = *
reinterpret_cast<u32 *
> (
277 const auto x = requests.front ();
279 if (context == x->context)
281 std::tie (rv, break_dispatch) =
282 x->assign_response (
id, shm_data);
286 std::tie (rv, break_dispatch) =
287 x->assign_response (
id,
nullptr);
291 requests.pop_front ();
298 std::tie (rv, break_dispatch) =
299 events[id]->assign_response (
id, shm_data);
300 matching_req = events[id];
307 if ((matching_req && matching_req == limit && break_dispatch) ||
312 loop_again = !requests.empty () || (event_count > 0);
322 return dispatch (&limit);
338 return dispatch (req);
342 void msg_free (
void *shm_data)
344 #if VAPI_CPP_DEBUG_LEAKS 345 on_shm_data_free (shm_data);
350 template <
template <
typename XReq,
typename XResp,
typename... XArgs>
352 typename Req, typename Resp, typename... Args>
353 vapi_error_e send (X<Req, Resp, Args...> *req)
360 req_context_counter.fetch_add (1, std::memory_order_relaxed);
361 req->request.shm_data->header.context = req_context;
362 vapi_swap_to_be<Req> (req->request.shm_data);
363 std::lock_guard<std::recursive_mutex> lock (requests_mutex);
364 vapi_error_e rv =
vapi_send (vapi_ctx, req->request.shm_data);
368 requests.emplace_back (req);
369 req->set_context (req_context);
370 #if VAPI_CPP_DEBUG_LEAKS 371 on_shm_data_free (req->request.shm_data);
373 req->request.shm_data =
nullptr;
377 vapi_swap_to_host<Req> (req->request.shm_data);
382 template <
template <
typename XReq,
typename XResp,
typename... XArgs>
384 typename Req, typename Resp, typename... Args>
385 vapi_error_e send_with_control_ping (X<Req, Resp, Args...> *req)
392 req_context_counter.fetch_add (1, std::memory_order_relaxed);
393 req->request.shm_data->header.context = req_context;
394 vapi_swap_to_be<Req> (req->request.shm_data);
395 std::lock_guard<std::recursive_mutex> lock (requests_mutex);
396 vapi_error_e rv = vapi_send_with_control_ping (
397 vapi_ctx, req->request.shm_data, req_context);
401 requests.emplace_back (req);
402 req->set_context (req_context);
403 #if VAPI_CPP_DEBUG_LEAKS 404 on_shm_data_free (req->request.shm_data);
406 req->request.shm_data =
nullptr;
410 vapi_swap_to_host<Req> (req->request.shm_data);
417 std::lock_guard<std::recursive_mutex> lock (requests_mutex);
418 std::remove (requests.begin (), requests.end (),
request);
424 std::lock_guard<std::recursive_mutex> lock (events_mutex);
432 std::lock_guard<std::recursive_mutex> lock (events_mutex);
433 events[id] =
nullptr;
438 std::atomic_ulong req_context_counter;
439 std::mutex dispatch_mutex;
441 std::recursive_mutex requests_mutex;
442 std::recursive_mutex events_mutex;
443 std::deque<Common_req *> requests;
444 std::vector<Common_req *> events;
447 template <
typename Req,
typename Resp,
typename... Args>
450 template <
typename Req,
typename Resp,
typename... Args>
friend class Dump;
456 template <
typename M,
typename... Args>
459 template <
typename M>
friend class Msg;
461 #if VAPI_CPP_DEBUG_LEAKS 462 void on_shm_data_alloc (
void *shm_data)
466 auto pos = shm_data_set.find (shm_data);
467 if (pos == shm_data_set.end ())
469 shm_data_set.insert (shm_data);
473 printf (
"Double-add shm_data @%p!\n", shm_data);
478 void on_shm_data_free (
void *shm_data)
480 auto pos = shm_data_set.find (shm_data);
481 if (pos == shm_data_set.end ())
483 printf (
"Freeing untracked shm_data @%p!\n", shm_data);
487 shm_data_set.erase (pos);
490 std::unordered_set<void *> shm_data_set;
494 template <
typename Req,
typename Resp,
typename... Args>
class Request;
496 template <
typename Req,
typename Resp,
typename... Args>
class Dump;
518 template <
typename M>
class Msg 521 Msg (
const Msg &) =
delete;
525 VAPI_DBG (
"Destroy Msg<%s>@%p, shm_data@%p",
529 con.get ().msg_free (shm_data);
536 return *msg_id_holder ();
539 template <
typename X = M>
540 typename std::enable_if<vapi_has_payload_trait<X>::value,
541 decltype (X::payload) &>::type
544 return shm_data->payload;
550 VAPI_DBG (
"Move construct Msg<%s> from msg@%p to msg@%p, shm_data@%p",
552 shm_data = msg.shm_data;
553 msg.shm_data =
nullptr;
558 VAPI_DBG (
"Move assign Msg<%s> from msg@%p to msg@%p, shm_data@%p",
560 con.get ().msg_free (shm_data);
562 shm_data = msg.shm_data;
563 msg.shm_data =
nullptr;
567 struct Msg_allocator : std::allocator<Msg<M>>
569 template <
class U,
class... Args>
void construct (U *p, Args &&... args)
571 ::new ((
void *)p) U (std::forward<Args> (args)...);
583 (
id == *msg_id_holder ()));
584 *msg_id_holder () = id;
599 this->shm_data =
static_cast<shm_data_type *
> (shm_data);
606 assert (
nullptr == this->shm_data);
607 if (resp_id != get_msg_id ())
611 this->shm_data =
static_cast<M *
> (shm_data);
612 vapi_swap_to_host<M> (this->shm_data);
613 VAPI_DBG (
"Assign response to Msg<%s>@%p shm_data@%p",
617 std::reference_wrapper<Connection> con;
618 using shm_data_type =
M;
619 shm_data_type *shm_data;
623 template <
typename Req,
typename Resp,
typename... Args>
626 template <
typename Req,
typename Resp,
typename... Args>
friend class Dump;
632 friend struct Msg_allocator;
640 template <
typename Req,
typename Resp,
typename... Args>
648 request{con, vapi_alloc<Req> (con, args...)}, response{con,
nullptr}
658 con.unregister_request (
this);
664 return con.send (
this);
678 virtual std::tuple<vapi_error_e, bool> assign_response (
vapi_msg_id_t id,
682 response.assign_response (
id, shm_data);
684 if (
nullptr != callback)
686 return std::make_pair (callback (*
this),
true);
688 return std::make_pair (
VAPI_OK,
true);
720 typename std::vector<Msg<M>,
744 void mark_complete ()
759 vapi_swap_to_host<M> (
static_cast<M *
> (shm_data));
760 set.emplace_back (con, shm_data);
761 VAPI_DBG (
"Result_set@%p emplace_back shm_data@%p",
this, shm_data);
773 template <
typename Req,
typename Resp,
typename... Args>
friend class Dump;
782 template <
typename Req,
typename Resp,
typename... Args>
789 :
Common_req{con}, request{con, vapi_alloc<Req> (con, args...)},
790 result_set{con}, callback{callback}
805 con.msg_free (shm_data);
806 result_set.mark_complete ();
808 if (
nullptr != callback)
810 return std::make_pair (callback (*
this),
true);
812 return std::make_pair (
VAPI_OK,
true);
816 result_set.assign_response (
id, shm_data);
818 return std::make_pair (
VAPI_OK,
false);
823 return con.send_with_control_ping (
this);
856 :
Common_req{con}, result_set{con}, callback{callback}
862 con.register_event (
this);
869 con.unregister_event (
this);
875 result_set.assign_response (
id, shm_data);
876 if (
nullptr != callback)
878 return std::make_pair (callback (*
this),
true);
880 return std::make_pair (
VAPI_OK,
true);
892 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_recv(vapi_ctx_t ctx, void **msg, size_t *msg_size, svm_q_conditional_wait_t cond, u32 time)
low-level api for reading messages from vpp
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
blocking call, return on signal or time-out
void vapi_swap_to_be(M *msg)
Class representing event registration - incoming events (messages) from vpp as a result of a subscrip...
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
vapi_error_e dispatch(const Common_req *limit=nullptr, u32 time=5)
wait for responses from vpp and assign them to appropriate objects
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