1 /*
2 * Copyright 2022 Yonggang Luo
3 * SPDX-License-Identifier: MIT
4 *
5 * Extend C11 call_once to support context parameter
6 */
7
8 #ifndef U_CALL_ONCE_H_
9 #define U_CALL_ONCE_H_
10
11 #include <stdbool.h>
12
13 #include "c11/threads.h"
14 #include "macros.h"
15 #include "u_atomic.h"
16
17 #ifdef __cplusplus
18 extern "C" {
19 #endif
20
21 /* The data can be mutable or immutable. */
22 typedef void (*util_call_once_data_func)(const void *data);
23
24 struct util_once_flag {
25 bool called;
26 once_flag flag;
27 };
28 typedef struct util_once_flag util_once_flag;
29
30 #define UTIL_ONCE_FLAG_INIT { false, ONCE_FLAG_INIT }
31
32 /**
33 * This is used to optimize the call to call_once out when the func are
34 * already called and finished, so when util_call_once are called in
35 * hot path it's only incur an extra load instruction cost.
36 */
37 static ALWAYS_INLINE void
util_call_once(util_once_flag * flag,void (* func)(void))38 util_call_once(util_once_flag *flag, void (*func)(void))
39 {
40 if (unlikely(!p_atomic_read_relaxed(&flag->called))) {
41 call_once(&flag->flag, func);
42 p_atomic_set(&flag->called, true);
43 }
44 }
45
46 /**
47 * @brief Wrapper around call_once to pass data to func
48 */
49 void
50 util_call_once_data_slow(once_flag *once, util_call_once_data_func func, const void *data);
51
52 /**
53 * This is used to optimize the call to util_call_once_data_slow out when
54 * the func function are already called and finished,
55 * so when util_call_once_data are called in hot path it's only incur an extra
56 * load instruction cost.
57 */
58 static ALWAYS_INLINE void
util_call_once_data(util_once_flag * flag,util_call_once_data_func func,const void * data)59 util_call_once_data(util_once_flag *flag, util_call_once_data_func func, const void *data)
60 {
61 if (unlikely(!p_atomic_read_relaxed(&flag->called))) {
62 util_call_once_data_slow(&(flag->flag), func, data);
63 p_atomic_set(&flag->called, true);
64 }
65 }
66
67 #ifdef __cplusplus
68 }
69 #endif
70
71 #endif /* U_CALL_ONCE_H_ */
72