blob: 9a1ad0a9778e20ebf4b8f6240cd0e56076c81c99 [file] [log] [blame]
/*
* Copyright (c) 2020 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/** @file
* @brief Callback multiplex scheme
*/
#ifndef included_callback_data_h
#define included_callback_data_h
#include <vppinfra/clib.h>
/** @brief Declare and define a callback set type
* @param set_t_ The set type to define
* @param cb_t_ The callback type to use
*/
#define clib_callback_data_typedef(set_t_, cb_t_) \
typedef struct set_t_ \
{ \
cb_t_* curr; \
cb_t_* volatile next; \
cb_t_* spare; \
clib_spinlock_t* lock; \
} set_t_
/** @brief Initialize a callback set
* @param set_ The callback set to initialize
* @param lock_ The lock to use, if any
*/
#define clib_callback_data_init(set_,lock_) \
do { \
(set_)->lock = (lock_); \
(set_)->curr = 0; \
(set_)->next = 0; \
(set_)->spare = 0; \
} while (0)
/** @brief Add a callback to the specified callback set
* @param set_ The callback set
* @param value_ The value_ to assign the callback
*
* Add a callback from the indicated callback set. If the set is
* currently being iterated, then the change will be applied after the
* current full iteration, and prior to the next full iteration.
*/
#define clib_callback_data_add(set_,value_) \
do { \
clib_spinlock_lock_if_init ((set_)->lock); \
typeof ((set_)->next) next_ = (set_)->next; \
if (PREDICT_TRUE (next_ == 0)) \
{ \
next_ = (set_)->spare; \
(set_)->spare = 0; \
vec_append (next_, (set_)->curr); \
} \
u32 sz_ = vec_len (next_); \
vec_validate (next_, sz_); \
next_[sz_] = (value_); \
(set_)->next = next_; \
clib_spinlock_unlock_if_init ((set_)->lock); \
} while (0)
/** @brief Remove a callback from the specified callback set
* @param set_ The callback set
* @param fp_ The current callback function
* @return 1 if the function was removed, 0 if not
*
* Remove a callback from the indicated callback set. Idempotent. If
* the set is currently being iterated, then the change will be applied
* after the current full iteration, and prior to the next full
* iteration.
*/
#define clib_callback_data_remove(set_,fp_) \
({ \
int found_ = 0; \
clib_spinlock_lock_if_init ((set_)->lock); \
typeof ((set_)->next) next_ = (set_)->next; \
if (PREDICT_TRUE (next_ == 0)) \
{ \
next_ = (set_)->spare; \
(set_)->spare = 0; \
vec_append (next_, (set_)->curr); \
} \
u32 sz_ = vec_len (next_); \
u32 i_; \
for (i_ = 0; i_ < sz_; i_++) \
if (next_[i_].fp == (fp_)) \
{ \
vec_delete (next_, 1, i_); \
found_ = 1; \
break; \
} \
(set_)->next = next_; \
clib_spinlock_unlock_if_init ((set_)->lock); \
found_; \
})
/** @brief Swap a callback in the specified callback set
* @param set_ The callback set
* @param fp_ The current callback function
* @param value_ The value_ to assign the callback
* @return 1 if the function was swapped, 0 if not
*
* Swap a callback in the indicated callback set. If the callback is
* not found, then nothing is done. If the set is currently being
* iterated, then the change will be applied after the current full
* iteration, and prior to the next full iteration.
*/
#define clib_callback_data_swap(set_,fp_,value_) \
({ \
int found_ = 0; \
clib_spinlock_lock_if_init ((set_)->lock); \
typeof ((set_)->next) next_ = (set_)->next; \
if (PREDICT_TRUE (next_ == 0)) \
{ \
next_ = (set_)->spare; \
(set_)->spare = 0; \
vec_append (next_, (set_)->curr); \
} \
u32 sz_ = vec_len (next_); \
u32 i_; \
for (i_ = 0; i_ < sz_; i_++) \
if (next_[i_].fp == (fp_)) \
{ \
next_[i_] = (value_); \
found_ = 1; \
break; \
} \
(set_)->next = next_; \
clib_spinlock_unlock_if_init ((set_)->lock); \
found_; \
})
/** @brief Ensure a callback is in the specified callback set
* @param set_ The callback set
* @param value_ The value_ to assign the callback
* @return 1 if the function was swapped, 0 if not
*
* Add or swap a callback in the indicated callback set. If the
* callback is already in the set, it is replaced. If the callback is
* not found, then it is added. If the set is currently being
* iterated, then the change will be applied after the current full
* iteration, and prior to the next full iteration.
*/
#define clib_callback_data_ensure(set_,value_) \
do { \
int found_ = 0; \
clib_spinlock_lock_if_init ((set_)->lock); \
typeof ((set_)->next) next_ = (set_)->next; \
if (PREDICT_TRUE (next_ == 0)) \
{ \
next_ = (set_)->spare; \
(set_)->spare = 0; \
vec_append (next_, (set_)->curr); \
} \
u32 sz_ = vec_len (next_); \
u32 i_; \
for (i_ = 0; i_ < sz_; i_++) \
if (next_[i_].fp == (value_).fp) \
{ \
found_ = 1; \
break; \
} \
if (!found_) \
vec_validate (next_, i_); \
next_[i_] = (value_); \
(set_)->next = next_; \
clib_spinlock_unlock_if_init ((set_)->lock); \
} while(0)
/** @brief Enable/Disable the specified callback
* @param set_ The callback set
* @param fp_ The callback function
* @param ena_ 1 to enable, 0 to disable
*
* Enable or disable a callback function, with no data.
*/
#define clib_callback_data_enable_disable(set_,fp_,ena_) \
do { \
if (ena_) \
{ \
typeof ((set_)->next[0]) data_ = { .fp = (fp_) }; \
clib_callback_data_add ((set_), data_); \
} \
else \
clib_callback_data_remove ((set_), (fp_)); \
} while (0)
/** @brief Get the value of a callback, if set.
* @param set_ The callback set
* @param fp_ The callback function
* @param v_ Set to the callback's current value
* @return 1 if the function is in the set, 0 if not
*/
#define clib_callback_data_get_value(set_,fp_,v_) \
({ \
int found_ = 0; \
clib_spinlock_lock_if_init ((set_)->lock); \
typeof ((set_)->next) search_ = (set_)->next; \
if (PREDICT_TRUE (search_ == 0)) \
search_ = (set_)->curr; \
u32 sz_ = vec_len (search_); \
u32 i_; \
for (i_ = 0; i_ < sz_; i_++) \
if (search_[i_].fp == (fp_)) \
{ \
(v_) = search_[i]; \
found_ = 1; \
break; \
} \
clib_spinlock_unlock_if_init ((set_)->lock); \
found_; \
})
/** @brief Check if callback is set
* @param set_ The callback set
* @param fp_ The callback function
* @return 1 if the function is in the set, 0 if not
*/
#define clib_callback_data_is_set(set_,fp_) \
({ \
int found_ = 0; \
clib_spinlock_lock_if_init ((set_)->lock); \
typeof ((set_)->next) search_ = (set_)->next; \
if (PREDICT_TRUE (search_ == 0)) \
search_ = (set_)->curr; \
u32 sz_ = vec_len (search_); \
u32 i_; \
for (i_ = 0; i_ < sz_; i_++) \
if (search_[i_].fp == (fp_)) \
{ \
found_ = 1; \
break; \
} \
clib_spinlock_unlock_if_init ((set_)->lock); \
found_; \
})
/** @brief Check for and get current callback set
* @param set_ the callback set
* @param varargs additional callback parameters
*/
#define clib_callback_data_check_and_get(set_) \
({ \
typeof ((set_)->curr) curr_ = (set_)->curr; \
if (PREDICT_FALSE ((set_)->next != 0)) \
{ \
clib_spinlock_lock_if_init ((set_)->lock); \
vec_reset_length (curr_); \
(set_)->spare = curr_; \
curr_ = (set_)->next; \
(set_)->next = 0; \
if (PREDICT_FALSE (0 == vec_len (curr_))) \
vec_free (curr_); \
(set_)->curr = curr_; \
clib_spinlock_unlock_if_init ((set_)->lock); \
} \
curr_; \
})
/** @brief Iterate and call a callback vector
* @param vec_ the callback vector
* @param varargs additional callback parameters
*/
#define clib_callback_data_call_vec(vec_, ...) \
do { \
u32 sz_ = vec_len (vec_); \
u32 i_; \
for (i_ = 0; i_ < sz_; i_++) \
{ \
CLIB_PREFETCH (&vec_[i_+1], CLIB_CACHE_LINE_BYTES, STORE); \
(vec_[i_].fp) (&vec_[i_], __VA_ARGS__); \
} \
} while (0)
/** @brief Call the specified callback set
* @param set_ the callback set
* @param varargs additional callback parameters
*/
#define clib_callback_data_call(set_, ...) \
do { \
typeof ((set_)->curr) v_ = clib_callback_data_check_and_get(set_); \
clib_callback_data_iterate (v_, __VA_ARGS__); \
} while (0)
/** @brief prefetch the callback set
* @param set_ The callback set
*/
#define clib_callback_data_prefetch(set_) \
do { \
if (PREDICT_FALSE ((set_)->curr)) \
CLIB_PREFETCH ((set_)->curr, CLIB_CACHE_LINE_BYTES, STORE); \
} while (0)
#endif /* included_callback_data_h */
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/