VCL: refactor async & spinlocks
[vpp.git] / src / vcl / vcl_event.c
1 /*
2  * Copyright (c) 2019 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <vppinfra/fifo.h>
17 #include <vppinfra/pool.h>
18 #include <vppinfra/hash.h>
19 #include <vnet/api_errno.h>
20
21 #include <vcl/vppcom.h>
22 #include <vcl/vcl_event.h>
23
24 /**
25  * @file
26  * @brief VPP Communications Library (VCL) event handler.
27  *
28  * Definitions for generic event handling in VCL.
29  */
30
31 int
32 vce_generate_event (vce_event_thread_t *evt, u32 ev_idx)
33 {
34   int elts, rv = 0;
35   vce_event_t *p;
36
37   pthread_mutex_lock (&(evt->generator_lock));
38
39   /* Check there is event data for this event */
40
41   VCE_EVENTS_LOCK();
42   p =  pool_elt_at_index (evt->vce_events, ev_idx);
43   ASSERT(p);
44
45   elts = (int) clib_fifo_free_elts (evt->event_index_fifo);
46   if (PREDICT_TRUE (elts))
47     {
48       /* Add event to queue */
49       clib_fifo_add1 (evt->event_index_fifo, ev_idx);
50       pthread_cond_signal (&(evt->generator_cond));
51     }
52   else
53     {
54       rv = VNET_API_ERROR_QUEUE_FULL;
55     }
56
57   VCE_EVENTS_UNLOCK ();
58   pthread_mutex_unlock (&(evt->generator_lock));
59
60   return rv;
61 }
62
63 void
64 vce_clear_event (vce_event_thread_t *evt, u32 ev_idx)
65 {
66   VCE_EVENTS_LOCK ();
67   pool_put_index (evt->vce_events, ev_idx);
68   VCE_EVENTS_UNLOCK ();
69 }
70
71 vce_event_t *
72 vce_get_event_from_index(vce_event_thread_t *evt, u32 ev_idx)
73 {
74   vce_event_t *ev = 0;
75   /* Assumes caller has obtained the spinlock (evt->events_lockp) */
76
77   if ( ! pool_is_free_index (evt->vce_events, ev_idx))
78     ev = pool_elt_at_index (evt->vce_events, ev_idx);
79
80   return ev;
81 }
82
83 vce_event_handler_reg_t *
84 vce_get_event_handler (vce_event_thread_t *evt, vce_event_key_t *evk)
85 {
86   vce_event_handler_reg_t *handler = 0;
87   uword *p;
88
89   VCE_HANDLERS_LOCK ();
90   p = hash_get (evt->handlers_index_by_event_key, evk->as_u64);
91   if (p)
92     handler = pool_elt_at_index (evt->vce_event_handlers, p[0]);
93   VCE_HANDLERS_UNLOCK ();
94
95   return handler;
96 }
97
98 vce_event_handler_reg_t *
99 vce_register_handler (vce_event_thread_t *evt, vce_event_key_t *evk,
100                       vce_event_callback_t cb, void *cb_args)
101 {
102   vce_event_handler_reg_t *handler;
103   vce_event_handler_reg_t *old_handler = 0;
104   uword *p;
105   u32 handler_index;
106
107   /* TODO - multiple handler support. For now we can replace
108    * and re-instate, which is useful for event recycling */
109
110   VCE_HANDLERS_LOCK ();
111
112   p = hash_get (evt->handlers_index_by_event_key, evk->as_u64);
113   if (p)
114     {
115       old_handler = pool_elt_at_index (evt->vce_event_handlers, p[0]);
116       /* If we are just re-registering, ignore and move on
117        * else store the old handler_fn for unregister to re-instate */
118       if (old_handler->handler_fn == cb)
119         {
120
121           VCE_HANDLERS_UNLOCK ();
122
123           /* Signal event thread that a handler exists in case any
124            * recycled events requiring this handler are pending */
125           pthread_mutex_lock (&(evt->generator_lock));
126           pthread_cond_signal (&(evt->generator_cond));
127           pthread_mutex_unlock (&(evt->generator_lock));
128           return old_handler;
129         }
130     }
131
132   pool_get (evt->vce_event_handlers, handler);
133   handler_index = (u32) (handler - evt->vce_event_handlers);
134
135   handler->handler_fn = cb;
136   handler->replaced_handler_idx = (u32) ((p) ? p[0] : ~0);
137   handler->ev_idx = (u32) ~0; //This will be set by the event thread if event happens
138   handler->evk = evk->as_u64;
139   handler->handler_fn_args = cb_args;
140
141   hash_set (evt->handlers_index_by_event_key, evk->as_u64, handler_index);
142
143   pthread_cond_init (&(handler->handler_cond), NULL);
144   pthread_mutex_init (&(handler->handler_lock), NULL);
145
146   VCE_HANDLERS_UNLOCK ();
147
148   /* Signal event thread that a new handler exists in case any
149    * recycled events requiring this handler are pending */
150   pthread_mutex_lock (&(evt->generator_lock));
151   pthread_cond_signal (&(evt->generator_cond));
152   pthread_mutex_unlock (&(evt->generator_lock));
153
154   return handler;
155 }
156
157 int
158 vce_unregister_handler (vce_event_thread_t *evt,
159                         vce_event_handler_reg_t *handler)
160 {
161   uword *p;
162   u64 evk = handler->evk;
163   u8 generate_signal = 0;
164
165   VCE_HANDLERS_LOCK ();
166
167   p = hash_get (evt->handlers_index_by_event_key, evk);
168   if (!p)
169     {
170       VCE_HANDLERS_UNLOCK ();
171       return VNET_API_ERROR_NO_SUCH_ENTRY;
172     }
173
174   handler = pool_elt_at_index (evt->vce_event_handlers, p[0]);
175
176   /* If this handler replaced another handler, re-instate it */
177   if (handler->replaced_handler_idx != ~0)
178     {
179       hash_set (evt->handlers_index_by_event_key, evk,
180                 handler->replaced_handler_idx);
181       generate_signal = 1;
182     }
183   else
184     {
185       hash_unset (evt->handlers_index_by_event_key, evk);
186     }
187
188   pthread_mutex_destroy (&(handler->handler_lock));
189   pthread_cond_destroy (&(handler->handler_cond));
190   pool_put (evt->vce_event_handlers, handler);
191
192   VCE_HANDLERS_UNLOCK ();
193
194   if (generate_signal)
195     {
196       /* Signal event thread that a new handler exists in case any
197        * recycled events requiring this handler are pending */
198       pthread_mutex_lock (&(evt->generator_lock));
199       pthread_cond_signal (&(evt->generator_cond));
200       pthread_mutex_unlock (&(evt->generator_lock));
201     }
202
203   return 0;
204 }
205
206 void *
207 vce_event_thread_fn (void *arg)
208 {
209   vce_event_thread_t *evt = (vce_event_thread_t *) arg;
210   vce_event_t *ev;
211   u32 ev_idx;
212   vce_event_handler_reg_t *handler;
213   uword *p;
214   u32 recycle_count = 0;
215
216   pthread_mutex_lock (&(evt->generator_lock));
217   while (1)
218     {
219       uword fifo_depth = clib_fifo_elts (evt->event_index_fifo);
220       while ((fifo_depth == 0) || (recycle_count == fifo_depth))
221         {
222           recycle_count = 0;
223           pthread_cond_wait (&(evt->generator_cond), &(evt->generator_lock));
224           fifo_depth = clib_fifo_elts (evt->event_index_fifo);
225         }
226
227       /* Remove event */
228       VCE_EVENTS_LOCK ();
229       clib_fifo_sub1 (evt->event_index_fifo, ev_idx);
230       ev = vce_get_event_from_index (evt, ev_idx);
231       ASSERT(ev);
232       if (recycle_count && ev->recycle)
233         {
234           clib_fifo_add1 (evt->event_index_fifo, ev_idx);
235           VCE_EVENTS_UNLOCK ();
236           continue;
237         }
238       VCE_HANDLERS_LOCK ();
239
240       p = hash_get (evt->handlers_index_by_event_key, ev->evk.as_u64);
241       if (!p)
242         {
243           /* If an event falls in the woods, and there is no handler to hear it,
244            * does it make any sound?
245            * I don't know either, so lets biff the event */
246           pool_put(evt->vce_events, ev);
247           VCE_EVENTS_UNLOCK ();
248           VCE_HANDLERS_UNLOCK ();
249           pthread_mutex_unlock (&(evt->generator_lock));
250         }
251       else
252         {
253           u32 evt_recycle = ev->recycle;
254           handler = pool_elt_at_index (evt->vce_event_handlers, p[0]);
255           handler->ev_idx = ev_idx;
256           ev->recycle = 0;
257
258           VCE_EVENTS_UNLOCK ();
259           VCE_HANDLERS_UNLOCK ();
260           pthread_mutex_unlock (&(evt->generator_lock));
261
262           (handler->handler_fn)(handler);
263
264           VCE_EVENTS_LOCK ();
265           ev = vce_get_event_from_index (evt, ev_idx);
266           recycle_count += (!evt_recycle && ev && ev->recycle) ? 1 : 0;
267           VCE_EVENTS_UNLOCK ();
268         }
269
270       pthread_mutex_lock (&(evt->generator_lock));
271     }
272   return NULL;
273 }
274
275 int
276 vce_start_event_thread (vce_event_thread_t *evt, u8 max_events)
277 {
278   clib_fifo_validate (evt->event_index_fifo, max_events);
279   evt->handlers_index_by_event_key = hash_create (0, sizeof (uword));
280
281   pthread_cond_init (&(evt->generator_cond), NULL);
282   pthread_mutex_init (&(evt->generator_lock), NULL);
283
284   clib_spinlock_init (&(evt->events_lockp));
285   clib_spinlock_init (&(evt->handlers_lockp));
286
287   return pthread_create (&(evt->thread), NULL /* attr */ ,
288                          vce_event_thread_fn, evt);
289 }
290