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);
203 int max_outstanding_requests,
int response_queue_size,
204 bool handle_keepalives =
true)
207 max_outstanding_requests, response_queue_size,
218 auto x = requests.size ();
221 VAPI_DBG (
"popping request @%p", requests.front ());
222 requests.pop_front ();
252 std::lock_guard<std::mutex> lock (dispatch_mutex);
254 bool loop_again =
true;
258 size_t shm_data_size;
265 #if VAPI_CPP_DEBUG_LEAKS 266 on_shm_data_alloc (shm_data);
268 std::lock_guard<std::recursive_mutex> requests_lock (requests_mutex);
269 std::lock_guard<std::recursive_mutex> events_lock (events_mutex);
271 vapi_ctx, be16toh (*static_cast<u16 *> (shm_data)));
273 bool break_dispatch =
false;
277 u32 context = *
reinterpret_cast<u32 *
> (
279 const auto x = requests.front ();
281 if (context == x->context)
283 std::tie (rv, break_dispatch) =
284 x->assign_response (
id, shm_data);
288 std::tie (rv, break_dispatch) =
289 x->assign_response (
id,
nullptr);
293 requests.pop_front ();
300 std::tie (rv, break_dispatch) =
301 events[
id]->assign_response (
id, shm_data);
302 matching_req = events[
id];
309 if ((matching_req && matching_req == limit && break_dispatch) ||
314 loop_again = !requests.empty () || (event_count > 0);
324 return dispatch (&limit);
340 return dispatch (req);
344 void msg_free (
void *shm_data)
346 #if VAPI_CPP_DEBUG_LEAKS 347 on_shm_data_free (shm_data);
352 template <
template <
typename XReq,
typename XResp,
typename... XArgs>
354 typename Req, typename Resp, typename... Args>
355 vapi_error_e send (X<Req, Resp, Args...> *req)
362 req_context_counter.fetch_add (1, std::memory_order_relaxed);
363 req->request.shm_data->header.context = req_context;
364 vapi_swap_to_be<Req> (req->request.shm_data);
365 std::lock_guard<std::recursive_mutex> lock (requests_mutex);
366 vapi_error_e rv =
vapi_send (vapi_ctx, req->request.shm_data);
370 requests.emplace_back (req);
371 req->set_context (req_context);
372 #if VAPI_CPP_DEBUG_LEAKS 373 on_shm_data_free (req->request.shm_data);
375 req->request.shm_data =
nullptr;
379 vapi_swap_to_host<Req> (req->request.shm_data);
384 template <
template <
typename XReq,
typename XResp,
typename... XArgs>
386 typename Req, typename Resp, typename... Args>
387 vapi_error_e send_with_control_ping (X<Req, Resp, Args...> *req)
394 req_context_counter.fetch_add (1, std::memory_order_relaxed);
395 req->request.shm_data->header.context = req_context;
396 vapi_swap_to_be<Req> (req->request.shm_data);
397 std::lock_guard<std::recursive_mutex> lock (requests_mutex);
398 vapi_error_e rv = vapi_send_with_control_ping (
399 vapi_ctx, req->request.shm_data, req_context);
403 requests.emplace_back (req);
404 req->set_context (req_context);
405 #if VAPI_CPP_DEBUG_LEAKS 406 on_shm_data_free (req->request.shm_data);
408 req->request.shm_data =
nullptr;
412 vapi_swap_to_host<Req> (req->request.shm_data);
419 std::lock_guard<std::recursive_mutex> lock (requests_mutex);
420 std::remove (requests.begin (), requests.end (),
request);
426 std::lock_guard<std::recursive_mutex> lock (events_mutex);
434 std::lock_guard<std::recursive_mutex> lock (events_mutex);
435 events[
id] =
nullptr;
440 std::atomic_ulong req_context_counter;
441 std::mutex dispatch_mutex;
443 std::recursive_mutex requests_mutex;
444 std::recursive_mutex events_mutex;
445 std::deque<Common_req *> requests;
446 std::vector<Common_req *> events;
449 template <
typename Req,
typename Resp,
typename... Args>
452 template <
typename Req,
typename Resp,
typename... Args>
friend class Dump;
458 template <
typename M,
typename... Args>
461 template <
typename M>
friend class Msg;
463 #if VAPI_CPP_DEBUG_LEAKS 464 void on_shm_data_alloc (
void *shm_data)
468 auto pos = shm_data_set.find (shm_data);
469 if (pos == shm_data_set.end ())
471 shm_data_set.insert (shm_data);
475 printf (
"Double-add shm_data @%p!\n", shm_data);
480 void on_shm_data_free (
void *shm_data)
482 auto pos = shm_data_set.find (shm_data);
483 if (pos == shm_data_set.end ())
485 printf (
"Freeing untracked shm_data @%p!\n", shm_data);
489 shm_data_set.erase (pos);
492 std::unordered_set<void *> shm_data_set;
496 template <
typename Req,
typename Resp,
typename... Args>
class Request;
498 template <
typename Req,
typename Resp,
typename... Args>
class Dump;
520 template <
typename M>
class Msg 523 Msg (
const Msg &) =
delete;
527 VAPI_DBG (
"Destroy Msg<%s>@%p, shm_data@%p",
531 con.get ().msg_free (shm_data);
538 return *msg_id_holder ();
541 template <
typename X = M>
542 typename std::enable_if<vapi_has_payload_trait<X>::value,
543 decltype (X::payload) &>::type
546 return shm_data->payload;
552 VAPI_DBG (
"Move construct Msg<%s> from msg@%p to msg@%p, shm_data@%p",
554 shm_data = msg.shm_data;
555 msg.shm_data =
nullptr;
560 VAPI_DBG (
"Move assign Msg<%s> from msg@%p to msg@%p, shm_data@%p",
562 con.get ().msg_free (shm_data);
564 shm_data = msg.shm_data;
565 msg.shm_data =
nullptr;
569 struct Msg_allocator : std::allocator<Msg<M>>
571 template <
class U,
class... Args>
void construct (U *p, Args &&... args)
573 ::new ((
void *)p) U (std::forward<Args> (args)...);
585 (
id == *msg_id_holder ()));
586 *msg_id_holder () =
id;
601 this->shm_data =
static_cast<shm_data_type *
> (shm_data);
608 assert (
nullptr == this->shm_data);
609 if (resp_id != get_msg_id ())
613 this->shm_data =
static_cast<M *
> (shm_data);
614 vapi_swap_to_host<M> (this->shm_data);
615 VAPI_DBG (
"Assign response to Msg<%s>@%p shm_data@%p",
619 std::reference_wrapper<Connection> con;
620 using shm_data_type =
M;
621 shm_data_type *shm_data;
625 template <
typename Req,
typename Resp,
typename... Args>
628 template <
typename Req,
typename Resp,
typename... Args>
friend class Dump;
634 friend struct Msg_allocator;
642 template <
typename Req,
typename Resp,
typename... Args>
650 request{con, vapi_alloc<Req> (con, args...)}, response{con,
nullptr}
660 con.unregister_request (
this);
666 return con.send (
this);
680 virtual std::tuple<vapi_error_e, bool> assign_response (
vapi_msg_id_t id,
684 response.assign_response (
id, shm_data);
686 if (
nullptr != callback)
688 return std::make_pair (callback (*
this),
true);
690 return std::make_pair (
VAPI_OK,
true);
722 typename std::vector<Msg<M>,
746 void mark_complete ()
761 vapi_swap_to_host<M> (
static_cast<M *
> (shm_data));
762 set.emplace_back (con, shm_data);
763 VAPI_DBG (
"Result_set@%p emplace_back shm_data@%p",
this, shm_data);
775 template <
typename Req,
typename Resp,
typename... Args>
friend class Dump;
784 template <
typename Req,
typename Resp,
typename... Args>
791 :
Common_req{con}, request{con, vapi_alloc<Req> (con, args...)},
792 result_set{con}, callback{callback}
807 con.msg_free (shm_data);
808 result_set.mark_complete ();
810 if (
nullptr != callback)
812 return std::make_pair (callback (*
this),
true);
814 return std::make_pair (
VAPI_OK,
true);
818 result_set.assign_response (
id, shm_data);
820 return std::make_pair (
VAPI_OK,
false);
825 return con.send_with_control_ping (
this);
858 :
Common_req{con}, result_set{con}, callback{callback}
864 con.register_event (
this);
871 con.unregister_event (
this);
877 result_set.assign_response (
id, shm_data);
878 if (
nullptr != callback)
880 return std::make_pair (callback (*
this),
true);
882 return std::make_pair (
VAPI_OK,
true);
894 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
std::enable_if< vapi_has_payload_trait< X >::value, decltype(X::payload) & >::type get_payload() const
static vapi_msg_id_t get_msg_id()
size_t vapi_get_message_count()
vapi_msg_id_t vapi_get_msg_id_t()
Connection & get_connection()
Request(Connection &con, Args... args, std::function< vapi_error_e(Request< Req, Resp, Args... > &)> callback=nullptr)
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
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, bool handle_keepalives)
connect to vpp
typename Msg< Resp >::shm_data_type resp_type
blocking call, returns on signal or time-out - best used in combination with condvars, with eventfds we don't yield the cpu
vapi_error_e connect(const char *name, const char *chroot_prefix, int max_outstanding_requests, int response_queue_size, bool handle_keepalives=true)
connect to vpp
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_disconnect(vapi_ctx_t ctx)
disconnect from vpp
Class representing iterable set of responses of the same type.
vhost_vring_state_t state
const Msg< Resp > & get_response(void)
#define VAPI_INVALID_MSG_ID
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 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...)
const Msg< Req > & get_request(void) const
vapi_msg_id_t vapi_lookup_vapi_msg_id_t(vapi_ctx_t ctx, u16 vl_msg_id)
virtual ~Event_registration()
virtual const char * what() const
Class representing a message stored in shared memory.
Msg< Req > & get_request(void)
virtual const char * what() const
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)
vapi_response_state_e get_response_state(void) const
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 Result_set< Resp > & get_result_set(void) const
const_iterator begin() const
Dump(Connection &con, Args... args, std::function< vapi_error_e(Dump< 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
const_iterator end() 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.
vapi_error_e vapi_ctx_alloc(vapi_ctx_t *result)
allocate vapi context
unsigned int vapi_msg_id_t