LCOV - code coverage report
Current view: top level - vpp-api/vapi - vapi.hpp (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 214 273 78.4 %
Date: 2023-10-26 01:39:38 Functions: 397 1194 33.2 %

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

Generated by: LCOV version 1.14