LCOV - code coverage report
Current view: top level - vpp-api/vapi - vapi.hpp (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 192 248 77.4 %
Date: 2023-07-05 22:20:52 Functions: 331 1085 30.5 %

          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             :  */

Generated by: LCOV version 1.14