109af9fc5227a78360c618b61ab3f90180fbddab
[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
207   connect (const char *name, const char *chroot_prefix,
208            int max_outstanding_requests, int response_queue_size,
209            bool handle_keepalives = true, bool use_uds = false)
210   {
211     return vapi_connect_ex (vapi_ctx, name, chroot_prefix,
212                             max_outstanding_requests, response_queue_size,
213                             VAPI_MODE_BLOCKING, handle_keepalives, use_uds);
214   }
215
216   /**
217    * @brief disconnect from vpp
218    *
219    * @return VAPI_OK on success, other error code on error
220    */
221   vapi_error_e disconnect ()
222   {
223     auto x = requests.size ();
224     while (x > 0)
225       {
226         VAPI_DBG ("popping request @%p", requests.front ());
227         requests.pop_front ();
228         --x;
229       }
230     return vapi_disconnect (vapi_ctx);
231   };
232
233   /**
234    * @brief get event file descriptor
235    *
236    * @note this file descriptor becomes readable when messages (from vpp)
237    * are waiting in queue
238    *
239    * @param[out] fd pointer to result variable
240    *
241    * @return VAPI_OK on success, other error code on error
242    */
243   vapi_error_e get_fd (int *fd)
244   {
245     return vapi_get_fd (vapi_ctx, fd);
246   }
247
248   /**
249    * @brief wait for responses from vpp and assign them to appropriate objects
250    *
251    * @param limit stop dispatch after the limit object received it's response
252    *
253    * @return VAPI_OK on success, other error code on error
254    */
255   vapi_error_e dispatch (const Common_req *limit = nullptr, u32 time = 5)
256   {
257     std::lock_guard<std::mutex> lock (dispatch_mutex);
258     vapi_error_e rv = VAPI_OK;
259     bool loop_again = true;
260     while (loop_again)
261       {
262         void *shm_data;
263         size_t shm_data_size;
264         rv = vapi_recv (vapi_ctx, &shm_data, &shm_data_size, SVM_Q_TIMEDWAIT,
265                         time);
266         if (VAPI_OK != rv)
267           {
268             return rv;
269           }
270 #if VAPI_CPP_DEBUG_LEAKS
271         on_shm_data_alloc (shm_data);
272 #endif
273         std::lock_guard<std::recursive_mutex> requests_lock (requests_mutex);
274         std::lock_guard<std::recursive_mutex> events_lock (events_mutex);
275         vapi_msg_id_t id = vapi_lookup_vapi_msg_id_t (
276             vapi_ctx, be16toh (*static_cast<u16 *> (shm_data)));
277         bool has_context = vapi_msg_is_with_context (id);
278         bool break_dispatch = false;
279         Common_req *matching_req = nullptr;
280         if (has_context)
281           {
282             u32 context = *reinterpret_cast<u32 *> (
283                 (static_cast<u8 *> (shm_data) + vapi_get_context_offset (id)));
284             const auto x = requests.front ();
285             matching_req = x;
286             if (context == x->context)
287               {
288                 std::tie (rv, break_dispatch) =
289                     x->assign_response (id, shm_data);
290               }
291             else
292               {
293                 std::tie (rv, break_dispatch) =
294                     x->assign_response (id, nullptr);
295               }
296             if (break_dispatch)
297               {
298                 requests.pop_front ();
299               }
300           }
301         else
302           {
303             if (events[id])
304               {
305                 std::tie (rv, break_dispatch) =
306                     events[id]->assign_response (id, shm_data);
307                 matching_req = events[id];
308               }
309             else
310               {
311                 msg_free (shm_data);
312               }
313           }
314         if ((matching_req && matching_req == limit && break_dispatch) ||
315             VAPI_OK != rv)
316           {
317             return rv;
318           }
319         loop_again = !requests.empty () || (event_count > 0);
320       }
321     return rv;
322   }
323
324   /**
325    * @brief convenience wrapper function
326    */
327   vapi_error_e dispatch (const Common_req &limit)
328   {
329     return dispatch (&limit);
330   }
331
332   /**
333    * @brief wait for response to a specific request
334    *
335    * @param req request to wait for response for
336    *
337    * @return VAPI_OK on success, other error code on error
338    */
339   vapi_error_e wait_for_response (const Common_req &req)
340   {
341     if (RESPONSE_READY == req.get_response_state ())
342       {
343         return VAPI_OK;
344       }
345     return dispatch (req);
346   }
347
348 private:
349   void msg_free (void *shm_data)
350   {
351 #if VAPI_CPP_DEBUG_LEAKS
352     on_shm_data_free (shm_data);
353 #endif
354     vapi_msg_free (vapi_ctx, shm_data);
355   }
356
357   template <template <typename XReq, typename XResp, typename... XArgs>
358             class X,
359             typename Req, typename Resp, typename... Args>
360   vapi_error_e send (X<Req, Resp, Args...> *req)
361   {
362     if (!req)
363       {
364         return VAPI_EINVAL;
365       }
366     u32 req_context =
367         req_context_counter.fetch_add (1, std::memory_order_relaxed);
368     req->request.shm_data->header.context = req_context;
369     vapi_swap_to_be<Req> (req->request.shm_data);
370     std::lock_guard<std::recursive_mutex> lock (requests_mutex);
371     vapi_error_e rv = vapi_send (vapi_ctx, req->request.shm_data);
372     if (VAPI_OK == rv)
373       {
374         VAPI_DBG ("Push %p", req);
375         requests.emplace_back (req);
376         req->set_context (req_context);
377 #if VAPI_CPP_DEBUG_LEAKS
378         on_shm_data_free (req->request.shm_data);
379 #endif
380         req->request.shm_data = nullptr; /* consumed by vapi_send */
381       }
382     else
383       {
384         vapi_swap_to_host<Req> (req->request.shm_data);
385       }
386     return rv;
387   }
388
389   template <template <typename XReq, typename XResp, typename... XArgs>
390             class X,
391             typename Req, typename Resp, typename... Args>
392   vapi_error_e send_with_control_ping (X<Req, Resp, Args...> *req)
393   {
394     if (!req)
395       {
396         return VAPI_EINVAL;
397       }
398     u32 req_context =
399         req_context_counter.fetch_add (1, std::memory_order_relaxed);
400     req->request.shm_data->header.context = req_context;
401     vapi_swap_to_be<Req> (req->request.shm_data);
402     std::lock_guard<std::recursive_mutex> lock (requests_mutex);
403     vapi_error_e rv = vapi_send_with_control_ping (
404         vapi_ctx, req->request.shm_data, req_context);
405     if (VAPI_OK == rv)
406       {
407         VAPI_DBG ("Push %p", req);
408         requests.emplace_back (req);
409         req->set_context (req_context);
410 #if VAPI_CPP_DEBUG_LEAKS
411         on_shm_data_free (req->request.shm_data);
412 #endif
413         req->request.shm_data = nullptr; /* consumed by vapi_send */
414       }
415     else
416       {
417         vapi_swap_to_host<Req> (req->request.shm_data);
418       }
419     return rv;
420   }
421
422   void unregister_request (Common_req *request)
423   {
424     std::lock_guard<std::recursive_mutex> lock (requests_mutex);
425     std::remove (requests.begin (), requests.end (), request);
426   }
427
428   template <typename M> void register_event (Event_registration<M> *event)
429   {
430     const vapi_msg_id_t id = M::get_msg_id ();
431     std::lock_guard<std::recursive_mutex> lock (events_mutex);
432     events[id] = event;
433     ++event_count;
434   }
435
436   template <typename M> void unregister_event (Event_registration<M> *event)
437   {
438     const vapi_msg_id_t id = M::get_msg_id ();
439     std::lock_guard<std::recursive_mutex> lock (events_mutex);
440     events[id] = nullptr;
441     --event_count;
442   }
443
444   vapi_ctx_t vapi_ctx;
445   std::atomic_ulong req_context_counter;
446   std::mutex dispatch_mutex;
447
448   std::recursive_mutex requests_mutex;
449   std::recursive_mutex events_mutex;
450   std::deque<Common_req *> requests;
451   std::vector<Common_req *> events;
452   int event_count;
453
454   template <typename Req, typename Resp, typename... Args>
455   friend class Request;
456
457   template <typename Req, typename Resp, typename... Args> friend class Dump;
458
459   template <typename Req, typename Resp, typename StreamMessage,
460             typename... Args>
461   friend class Stream;
462
463   template <typename M> friend class Result_set;
464
465   template <typename M> friend class Event_registration;
466
467   template <typename M, typename... Args>
468   friend M *vapi_alloc (Connection &con, Args...);
469
470   template <typename M> friend class Msg;
471
472 #if VAPI_CPP_DEBUG_LEAKS
473   void on_shm_data_alloc (void *shm_data)
474   {
475     if (shm_data)
476       {
477         auto pos = shm_data_set.find (shm_data);
478         if (pos == shm_data_set.end ())
479           {
480             shm_data_set.insert (shm_data);
481           }
482         else
483           {
484             printf ("Double-add shm_data @%p!\n", shm_data);
485           }
486       }
487   }
488
489   void on_shm_data_free (void *shm_data)
490   {
491     auto pos = shm_data_set.find (shm_data);
492     if (pos == shm_data_set.end ())
493       {
494         printf ("Freeing untracked shm_data @%p!\n", shm_data);
495       }
496     else
497       {
498         shm_data_set.erase (pos);
499       }
500   }
501   std::unordered_set<void *> shm_data_set;
502 #endif
503 };
504
505 template <typename Req, typename Resp, typename... Args> class Request;
506
507 template <typename Req, typename Resp, typename... Args> class Dump;
508
509 template <typename Req, typename Resp, typename StreamMessage,
510           typename... Args>
511 class Stream;
512
513 template <class, class = void> struct vapi_has_payload_trait : std::false_type
514 {
515 };
516
517 template <class... T> using vapi_void_t = void;
518
519 template <class T>
520 struct vapi_has_payload_trait<T, vapi_void_t<decltype (&T::payload)>>
521     : std::true_type
522 {
523 };
524
525 template <typename M> void vapi_msg_set_msg_id (vapi_msg_id_t id)
526 {
527   Msg<M>::set_msg_id (id);
528 }
529
530 /**
531  * Class representing a message stored in shared memory
532  */
533 template <typename M> class Msg
534 {
535 public:
536   Msg (const Msg &) = delete;
537
538   ~Msg ()
539   {
540     VAPI_DBG ("Destroy Msg<%s>@%p, shm_data@%p",
541               vapi_get_msg_name (get_msg_id ()), this, shm_data);
542     if (shm_data)
543       {
544         con.get ().msg_free (shm_data);
545         shm_data = nullptr;
546       }
547   }
548
549   static vapi_msg_id_t get_msg_id ()
550   {
551     return *msg_id_holder ();
552   }
553
554   template <typename X = M>
555   typename std::enable_if<vapi_has_payload_trait<X>::value,
556                           decltype (X::payload) &>::type
557   get_payload () const
558   {
559     return shm_data->payload;
560   }
561
562 private:
563   Msg (Msg<M> &&msg) : con{msg.con}
564   {
565     VAPI_DBG ("Move construct Msg<%s> from msg@%p to msg@%p, shm_data@%p",
566               vapi_get_msg_name (get_msg_id ()), &msg, this, msg.shm_data);
567     shm_data = msg.shm_data;
568     msg.shm_data = nullptr;
569   }
570
571   Msg<M> &operator= (Msg<M> &&msg)
572   {
573     VAPI_DBG ("Move assign Msg<%s> from msg@%p to msg@%p, shm_data@%p",
574               vapi_get_msg_name (get_msg_id ()), &msg, this, msg.shm_data);
575     con.get ().msg_free (shm_data);
576     con = msg.con;
577     shm_data = msg.shm_data;
578     msg.shm_data = nullptr;
579     return *this;
580   }
581
582   struct Msg_allocator : std::allocator<Msg<M>>
583   {
584     template <class U, class... Args> void construct (U *p, Args &&... args)
585     {
586       ::new ((void *)p) U (std::forward<Args> (args)...);
587     }
588
589     template <class U> struct rebind
590     {
591       typedef Msg_allocator other;
592     };
593   };
594
595   static void set_msg_id (vapi_msg_id_t id)
596   {
597     assert ((VAPI_INVALID_MSG_ID == *msg_id_holder ()) ||
598             (id == *msg_id_holder ()));
599     *msg_id_holder () = id;
600   }
601
602   static vapi_msg_id_t *msg_id_holder ()
603   {
604     static vapi_msg_id_t my_id{VAPI_INVALID_MSG_ID};
605     return &my_id;
606   }
607
608   Msg (Connection &con, void *shm_data) : con{con}
609   {
610     if (!con.is_msg_available (get_msg_id ()))
611       {
612         throw Msg_not_available_exception ();
613       }
614     this->shm_data = static_cast<shm_data_type *> (shm_data);
615     VAPI_DBG ("New Msg<%s>@%p shm_data@%p", vapi_get_msg_name (get_msg_id ()),
616               this, shm_data);
617   }
618
619   void assign_response (vapi_msg_id_t resp_id, void *shm_data)
620   {
621     assert (nullptr == this->shm_data);
622     if (resp_id != get_msg_id ())
623       {
624         throw Unexpected_msg_id_exception ();
625       }
626     this->shm_data = static_cast<M *> (shm_data);
627     vapi_swap_to_host<M> (this->shm_data);
628     VAPI_DBG ("Assign response to Msg<%s>@%p shm_data@%p",
629               vapi_get_msg_name (get_msg_id ()), this, shm_data);
630   }
631
632   std::reference_wrapper<Connection> con;
633   using shm_data_type = M;
634   shm_data_type *shm_data;
635
636   friend class Connection;
637
638   template <typename Req, typename Resp, typename... Args>
639   friend class Request;
640
641   template <typename Req, typename Resp, typename... Args> friend class Dump;
642
643   template <typename Req, typename Resp, typename StreamMessage,
644             typename... Args>
645   friend class Stream;
646
647   template <typename X> friend class Event_registration;
648
649   template <typename X> friend class Result_set;
650
651   friend struct Msg_allocator;
652
653   template <typename X> friend void vapi_msg_set_msg_id (vapi_msg_id_t id);
654 };
655
656 /**
657  * Class representing a simple request - with a single response message
658  */
659 template <typename Req, typename Resp, typename... Args>
660 class Request : public Common_req
661 {
662 public:
663   Request (Connection &con, Args... args,
664            std::function<vapi_error_e (Request<Req, Resp, Args...> &)>
665              callback = nullptr)
666       : Common_req{ con }, callback{ std::move (callback) },
667         request{ con, vapi_alloc<Req> (con, args...) }, response{ con,
668                                                                   nullptr }
669   {
670   }
671
672   Request (const Request &) = delete;
673
674   virtual ~Request ()
675   {
676     if (RESPONSE_NOT_READY == get_response_state ())
677       {
678         con.unregister_request (this);
679       }
680   }
681
682   vapi_error_e execute ()
683   {
684     return con.send (this);
685   }
686
687   const Msg<Req> &get_request (void) const
688   {
689     return request;
690   }
691
692   const Msg<Resp> &get_response (void)
693   {
694     return response;
695   }
696
697 private:
698   virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id,
699                                                           void *shm_data)
700   {
701     assert (RESPONSE_NOT_READY == get_response_state ());
702     response.assign_response (id, shm_data);
703     set_response_state (RESPONSE_READY);
704     if (nullptr != callback)
705       {
706         return std::make_pair (callback (*this), true);
707       }
708     return std::make_pair (VAPI_OK, true);
709   }
710   std::function<vapi_error_e (Request<Req, Resp, Args...> &)> callback;
711   Msg<Req> request;
712   Msg<Resp> response;
713
714   friend class Connection;
715 };
716
717 /**
718  * Class representing iterable set of responses of the same type
719  */
720 template <typename M> class Result_set
721 {
722 public:
723   ~Result_set ()
724   {
725   }
726
727   Result_set (const Result_set &) = delete;
728
729   bool is_complete () const
730   {
731     return complete;
732   }
733
734   size_t size () const
735   {
736     return set.size ();
737   }
738
739   using const_iterator =
740       typename std::vector<Msg<M>,
741                            typename Msg<M>::Msg_allocator>::const_iterator;
742
743   const_iterator begin () const
744   {
745     return set.begin ();
746   }
747
748   const_iterator end () const
749   {
750     return set.end ();
751   }
752
753   void free_response (const_iterator pos)
754   {
755     set.erase (pos);
756   }
757
758   void free_all_responses ()
759   {
760     set.clear ();
761   }
762
763 private:
764   void mark_complete ()
765   {
766     complete = true;
767   }
768
769   void assign_response (vapi_msg_id_t resp_id, void *shm_data)
770   {
771     if (resp_id != Msg<M>::get_msg_id ())
772       {
773         {
774           throw Unexpected_msg_id_exception ();
775         }
776       }
777     else if (shm_data)
778       {
779         vapi_swap_to_host<M> (static_cast<M *> (shm_data));
780         set.emplace_back (con, shm_data);
781         VAPI_DBG ("Result_set@%p emplace_back shm_data@%p", this, shm_data);
782       }
783   }
784
785   Result_set (Connection &con) : con (con), complete{false}
786   {
787   }
788
789   Connection &con;
790   bool complete;
791   std::vector<Msg<M>, typename Msg<M>::Msg_allocator> set;
792
793   template <typename Req, typename Resp, typename StreamMessage,
794             typename... Args>
795   friend class Stream;
796
797   template <typename Req, typename Resp, typename... Args> friend class Dump;
798
799   template <typename X> friend class Event_registration;
800 };
801
802 /**
803  * Class representing a RPC request - zero or more identical responses to a
804  * single request message with a response
805  */
806 template <typename Req, typename Resp, typename StreamMessage,
807           typename... Args>
808 class Stream : public Common_req
809 {
810 public:
811   Stream (
812     Connection &con, Args... args,
813     std::function<vapi_error_e (Stream<Req, Resp, StreamMessage, Args...> &)>
814       cb = nullptr)
815       : Common_req{ con }, request{ con, vapi_alloc<Req> (con, args...) },
816         response{ con, nullptr }, result_set{ con }, callback{ std::move (cb) }
817   {
818   }
819
820   Stream (const Stream &) = delete;
821
822   virtual ~Stream () {}
823
824   virtual std::tuple<vapi_error_e, bool>
825   assign_response (vapi_msg_id_t id, void *shm_data)
826   {
827     if (id == response.get_msg_id ())
828       {
829         response.assign_response (id, shm_data);
830         result_set.mark_complete ();
831         set_response_state (RESPONSE_READY);
832         if (nullptr != callback)
833           {
834             return std::make_pair (callback (*this), true);
835           }
836         return std::make_pair (VAPI_OK, true);
837       }
838     else
839       {
840         result_set.assign_response (id, shm_data);
841       }
842     return std::make_pair (VAPI_OK, false);
843   }
844
845   vapi_error_e
846   execute ()
847   {
848     return con.send (this);
849   }
850
851   const Msg<Req> &
852   get_request (void)
853   {
854     return request;
855   }
856
857   const Msg<Resp> &
858   get_response (void)
859   {
860     return response;
861   }
862
863   using resp_type = typename Msg<StreamMessage>::shm_data_type;
864
865   const Result_set<StreamMessage> &
866   get_result_set (void) const
867   {
868     return result_set;
869   }
870
871 private:
872   Msg<Req> request;
873   Msg<Resp> response;
874   Result_set<StreamMessage> result_set;
875   std::function<vapi_error_e (Stream<Req, Resp, StreamMessage, Args...> &)>
876     callback;
877
878   friend class Connection;
879   friend class Result_set<StreamMessage>;
880 };
881
882 /**
883  * Class representing a dump request - zero or more identical responses to a
884  * single request message
885  */
886 template <typename Req, typename Resp, typename... Args>
887 class Dump : public Common_req
888 {
889 public:
890   Dump (Connection &con, Args... args,
891         std::function<vapi_error_e (Dump<Req, Resp, Args...> &)> callback =
892           nullptr)
893       : Common_req{ con }, request{ con, vapi_alloc<Req> (con, args...) },
894         result_set{ con }, callback{ std::move (callback) }
895   {
896   }
897
898   Dump (const Dump &) = delete;
899
900   virtual ~Dump ()
901   {
902   }
903
904   virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id,
905                                                           void *shm_data)
906   {
907     if (id == vapi_msg_id_control_ping_reply)
908       {
909         con.msg_free (shm_data);
910         result_set.mark_complete ();
911         set_response_state (RESPONSE_READY);
912         if (nullptr != callback)
913           {
914             return std::make_pair (callback (*this), true);
915           }
916         return std::make_pair (VAPI_OK, true);
917       }
918     else
919       {
920         result_set.assign_response (id, shm_data);
921       }
922     return std::make_pair (VAPI_OK, false);
923   }
924
925   vapi_error_e execute ()
926   {
927     return con.send_with_control_ping (this);
928   }
929
930   Msg<Req> &get_request (void)
931   {
932     return request;
933   }
934
935   using resp_type = typename Msg<Resp>::shm_data_type;
936
937   const Result_set<Resp> &get_result_set (void) const
938   {
939     return result_set;
940   }
941
942 private:
943   Msg<Req> request;
944   Result_set<resp_type> result_set;
945   std::function<vapi_error_e (Dump<Req, Resp, Args...> &)> callback;
946
947   friend class Connection;
948 };
949
950 /**
951  * Class representing event registration - incoming events (messages) from
952  * vpp as a result of a subscription (typically a want_* simple request)
953  */
954 template <typename M> class Event_registration : public Common_req
955 {
956 public:
957   Event_registration (
958     Connection &con,
959     std::function<vapi_error_e (Event_registration<M> &)> callback = nullptr)
960       : Common_req{ con }, result_set{ con }, callback{ std::move (callback) }
961   {
962     if (!con.is_msg_available (M::get_msg_id ()))
963       {
964         throw Msg_not_available_exception ();
965       }
966     con.register_event (this);
967   }
968
969   Event_registration (const Event_registration &) = delete;
970
971   virtual ~Event_registration ()
972   {
973     con.unregister_event (this);
974   }
975
976   virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id,
977                                                           void *shm_data)
978   {
979     result_set.assign_response (id, shm_data);
980     if (nullptr != callback)
981       {
982         return std::make_pair (callback (*this), true);
983       }
984     return std::make_pair (VAPI_OK, true);
985   }
986
987   using resp_type = typename M::shm_data_type;
988
989   Result_set<resp_type> &get_result_set (void)
990   {
991     return result_set;
992   }
993
994 private:
995   Result_set<resp_type> result_set;
996   std::function<vapi_error_e (Event_registration<M> &)> callback;
997 };
998 };
999
1000 #endif
1001
1002 /*
1003  * fd.io coding-style-patch-verification: ON
1004  *
1005  * Local Variables:
1006  * eval: (c-set-style "gnu")
1007  * End:
1008  */