1 /**
2 * Copyright (C) Mellanox Technologies Ltd. 2001-2016.  ALL RIGHTS RESERVED.
3 * Copyright (C) ARM Ltd. 2016-2017.  ALL RIGHTS RESERVED.
4 *
5 * See file LICENSE for terms.
6 */
7 
8 #ifndef UCS_CALLBACKQ_H
9 #define UCS_CALLBACKQ_H
10 
11 #include <ucs/datastruct/list.h>
12 #include <ucs/sys/compiler_def.h>
13 #include <ucs/type/status.h>
14 #include <stddef.h>
15 #include <stdint.h>
16 
17 BEGIN_C_DECLS
18 
19 /** @file callbackq.h */
20 
21 /*
22  * Thread-safe callback queue:
23  *  - only one thread can dispatch
24  *  - any thread can add and remove
25  *  - add/remove operations are O(1)
26  */
27 
28 #define UCS_CALLBACKQ_FAST_COUNT   7     /* Max. number of fast-path callbacks */
29 #define UCS_CALLBACKQ_ID_NULL      (-1)  /* Invalid callback identifier */
30 
31 
32 /*
33  * Forward declarations
34  */
35 typedef struct ucs_callbackq       ucs_callbackq_t;
36 typedef struct ucs_callbackq_elem  ucs_callbackq_elem_t;
37 
38 
39 /**
40  * Callback which can be placed in a queue.
41  *
42  * @param [in] arg  User-defined argument for the callback.
43  *
44  * @return Count of how much "work" was done by the callback. For example, zero
45  *         means that no work was done, and any nonzero value means that something
46  *         was done.
47  */
48 typedef unsigned (*ucs_callback_t)(void *arg);
49 
50 
51 /**
52  * Callback queue element predicate.
53  *
54  * @param [in] elem  Callback queue element to check.
55  * @param [in] arg   User-defined argument.
56  *
57  * @return Predicate result value - nonzero means "true", zero means "false".
58  */
59 typedef int (*ucs_callbackq_predicate_t)(const ucs_callbackq_elem_t *elem,
60                                          void *arg);
61 
62 
63 /**
64  * @ingroup UCS_RESOURCE
65  * Callback flags
66  */
67 enum ucs_callbackq_flags {
68     UCS_CALLBACKQ_FLAG_FAST        = UCS_BIT(0), /**< Fast-path (best effort) */
69     UCS_CALLBACKQ_FLAG_ONESHOT     = UCS_BIT(1)  /**< Call the callback only once
70                                                       (cannot be used with FAST) */
71 };
72 
73 
74 /**
75  * Callback queue element.
76  */
77 struct ucs_callbackq_elem {
78     ucs_callback_t                 cb;       /**< Callback function */
79     void                           *arg;     /**< Function argument */
80     unsigned                       flags;    /**< Callback flags */
81     int                            id;       /**< Callback id */
82 };
83 
84 
85 /**
86  * A queue of callback to execute
87  */
88 struct ucs_callbackq {
89     /**
90      * Array of fast-path element, the last is reserved as a sentinel to mark
91      * array end.
92      */
93     ucs_callbackq_elem_t           fast_elems[UCS_CALLBACKQ_FAST_COUNT + 1];
94 
95     /**
96      * Private data, which we don't want to expose in API to avoid pulling
97      * more header files
98      */
99     char                           priv[72];
100 };
101 
102 
103 /**
104  * Initialize the callback queue.
105  *
106  * @param  [in] cbq      Callback queue to initialize.
107  */
108 ucs_status_t ucs_callbackq_init(ucs_callbackq_t *cbq);
109 
110 
111 /**
112  * Clean up the callback queue and release associated memory.
113  *
114  * @param  [in] cbq      Callback queue to clean up.
115  */
116 void ucs_callbackq_cleanup(ucs_callbackq_t *cbq);
117 
118 
119 /**
120  * Add a callback to the queue.
121  * This is *not* safe to call while another thread might be dispatching callbacks.
122  * However, it can be used from the dispatch context (e.g a callback may use this
123  * function to add another callback).
124  *
125  * @param  [in] cbq      Callback queue to add the callback to.
126  * @param  [in] cb       Callback to add.
127  * @param  [in] arg      User-defined argument for the callback.
128  * @param  [in] flags    Flags for the callback, from  @ref ucs_callbackq_flags.
129  *
130  * @return Unique identifier of the callback in the queue.
131  */
132 int ucs_callbackq_add(ucs_callbackq_t *cbq, ucs_callback_t cb, void *arg,
133                       unsigned flags);
134 
135 
136 /**
137  * Remove a callback from the queue immediately.
138  * This is *not* safe to call while another thread might be dispatching callbacks.
139  * However, it can be used from the dispatch context (e.g a callback may use this
140  * function to remove itself or another callback). In this case, the callback may
141  * still be dispatched once after this function returned.
142  *
143  * @param  [in] cbq      Callback queue to remove the callback from.
144  * @param  [in] id       Callback identifier to remove.
145  */
146 void ucs_callbackq_remove(ucs_callbackq_t *cbq, int id);
147 
148 
149 /**
150  * Add a callback to the queue.
151  * This can be used from any context and any thread, including but not limited to:
152  * - A callback can add another callback.
153  * - A thread can add a callback while another thread is dispatching callbacks.
154  *
155  * @param  [in] cbq      Callback queue to add the callback to.
156  * @param  [in] cb       Callback to add.
157  * @param  [in] arg      User-defined argument for the callback.
158  * @param  [in] flags    Flags for the callback, from  @ref ucs_callbackq_flags.
159  *
160  * @return Unique identifier of the callback in the queue.
161  */
162 int ucs_callbackq_add_safe(ucs_callbackq_t *cbq, ucs_callback_t cb, void *arg,
163                            unsigned flags);
164 
165 
166 /**
167  * Remove a callback from the queue in a safe but lazy fashion. The callback will
168  * be removed at some point in the near future.
169  * This can be used from any context and any thread, including but not limited to:
170  * - A callback can remove another callback or itself.
171  * - A thread can't remove a callback while another thread is dispatching callbacks.
172  *
173  * @param  [in] cbq      Callback queue to remove the callback from.
174  * @param  [in] id       Callback identifier to remove.
175  */
176 void ucs_callbackq_remove_safe(ucs_callbackq_t *cbq, int id);
177 
178 
179 /**
180  * Remove all callbacks from the queue for which the given predicate returns
181  * "true" (nonzero) value.
182  * This is *not* safe to call while another thread might be dispatching callbacks.
183  * However, it can be used from the dispatch context (e.g a callback may use this
184  * function to remove itself or another callback). In this case, the callback may
185  * still be dispatched once after this function returned.
186  *
187  * @param  [in] cbq       Callback queue.
188  * @param  [in] pred      Predicate to check candidates for removal.
189  * @param  [in] arg       User-defined argument for the predicate.
190  */
191 void ucs_callbackq_remove_if(ucs_callbackq_t *cbq, ucs_callbackq_predicate_t pred,
192                              void *arg);
193 
194 
195 /**
196  * Dispatch callbacks from the callback queue.
197  * Must be called from single thread only.
198  *
199  * @param  [in] cbq      Callback queue to dispatch callbacks from.
200 
201  * @return Sum of all return values from the dispatched callbacks.
202  */
ucs_callbackq_dispatch(ucs_callbackq_t * cbq)203 static inline unsigned ucs_callbackq_dispatch(ucs_callbackq_t *cbq)
204 {
205     ucs_callbackq_elem_t *elem;
206     ucs_callback_t cb;
207     unsigned count;
208 
209     count = 0;
210     for (elem = cbq->fast_elems; (cb = elem->cb) != NULL; ++elem) {
211         count += cb(elem->arg);
212     }
213     return count;
214 }
215 
216 END_C_DECLS
217 
218 #endif
219