58d170603e5432cb138406bbf3cc7e46b137d3ca
[vpp.git] / src / vpp-api / vapi / vapi.hpp
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   virtual const char *what () const throw ()
62   {
63     return "unexpected message id";
64   }
65 };
66
67 class Msg_not_available_exception : public std::exception
68 {
69 public:
70   virtual const char *what () const throw ()
71   {
72     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   virtual ~Common_req (){};
95
96   Connection &get_connection ()
97   {
98     return con;
99   };
100
101   vapi_response_state_e get_response_state (void) const
102   {
103     return response_state;
104   }
105
106 private:
107   Connection &con;
108   Common_req (Connection &con)
109       : con (con), context{0}, response_state{RESPONSE_NOT_READY}
110   {
111   }
112
113   void set_response_state (vapi_response_state_e state)
114   {
115     response_state = state;
116   }
117
118   virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id,
119                                                           void *shm_data) = 0;
120
121   void set_context (u32 context)
122   {
123     this->context = context;
124   }
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   Connection (void) : vapi_ctx{0}, event_count{0}
161   {
162
163     vapi_error_e rv = VAPI_OK;
164     if (!vapi_ctx)
165       {
166         if (VAPI_OK != (rv = vapi_ctx_alloc (&vapi_ctx)))
167           {
168             throw std::bad_alloc ();
169           }
170       }
171     events.reserve (vapi_get_message_count () + 1);
172   }
173
174   Connection (const Connection &) = delete;
175
176   ~Connection (void)
177   {
178     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   }
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   bool is_msg_available (vapi_msg_id_t type)
192   {
193     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   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     return vapi_connect (vapi_ctx, name, chroot_prefix,
211                          max_outstanding_requests, response_queue_size,
212                          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   vapi_error_e disconnect ()
221   {
222     auto x = requests.size ();
223     while (x > 0)
224       {
225         VAPI_DBG ("popping request @%p", requests.front ());
226         requests.pop_front ();
227         --x;
228       }
229     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   vapi_error_e dispatch (const Common_req *limit = nullptr, u32 time = 5)
255   {
256     std::lock_guard<std::mutex> lock (dispatch_mutex);
257     vapi_error_e rv = VAPI_OK;
258     bool loop_again = true;
259     while (loop_again)
260       {
261         void *shm_data;
262         size_t shm_data_size;
263         rv = vapi_recv (vapi_ctx, &shm_data, &shm_data_size, SVM_Q_TIMEDWAIT,
264                         time);
265         if (VAPI_OK != rv)
266           {
267             return rv;
268           }
269 #if VAPI_CPP_DEBUG_LEAKS
270         on_shm_data_alloc (shm_data);
271 #endif
272         std::lock_guard<std::recursive_mutex> requests_lock (requests_mutex);
273         std::lock_guard<std::recursive_mutex> events_lock (events_mutex);
274         vapi_msg_id_t id = vapi_lookup_vapi_msg_id_t (
275             vapi_ctx, be16toh (*static_cast<u16 *> (shm_data)));
276         bool has_context = vapi_msg_is_with_context (id);
277         bool break_dispatch = false;
278         Common_req *matching_req = nullptr;
279         if (has_context)
280           {
281             u32 context = *reinterpret_cast<u32 *> (
282                 (static_cast<u8 *> (shm_data) + vapi_get_context_offset (id)));
283             const auto x = requests.front ();
284             matching_req = x;
285             if (context == x->context)
286               {
287                 std::tie (rv, break_dispatch) =
288                     x->assign_response (id, shm_data);
289               }
290             else
291               {
292                 std::tie (rv, break_dispatch) =
293                     x->assign_response (id, nullptr);
294               }
295             if (break_dispatch)
296               {
297                 requests.pop_front ();
298               }
299           }
300         else
301           {
302             if (events[id])
303               {
304                 std::tie (rv, break_dispatch) =
305                     events[id]->assign_response (id, shm_data);
306                 matching_req = events[id];
307               }
308             else
309               {
310                 msg_free (shm_data);
311               }
312           }
313         if ((matching_req && matching_req == limit && break_dispatch) ||
314             VAPI_OK != rv)
315           {
316             return rv;
317           }
318         loop_again = !requests.empty () || (event_count > 0);
319       }
320     return rv;
321   }
322
323   /**
324    * @brief convenience wrapper function
325    */
326   vapi_error_e dispatch (const Common_req &limit)
327   {
328     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   vapi_error_e wait_for_response (const Common_req &req)
339   {
340     if (RESPONSE_READY == req.get_response_state ())
341       {
342         return VAPI_OK;
343       }
344     return dispatch (req);
345   }
346
347 private:
348   void msg_free (void *shm_data)
349   {
350 #if VAPI_CPP_DEBUG_LEAKS
351     on_shm_data_free (shm_data);
352 #endif
353     vapi_msg_free (vapi_ctx, shm_data);
354   }
355
356   template <template <typename XReq, typename XResp, typename... XArgs>
357             class X,
358             typename Req, typename Resp, typename... Args>
359   vapi_error_e send (X<Req, Resp, Args...> *req)
360   {
361     if (!req)
362       {
363         return VAPI_EINVAL;
364       }
365     u32 req_context =
366         req_context_counter.fetch_add (1, std::memory_order_relaxed);
367     req->request.shm_data->header.context = req_context;
368     vapi_swap_to_be<Req> (req->request.shm_data);
369     std::lock_guard<std::recursive_mutex> lock (requests_mutex);
370     vapi_error_e rv = vapi_send (vapi_ctx, req->request.shm_data);
371     if (VAPI_OK == rv)
372       {
373         VAPI_DBG ("Push %p", req);
374         requests.emplace_back (req);
375         req->set_context (req_context);
376 #if VAPI_CPP_DEBUG_LEAKS
377         on_shm_data_free (req->request.shm_data);
378 #endif
379         req->request.shm_data = nullptr; /* consumed by vapi_send */
380       }
381     else
382       {
383         vapi_swap_to_host<Req> (req->request.shm_data);
384       }
385     return rv;
386   }
387
388   template <template <typename XReq, typename XResp, typename... XArgs>
389             class X,
390             typename Req, typename Resp, typename... Args>
391   vapi_error_e send_with_control_ping (X<Req, Resp, Args...> *req)
392   {
393     if (!req)
394       {
395         return VAPI_EINVAL;
396       }
397     u32 req_context =
398         req_context_counter.fetch_add (1, std::memory_order_relaxed);
399     req->request.shm_data->header.context = req_context;
400     vapi_swap_to_be<Req> (req->request.shm_data);
401     std::lock_guard<std::recursive_mutex> lock (requests_mutex);
402     vapi_error_e rv = vapi_send_with_control_ping (
403         vapi_ctx, req->request.shm_data, req_context);
404     if (VAPI_OK == rv)
405       {
406         VAPI_DBG ("Push %p", req);
407         requests.emplace_back (req);
408         req->set_context (req_context);
409 #if VAPI_CPP_DEBUG_LEAKS
410         on_shm_data_free (req->request.shm_data);
411 #endif
412         req->request.shm_data = nullptr; /* consumed by vapi_send */
413       }
414     else
415       {
416         vapi_swap_to_host<Req> (req->request.shm_data);
417       }
418     return rv;
419   }
420
421   void unregister_request (Common_req *request)
422   {
423     std::lock_guard<std::recursive_mutex> lock (requests_mutex);
424     std::remove (requests.begin (), requests.end (), request);
425   }
426
427   template <typename M> void register_event (Event_registration<M> *event)
428   {
429     const vapi_msg_id_t id = M::get_msg_id ();
430     std::lock_guard<std::recursive_mutex> lock (events_mutex);
431     events[id] = event;
432     ++event_count;
433   }
434
435   template <typename M> void unregister_event (Event_registration<M> *event)
436   {
437     const vapi_msg_id_t id = M::get_msg_id ();
438     std::lock_guard<std::recursive_mutex> lock (events_mutex);
439     events[id] = nullptr;
440     --event_count;
441   }
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 template <typename M> void vapi_msg_set_msg_id (vapi_msg_id_t id)
525 {
526   Msg<M>::set_msg_id (id);
527 }
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   ~Msg ()
538   {
539     VAPI_DBG ("Destroy Msg<%s>@%p, shm_data@%p",
540               vapi_get_msg_name (get_msg_id ()), this, shm_data);
541     if (shm_data)
542       {
543         con.get ().msg_free (shm_data);
544         shm_data = nullptr;
545       }
546   }
547
548   static vapi_msg_id_t get_msg_id ()
549   {
550     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   get_payload () const
557   {
558     return shm_data->payload;
559   }
560
561 private:
562   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     shm_data = msg.shm_data;
567     msg.shm_data = nullptr;
568   }
569
570   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     con.get ().msg_free (shm_data);
575     con = msg.con;
576     shm_data = msg.shm_data;
577     msg.shm_data = nullptr;
578     return *this;
579   }
580
581   struct Msg_allocator : std::allocator<Msg<M>>
582   {
583     template <class U, class... Args> void construct (U *p, Args &&... args)
584     {
585       ::new ((void *)p) U (std::forward<Args> (args)...);
586     }
587
588     template <class U> struct rebind
589     {
590       typedef Msg_allocator other;
591     };
592   };
593
594   static void set_msg_id (vapi_msg_id_t id)
595   {
596     assert ((VAPI_INVALID_MSG_ID == *msg_id_holder ()) ||
597             (id == *msg_id_holder ()));
598     *msg_id_holder () = id;
599   }
600
601   static vapi_msg_id_t *msg_id_holder ()
602   {
603     static vapi_msg_id_t my_id{VAPI_INVALID_MSG_ID};
604     return &my_id;
605   }
606
607   Msg (Connection &con, void *shm_data) : con{con}
608   {
609     if (!con.is_msg_available (get_msg_id ()))
610       {
611         throw Msg_not_available_exception ();
612       }
613     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   }
617
618   void assign_response (vapi_msg_id_t resp_id, void *shm_data)
619   {
620     assert (nullptr == this->shm_data);
621     if (resp_id != get_msg_id ())
622       {
623         throw Unexpected_msg_id_exception ();
624       }
625     this->shm_data = static_cast<M *> (shm_data);
626     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   }
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   Request (Connection &con, Args... args,
663            std::function<vapi_error_e (Request<Req, Resp, Args...> &)>
664                callback = nullptr)
665       : Common_req{con}, callback{callback},
666         request{con, vapi_alloc<Req> (con, args...)}, response{con, nullptr}
667   {
668   }
669
670   Request (const Request &) = delete;
671
672   virtual ~Request ()
673   {
674     if (RESPONSE_NOT_READY == get_response_state ())
675       {
676         con.unregister_request (this);
677       }
678   }
679
680   vapi_error_e execute ()
681   {
682     return con.send (this);
683   }
684
685   const Msg<Req> &get_request (void) const
686   {
687     return request;
688   }
689
690   const Msg<Resp> &get_response (void)
691   {
692     return response;
693   }
694
695 private:
696   virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id,
697                                                           void *shm_data)
698   {
699     assert (RESPONSE_NOT_READY == get_response_state ());
700     response.assign_response (id, shm_data);
701     set_response_state (RESPONSE_READY);
702     if (nullptr != callback)
703       {
704         return std::make_pair (callback (*this), true);
705       }
706     return std::make_pair (VAPI_OK, true);
707   }
708   std::function<vapi_error_e (Request<Req, Resp, Args...> &)> callback;
709   Msg<Req> request;
710   Msg<Resp> response;
711
712   friend class Connection;
713 };
714
715 /**
716  * Class representing iterable set of responses of the same type
717  */
718 template <typename M> class Result_set
719 {
720 public:
721   ~Result_set ()
722   {
723   }
724
725   Result_set (const Result_set &) = delete;
726
727   bool is_complete () const
728   {
729     return complete;
730   }
731
732   size_t size () const
733   {
734     return set.size ();
735   }
736
737   using const_iterator =
738       typename std::vector<Msg<M>,
739                            typename Msg<M>::Msg_allocator>::const_iterator;
740
741   const_iterator begin () const
742   {
743     return set.begin ();
744   }
745
746   const_iterator end () const
747   {
748     return set.end ();
749   }
750
751   void free_response (const_iterator pos)
752   {
753     set.erase (pos);
754   }
755
756   void free_all_responses ()
757   {
758     set.clear ();
759   }
760
761 private:
762   void mark_complete ()
763   {
764     complete = true;
765   }
766
767   void assign_response (vapi_msg_id_t resp_id, void *shm_data)
768   {
769     if (resp_id != Msg<M>::get_msg_id ())
770       {
771         {
772           throw Unexpected_msg_id_exception ();
773         }
774       }
775     else if (shm_data)
776       {
777         vapi_swap_to_host<M> (static_cast<M *> (shm_data));
778         set.emplace_back (con, shm_data);
779         VAPI_DBG ("Result_set@%p emplace_back shm_data@%p", this, shm_data);
780       }
781   }
782
783   Result_set (Connection &con) : con (con), complete{false}
784   {
785   }
786
787   Connection &con;
788   bool complete;
789   std::vector<Msg<M>, typename Msg<M>::Msg_allocator> set;
790
791   template <typename Req, typename Resp, typename StreamMessage,
792             typename... Args>
793   friend class Stream;
794
795   template <typename Req, typename Resp, typename... Args> friend class Dump;
796
797   template <typename X> friend class Event_registration;
798 };
799
800 /**
801  * Class representing a RPC request - zero or more identical responses to a
802  * single request message with a response
803  */
804 template <typename Req, typename Resp, typename StreamMessage,
805           typename... Args>
806 class Stream : public Common_req
807 {
808 public:
809   Stream (
810     Connection &con, Args... args,
811     std::function<vapi_error_e (Stream<Req, Resp, StreamMessage, Args...> &)>
812       cb = nullptr)
813       : Common_req{ con }, request{ con, vapi_alloc<Req> (con, args...) },
814         response{ con, nullptr }, result_set{ con }, callback{ cb }
815   {
816   }
817
818   Stream (const Stream &) = delete;
819
820   virtual ~Stream () {}
821
822   virtual std::tuple<vapi_error_e, bool>
823   assign_response (vapi_msg_id_t id, void *shm_data)
824   {
825     if (id == response.get_msg_id ())
826       {
827         response.assign_response (id, shm_data);
828         result_set.mark_complete ();
829         set_response_state (RESPONSE_READY);
830         if (nullptr != callback)
831           {
832             return std::make_pair (callback (*this), true);
833           }
834         return std::make_pair (VAPI_OK, true);
835       }
836     else
837       {
838         result_set.assign_response (id, shm_data);
839       }
840     return std::make_pair (VAPI_OK, false);
841   }
842
843   vapi_error_e
844   execute ()
845   {
846     return con.send (this);
847   }
848
849   const Msg<Req> &
850   get_request (void)
851   {
852     return request;
853   }
854
855   const Msg<Resp> &
856   get_response (void)
857   {
858     return response;
859   }
860
861   using resp_type = typename Msg<StreamMessage>::shm_data_type;
862
863   const Result_set<StreamMessage> &
864   get_result_set (void) const
865   {
866     return result_set;
867   }
868
869 private:
870   Msg<Req> request;
871   Msg<Resp> response;
872   Result_set<StreamMessage> result_set;
873   std::function<vapi_error_e (Stream<Req, Resp, StreamMessage, Args...> &)>
874     callback;
875
876   friend class Connection;
877   friend class Result_set<StreamMessage>;
878 };
879
880 /**
881  * Class representing a dump request - zero or more identical responses to a
882  * single request message
883  */
884 template <typename Req, typename Resp, typename... Args>
885 class Dump : public Common_req
886 {
887 public:
888   Dump (Connection &con, Args... args,
889         std::function<vapi_error_e (Dump<Req, Resp, Args...> &)> callback =
890             nullptr)
891       : Common_req{con}, request{con, vapi_alloc<Req> (con, args...)},
892         result_set{con}, callback{callback}
893   {
894   }
895
896   Dump (const Dump &) = delete;
897
898   virtual ~Dump ()
899   {
900   }
901
902   virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id,
903                                                           void *shm_data)
904   {
905     if (id == vapi_msg_id_control_ping_reply)
906       {
907         con.msg_free (shm_data);
908         result_set.mark_complete ();
909         set_response_state (RESPONSE_READY);
910         if (nullptr != callback)
911           {
912             return std::make_pair (callback (*this), true);
913           }
914         return std::make_pair (VAPI_OK, true);
915       }
916     else
917       {
918         result_set.assign_response (id, shm_data);
919       }
920     return std::make_pair (VAPI_OK, false);
921   }
922
923   vapi_error_e execute ()
924   {
925     return con.send_with_control_ping (this);
926   }
927
928   Msg<Req> &get_request (void)
929   {
930     return request;
931   }
932
933   using resp_type = typename Msg<Resp>::shm_data_type;
934
935   const Result_set<Resp> &get_result_set (void) const
936   {
937     return result_set;
938   }
939
940 private:
941   Msg<Req> request;
942   Result_set<resp_type> result_set;
943   std::function<vapi_error_e (Dump<Req, Resp, Args...> &)> callback;
944
945   friend class Connection;
946 };
947
948 /**
949  * Class representing event registration - incoming events (messages) from
950  * vpp as a result of a subscription (typically a want_* simple request)
951  */
952 template <typename M> class Event_registration : public Common_req
953 {
954 public:
955   Event_registration (
956       Connection &con,
957       std::function<vapi_error_e (Event_registration<M> &)> callback = nullptr)
958       : Common_req{con}, result_set{con}, callback{callback}
959   {
960     if (!con.is_msg_available (M::get_msg_id ()))
961       {
962         throw Msg_not_available_exception ();
963       }
964     con.register_event (this);
965   }
966
967   Event_registration (const Event_registration &) = delete;
968
969   virtual ~Event_registration ()
970   {
971     con.unregister_event (this);
972   }
973
974   virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id,
975                                                           void *shm_data)
976   {
977     result_set.assign_response (id, shm_data);
978     if (nullptr != callback)
979       {
980         return std::make_pair (callback (*this), true);
981       }
982     return std::make_pair (VAPI_OK, true);
983   }
984
985   using resp_type = typename M::shm_data_type;
986
987   Result_set<resp_type> &get_result_set (void)
988   {
989     return result_set;
990   }
991
992 private:
993   Result_set<resp_type> result_set;
994   std::function<vapi_error_e (Event_registration<M> &)> callback;
995 };
996 };
997
998 #endif
999
1000 /*
1001  * fd.io coding-style-patch-verification: ON
1002  *
1003  * Local Variables:
1004  * eval: (c-set-style "gnu")
1005  * End:
1006  */