vapi: fix coverity warnings
[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{ std::move (callback) },
666         request{ con, vapi_alloc<Req> (con, args...) }, response{ con,
667                                                                   nullptr }
668   {
669   }
670
671   Request (const Request &) = delete;
672
673   virtual ~Request ()
674   {
675     if (RESPONSE_NOT_READY == get_response_state ())
676       {
677         con.unregister_request (this);
678       }
679   }
680
681   vapi_error_e execute ()
682   {
683     return con.send (this);
684   }
685
686   const Msg<Req> &get_request (void) const
687   {
688     return request;
689   }
690
691   const Msg<Resp> &get_response (void)
692   {
693     return response;
694   }
695
696 private:
697   virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id,
698                                                           void *shm_data)
699   {
700     assert (RESPONSE_NOT_READY == get_response_state ());
701     response.assign_response (id, shm_data);
702     set_response_state (RESPONSE_READY);
703     if (nullptr != callback)
704       {
705         return std::make_pair (callback (*this), true);
706       }
707     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   ~Result_set ()
723   {
724   }
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   const_iterator begin () const
743   {
744     return set.begin ();
745   }
746
747   const_iterator end () const
748   {
749     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   void mark_complete ()
764   {
765     complete = true;
766   }
767
768   void assign_response (vapi_msg_id_t resp_id, void *shm_data)
769   {
770     if (resp_id != Msg<M>::get_msg_id ())
771       {
772         {
773           throw Unexpected_msg_id_exception ();
774         }
775       }
776     else if (shm_data)
777       {
778         vapi_swap_to_host<M> (static_cast<M *> (shm_data));
779         set.emplace_back (con, shm_data);
780         VAPI_DBG ("Result_set@%p emplace_back shm_data@%p", this, shm_data);
781       }
782   }
783
784   Result_set (Connection &con) : con (con), complete{false}
785   {
786   }
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   Stream (
811     Connection &con, Args... args,
812     std::function<vapi_error_e (Stream<Req, Resp, StreamMessage, Args...> &)>
813       cb = nullptr)
814       : Common_req{ con }, request{ con, vapi_alloc<Req> (con, args...) },
815         response{ con, nullptr }, result_set{ con }, callback{ std::move (cb) }
816   {
817   }
818
819   Stream (const Stream &) = delete;
820
821   virtual ~Stream () {}
822
823   virtual std::tuple<vapi_error_e, bool>
824   assign_response (vapi_msg_id_t id, void *shm_data)
825   {
826     if (id == response.get_msg_id ())
827       {
828         response.assign_response (id, shm_data);
829         result_set.mark_complete ();
830         set_response_state (RESPONSE_READY);
831         if (nullptr != callback)
832           {
833             return std::make_pair (callback (*this), true);
834           }
835         return std::make_pair (VAPI_OK, true);
836       }
837     else
838       {
839         result_set.assign_response (id, shm_data);
840       }
841     return std::make_pair (VAPI_OK, false);
842   }
843
844   vapi_error_e
845   execute ()
846   {
847     return con.send (this);
848   }
849
850   const Msg<Req> &
851   get_request (void)
852   {
853     return request;
854   }
855
856   const Msg<Resp> &
857   get_response (void)
858   {
859     return response;
860   }
861
862   using resp_type = typename Msg<StreamMessage>::shm_data_type;
863
864   const Result_set<StreamMessage> &
865   get_result_set (void) const
866   {
867     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   Dump (Connection &con, Args... args,
890         std::function<vapi_error_e (Dump<Req, Resp, Args...> &)> callback =
891           nullptr)
892       : Common_req{ con }, request{ con, vapi_alloc<Req> (con, args...) },
893         result_set{ con }, callback{ std::move (callback) }
894   {
895   }
896
897   Dump (const Dump &) = delete;
898
899   virtual ~Dump ()
900   {
901   }
902
903   virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id,
904                                                           void *shm_data)
905   {
906     if (id == vapi_msg_id_control_ping_reply)
907       {
908         con.msg_free (shm_data);
909         result_set.mark_complete ();
910         set_response_state (RESPONSE_READY);
911         if (nullptr != callback)
912           {
913             return std::make_pair (callback (*this), true);
914           }
915         return std::make_pair (VAPI_OK, true);
916       }
917     else
918       {
919         result_set.assign_response (id, shm_data);
920       }
921     return std::make_pair (VAPI_OK, false);
922   }
923
924   vapi_error_e execute ()
925   {
926     return con.send_with_control_ping (this);
927   }
928
929   Msg<Req> &get_request (void)
930   {
931     return request;
932   }
933
934   using resp_type = typename Msg<Resp>::shm_data_type;
935
936   const Result_set<Resp> &get_result_set (void) const
937   {
938     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   Event_registration (
957     Connection &con,
958     std::function<vapi_error_e (Event_registration<M> &)> callback = nullptr)
959       : Common_req{ con }, result_set{ con }, callback{ std::move (callback) }
960   {
961     if (!con.is_msg_available (M::get_msg_id ()))
962       {
963         throw Msg_not_available_exception ();
964       }
965     con.register_event (this);
966   }
967
968   Event_registration (const Event_registration &) = delete;
969
970   virtual ~Event_registration ()
971   {
972     con.unregister_event (this);
973   }
974
975   virtual std::tuple<vapi_error_e, bool> assign_response (vapi_msg_id_t id,
976                                                           void *shm_data)
977   {
978     result_set.assign_response (id, shm_data);
979     if (nullptr != callback)
980       {
981         return std::make_pair (callback (*this), true);
982       }
983     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  */