vppinfra: toeplitz hash
[vpp.git] / src / vppinfra / callback_data.h
1 /*
2  * Copyright (c) 2020 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
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 /** @file
17  * @brief Callback multiplex scheme
18  */
19
20 #ifndef included_callback_data_h
21 #define included_callback_data_h
22 #include <vppinfra/clib.h>
23
24 /** @brief Declare and define a callback set type
25  * @param set_t_ The set type to define
26  * @param cb_t_ The callback type to use
27  */
28 #define clib_callback_data_typedef(set_t_, cb_t_)   \
29 typedef struct set_t_                               \
30 {                                                   \
31   cb_t_* curr;                                      \
32   cb_t_* volatile next;                             \
33   cb_t_* spare;                                     \
34   clib_spinlock_t* lock;                            \
35 } set_t_
36
37 /** @brief Initialize a callback set
38  * @param set_ The callback set to initialize
39  * @param lock_ The lock to use, if any
40  */
41 #define clib_callback_data_init(set_,lock_)  \
42 do {                                         \
43   (set_)->lock = (lock_);                    \
44   (set_)->curr = 0;                          \
45   (set_)->next = 0;                          \
46   (set_)->spare = 0;                         \
47 } while (0)
48
49 /** @brief Add a callback to the specified callback set
50  * @param set_ The callback set
51  * @param value_ The value_ to assign the callback
52  *
53  * Add a callback from the indicated callback set.  If the set is
54  * currently being iterated, then the change will be applied after the
55  * current full iteration, and prior to the next full iteration.
56  */
57 #define clib_callback_data_add(set_,value_)          \
58 do {                                                 \
59   clib_spinlock_lock_if_init ((set_)->lock);         \
60   typeof ((set_)->next) next_ = (set_)->next;        \
61   if (PREDICT_TRUE (next_ == 0))                     \
62     {                                                \
63       next_ = (set_)->spare;                         \
64       (set_)->spare = 0;                             \
65       vec_append (next_, (set_)->curr);              \
66     }                                                \
67   u32 sz_ = vec_len (next_);                         \
68   vec_validate (next_, sz_);                         \
69   next_[sz_] = (value_);                             \
70   (set_)->next = next_;                              \
71   clib_spinlock_unlock_if_init ((set_)->lock);       \
72 } while (0)
73
74 /** @brief Remove a callback from the specified callback set
75  * @param set_ The callback set
76  * @param fp_ The current callback function
77  * @return 1 if the function was removed, 0 if not
78  *
79  * Remove a callback from the indicated callback set.  Idempotent.  If
80  * the set is currently being iterated, then the change will be applied
81  * after the current full iteration, and prior to the next full
82  * iteration.
83  */
84 #define clib_callback_data_remove(set_,fp_)          \
85 ({                                                   \
86   int found_ = 0;                                    \
87   clib_spinlock_lock_if_init ((set_)->lock);         \
88   typeof ((set_)->next) next_ = (set_)->next;        \
89   if (PREDICT_TRUE (next_ == 0))                     \
90     {                                                \
91       next_ = (set_)->spare;                         \
92       (set_)->spare = 0;                             \
93       vec_append (next_, (set_)->curr);              \
94     }                                                \
95   u32 sz_ = vec_len (next_);                         \
96   u32 i_;                                            \
97   for (i_ = 0; i_ < sz_; i_++)                       \
98     if (next_[i_].fp == (fp_))                       \
99       {                                              \
100         vec_delete (next_, 1, i_);                   \
101         found_ = 1;                                  \
102         break;                                       \
103       }                                              \
104   (set_)->next = next_;                              \
105   clib_spinlock_unlock_if_init ((set_)->lock);       \
106   found_;                                            \
107 })
108
109 /** @brief Swap a callback in the specified callback set
110  * @param set_ The callback set
111  * @param fp_ The current callback function
112  * @param value_ The value_ to assign the callback
113  * @return 1 if the function was swapped, 0 if not
114  *
115  * Swap a callback in the indicated callback set.  If the callback is
116  * not found, then nothing is done.  If the set is currently being
117  * iterated, then the change will be applied after the current full
118  * iteration, and prior to the next full iteration.
119  */
120 #define clib_callback_data_swap(set_,fp_,value_)     \
121 ({                                                   \
122   int found_ = 0;                                    \
123   clib_spinlock_lock_if_init ((set_)->lock);         \
124   typeof ((set_)->next) next_ = (set_)->next;        \
125   if (PREDICT_TRUE (next_ == 0))                     \
126     {                                                \
127       next_ = (set_)->spare;                         \
128       (set_)->spare = 0;                             \
129       vec_append (next_, (set_)->curr);              \
130     }                                                \
131   u32 sz_ = vec_len (next_);                         \
132   u32 i_;                                            \
133   for (i_ = 0; i_ < sz_; i_++)                       \
134     if (next_[i_].fp == (fp_))                       \
135       {                                              \
136         next_[i_] = (value_);                        \
137         found_ = 1;                                  \
138         break;                                       \
139       }                                              \
140   (set_)->next = next_;                              \
141   clib_spinlock_unlock_if_init ((set_)->lock);       \
142   found_;                                            \
143 })
144
145 /** @brief Ensure a callback is in the specified callback set
146  * @param set_ The callback set
147  * @param value_ The value_ to assign the callback
148  * @return 1 if the function was swapped, 0 if not
149  *
150  * Add or swap a callback in the indicated callback set.  If the
151  * callback is already in the set, it is replaced.  If the callback is
152  * not found, then it is added.  If the set is currently being
153  * iterated, then the change will be applied after the current full
154  * iteration, and prior to the next full iteration.
155  */
156 #define clib_callback_data_ensure(set_,value_)       \
157 do {                                                 \
158   int found_ = 0;                                    \
159   clib_spinlock_lock_if_init ((set_)->lock);         \
160   typeof ((set_)->next) next_ = (set_)->next;        \
161   if (PREDICT_TRUE (next_ == 0))                     \
162     {                                                \
163       next_ = (set_)->spare;                         \
164       (set_)->spare = 0;                             \
165       vec_append (next_, (set_)->curr);              \
166     }                                                \
167   u32 sz_ = vec_len (next_);                         \
168   u32 i_;                                            \
169   for (i_ = 0; i_ < sz_; i_++)                       \
170     if (next_[i_].fp == (value_).fp)                 \
171       {                                              \
172         found_ = 1;                                  \
173         break;                                       \
174       }                                              \
175   if (!found_)                                       \
176     vec_validate (next_, i_);                        \
177   next_[i_] = (value_);                              \
178   (set_)->next = next_;                              \
179   clib_spinlock_unlock_if_init ((set_)->lock);       \
180 } while(0)
181
182 /** @brief Enable/Disable the specified callback
183  * @param set_ The callback set
184  * @param fp_ The callback function
185  * @param ena_ 1 to enable, 0 to disable
186  *
187  * Enable or disable a callback function, with no data.
188  */
189 #define clib_callback_data_enable_disable(set_,fp_,ena_)   \
190 do {                                                       \
191   if (ena_)                                                \
192     {                                                      \
193       typeof ((set_)->next[0]) data_ = { .fp = (fp_) };    \
194       clib_callback_data_add ((set_), data_);              \
195     }                                                      \
196   else                                                     \
197     clib_callback_data_remove ((set_), (fp_));             \
198 } while (0)
199
200 /** @brief Get the value of a callback, if set.
201  * @param set_ The callback set
202  * @param fp_ The callback function
203  * @param v_ Set to the callback's current value
204  * @return 1 if the function is in the set, 0 if not
205  */
206 #define clib_callback_data_get_value(set_,fp_,v_)    \
207 ({                                                   \
208   int found_ = 0;                                    \
209   clib_spinlock_lock_if_init ((set_)->lock);         \
210   typeof ((set_)->next) search_ = (set_)->next;      \
211   if (PREDICT_TRUE (search_ == 0))                   \
212     search_ = (set_)->curr;                          \
213   u32 sz_ = vec_len (search_);                       \
214   u32 i_;                                            \
215   for (i_ = 0; i_ < sz_; i_++)                       \
216     if (search_[i_].fp == (fp_))                     \
217       {                                              \
218         (v_) = search_[i];                           \
219         found_ = 1;                                  \
220         break;                                       \
221       }                                              \
222   clib_spinlock_unlock_if_init ((set_)->lock);       \
223   found_;                                            \
224 })
225
226 /** @brief Check if callback is set
227  * @param set_ The callback set
228  * @param fp_ The callback function
229  * @return 1 if the function is in the set, 0 if not
230  */
231 #define clib_callback_data_is_set(set_,fp_)          \
232 ({                                                   \
233   int found_ = 0;                                    \
234   clib_spinlock_lock_if_init ((set_)->lock);         \
235   typeof ((set_)->next) search_ = (set_)->next;      \
236   if (PREDICT_TRUE (search_ == 0))                   \
237     search_ = (set_)->curr;                          \
238   u32 sz_ = vec_len (search_);                       \
239   u32 i_;                                            \
240   for (i_ = 0; i_ < sz_; i_++)                       \
241     if (search_[i_].fp == (fp_))                     \
242       {                                              \
243         found_ = 1;                                  \
244         break;                                       \
245       }                                              \
246   clib_spinlock_unlock_if_init ((set_)->lock);       \
247   found_;                                            \
248 })
249
250 /** @brief Check for and get current callback set
251  * @param set_ the callback set
252  * @param varargs additional callback parameters
253  */
254 #define clib_callback_data_check_and_get(set_)       \
255 ({                                                   \
256   typeof ((set_)->curr) curr_ = (set_)->curr;        \
257   if (PREDICT_FALSE ((set_)->next != 0))             \
258     {                                                \
259       clib_spinlock_lock_if_init ((set_)->lock);     \
260       vec_reset_length (curr_);                      \
261       (set_)->spare = curr_;                         \
262       curr_ = (set_)->next;                          \
263       (set_)->next = 0;                              \
264       if (PREDICT_FALSE (0 == vec_len (curr_)))      \
265         vec_free (curr_);                            \
266       (set_)->curr = curr_;                          \
267       clib_spinlock_unlock_if_init ((set_)->lock);   \
268     }                                                \
269   curr_;                                             \
270 })
271
272 /** @brief Iterate and call a callback vector
273  * @param vec_ the callback vector
274  * @param varargs additional callback parameters
275  */
276 #define clib_callback_data_call_vec(vec_, ...)                                \
277   do                                                                          \
278     {                                                                         \
279       u32 sz_ = vec_len (vec_);                                               \
280       u32 i_;                                                                 \
281       for (i_ = 0; i_ < sz_; i_++)                                            \
282         {                                                                     \
283           clib_prefetch_store (&vec_[i_ + 1]);                                \
284           (vec_[i_].fp) (&vec_[i_], __VA_ARGS__);                             \
285         }                                                                     \
286     }                                                                         \
287   while (0)
288
289 /** @brief Call the specified callback set
290  * @param set_ the callback set
291  * @param varargs additional callback parameters
292  */
293 #define clib_callback_data_call(set_, ...)                           \
294 do {                                                                 \
295   typeof ((set_)->curr) v_ = clib_callback_data_check_and_get(set_); \
296   clib_callback_data_iterate (v_, __VA_ARGS__);                      \
297 } while (0)
298
299 /** @brief prefetch the callback set
300  * @param set_ The callback set
301  */
302 #define clib_callback_data_prefetch(set_)                                     \
303   do                                                                          \
304     {                                                                         \
305       if (PREDICT_FALSE ((set_)->curr))                                       \
306         clib_prefetch_store ((set_)->curr);                                   \
307     }                                                                         \
308   while (0)
309
310 #endif /* included_callback_data_h */
311
312 /*
313  * fd.io coding-style-patch-verification: ON
314  *
315  * Local Variables:
316  * eval: (c-set-style "gnu")
317  * End:
318  */