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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 * @brief Callback multiplex scheme
20 #ifndef included_callback_data_h
21 #define included_callback_data_h
22 #include <vppinfra/clib.h>
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
28 #define clib_callback_data_typedef(set_t_, cb_t_) \
29 typedef struct set_t_ \
32 cb_t_* volatile next; \
34 clib_spinlock_t* lock; \
37 /** @brief Initialize a callback set
38 * @param set_ The callback set to initialize
39 * @param lock_ The lock to use, if any
41 #define clib_callback_data_init(set_,lock_) \
43 (set_)->lock = (lock_); \
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
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.
57 #define clib_callback_data_add(set_,value_) \
59 clib_spinlock_lock_if_init ((set_)->lock); \
60 typeof ((set_)->next) next_ = (set_)->next; \
61 if (PREDICT_TRUE (next_ == 0)) \
63 next_ = (set_)->spare; \
65 vec_append (next_, (set_)->curr); \
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); \
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
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
84 #define clib_callback_data_remove(set_,fp_) \
87 clib_spinlock_lock_if_init ((set_)->lock); \
88 typeof ((set_)->next) next_ = (set_)->next; \
89 if (PREDICT_TRUE (next_ == 0)) \
91 next_ = (set_)->spare; \
93 vec_append (next_, (set_)->curr); \
95 u32 sz_ = vec_len (next_); \
97 for (i_ = 0; i_ < sz_; i_++) \
98 if (next_[i_].fp == (fp_)) \
100 vec_delete (next_, 1, i_); \
104 (set_)->next = next_; \
105 clib_spinlock_unlock_if_init ((set_)->lock); \
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
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.
120 #define clib_callback_data_swap(set_,fp_,value_) \
123 clib_spinlock_lock_if_init ((set_)->lock); \
124 typeof ((set_)->next) next_ = (set_)->next; \
125 if (PREDICT_TRUE (next_ == 0)) \
127 next_ = (set_)->spare; \
129 vec_append (next_, (set_)->curr); \
131 u32 sz_ = vec_len (next_); \
133 for (i_ = 0; i_ < sz_; i_++) \
134 if (next_[i_].fp == (fp_)) \
136 next_[i_] = (value_); \
140 (set_)->next = next_; \
141 clib_spinlock_unlock_if_init ((set_)->lock); \
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
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.
156 #define clib_callback_data_ensure(set_,value_) \
159 clib_spinlock_lock_if_init ((set_)->lock); \
160 typeof ((set_)->next) next_ = (set_)->next; \
161 if (PREDICT_TRUE (next_ == 0)) \
163 next_ = (set_)->spare; \
165 vec_append (next_, (set_)->curr); \
167 u32 sz_ = vec_len (next_); \
169 for (i_ = 0; i_ < sz_; i_++) \
170 if (next_[i_].fp == (value_).fp) \
176 vec_validate (next_, i_); \
177 next_[i_] = (value_); \
178 (set_)->next = next_; \
179 clib_spinlock_unlock_if_init ((set_)->lock); \
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
187 * Enable or disable a callback function, with no data.
189 #define clib_callback_data_enable_disable(set_,fp_,ena_) \
193 typeof ((set_)->next[0]) data_ = { .fp = (fp_) }; \
194 clib_callback_data_add ((set_), data_); \
197 clib_callback_data_remove ((set_), (fp_)); \
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
206 #define clib_callback_data_get_value(set_,fp_,v_) \
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_); \
215 for (i_ = 0; i_ < sz_; i_++) \
216 if (search_[i_].fp == (fp_)) \
222 clib_spinlock_unlock_if_init ((set_)->lock); \
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
231 #define clib_callback_data_is_set(set_,fp_) \
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_); \
240 for (i_ = 0; i_ < sz_; i_++) \
241 if (search_[i_].fp == (fp_)) \
246 clib_spinlock_unlock_if_init ((set_)->lock); \
250 /** @brief Check for and get current callback set
251 * @param set_ the callback set
252 * @param varargs additional callback parameters
254 #define clib_callback_data_check_and_get(set_) \
256 typeof ((set_)->curr) curr_ = (set_)->curr; \
257 if (PREDICT_FALSE ((set_)->next != 0)) \
259 clib_spinlock_lock_if_init ((set_)->lock); \
260 vec_reset_length (curr_); \
261 (set_)->spare = curr_; \
262 curr_ = (set_)->next; \
264 if (PREDICT_FALSE (0 == vec_len (curr_))) \
266 (set_)->curr = curr_; \
267 clib_spinlock_unlock_if_init ((set_)->lock); \
272 /** @brief Iterate and call a callback vector
273 * @param vec_ the callback vector
274 * @param varargs additional callback parameters
276 #define clib_callback_data_call_vec(vec_, ...) \
279 u32 sz_ = vec_len (vec_); \
281 for (i_ = 0; i_ < sz_; i_++) \
283 clib_prefetch_store (&vec_[i_ + 1]); \
284 (vec_[i_].fp) (&vec_[i_], __VA_ARGS__); \
289 /** @brief Call the specified callback set
290 * @param set_ the callback set
291 * @param varargs additional callback parameters
293 #define clib_callback_data_call(set_, ...) \
295 typeof ((set_)->curr) v_ = clib_callback_data_check_and_get(set_); \
296 clib_callback_data_iterate (v_, __VA_ARGS__); \
299 /** @brief prefetch the callback set
300 * @param set_ The callback set
302 #define clib_callback_data_prefetch(set_) \
305 if (PREDICT_FALSE ((set_)->curr)) \
306 clib_prefetch_store ((set_)->curr); \
310 #endif /* included_callback_data_h */
313 * fd.io coding-style-patch-verification: ON
316 * eval: (c-set-style "gnu")