Branch data Line data Source code
1 : : #ifdef DWC_NOTIFYLIB
2 : :
3 : : #include "dwc_notifier.h"
4 : : #include "dwc_list.h"
5 : :
6 : : typedef struct dwc_observer {
7 : : void *observer;
8 : : dwc_notifier_callback_t callback;
9 : : void *data;
10 : : char *notification;
11 : : DWC_CIRCLEQ_ENTRY(dwc_observer) list_entry;
12 : : } observer_t;
13 : :
14 : : DWC_CIRCLEQ_HEAD(observer_queue, dwc_observer);
15 : :
16 : : typedef struct dwc_notifier {
17 : : void *mem_ctx;
18 : : void *object;
19 : : struct observer_queue observers;
20 : : DWC_CIRCLEQ_ENTRY(dwc_notifier) list_entry;
21 : : } notifier_t;
22 : :
23 : : DWC_CIRCLEQ_HEAD(notifier_queue, dwc_notifier);
24 : :
25 : : typedef struct manager {
26 : : void *mem_ctx;
27 : : void *wkq_ctx;
28 : : dwc_workq_t *wq;
29 : : // dwc_mutex_t *mutex;
30 : : struct notifier_queue notifiers;
31 : : } manager_t;
32 : :
33 : : static manager_t *manager = NULL;
34 : :
35 : 3 : static int create_manager(void *mem_ctx, void *wkq_ctx)
36 : : {
37 : 3 : manager = dwc_alloc(mem_ctx, sizeof(manager_t));
38 : 3 : if (!manager) {
39 : : return -DWC_E_NO_MEMORY;
40 : : }
41 : :
42 : 3 : DWC_CIRCLEQ_INIT(&manager->notifiers);
43 : :
44 : 3 : manager->wq = dwc_workq_alloc(wkq_ctx, "DWC Notification WorkQ");
45 : 3 : if (!manager->wq) {
46 : : return -DWC_E_NO_MEMORY;
47 : : }
48 : :
49 : 3 : return 0;
50 : : }
51 : :
52 : 0 : static void free_manager(void)
53 : : {
54 : 0 : dwc_workq_free(manager->wq);
55 : :
56 : : /* All notifiers must have unregistered themselves before this module
57 : : * can be removed. Hitting this assertion indicates a programmer
58 : : * error. */
59 : : DWC_ASSERT(DWC_CIRCLEQ_EMPTY(&manager->notifiers),
60 : : "Notification manager being freed before all notifiers have been removed");
61 : 0 : dwc_free(manager->mem_ctx, manager);
62 : 0 : }
63 : :
64 : : #ifdef DEBUG
65 : : static void dump_manager(void)
66 : : {
67 : : notifier_t *n;
68 : : observer_t *o;
69 : :
70 : : DWC_ASSERT(manager, "Notification manager not found");
71 : :
72 : : DWC_DEBUG("List of all notifiers and observers:\n");
73 : : DWC_CIRCLEQ_FOREACH(n, &manager->notifiers, list_entry) {
74 : : DWC_DEBUG("Notifier %p has observers:\n", n->object);
75 : : DWC_CIRCLEQ_FOREACH(o, &n->observers, list_entry) {
76 : : DWC_DEBUG(" %p watching %s\n", o->observer, o->notification);
77 : : }
78 : : }
79 : : }
80 : : #else
81 : : #define dump_manager(...)
82 : : #endif
83 : :
84 : : static observer_t *alloc_observer(void *mem_ctx, void *observer, char *notification,
85 : : dwc_notifier_callback_t callback, void *data)
86 : : {
87 : 0 : observer_t *new_observer = dwc_alloc(mem_ctx, sizeof(observer_t));
88 : :
89 : 0 : if (!new_observer) {
90 : : return NULL;
91 : : }
92 : :
93 : 0 : DWC_CIRCLEQ_INIT_ENTRY(new_observer, list_entry);
94 : 0 : new_observer->observer = observer;
95 : 0 : new_observer->notification = notification;
96 : 0 : new_observer->callback = callback;
97 : 0 : new_observer->data = data;
98 : : return new_observer;
99 : : }
100 : :
101 : : static void free_observer(void *mem_ctx, observer_t *observer)
102 : : {
103 : 0 : dwc_free(mem_ctx, observer);
104 : : }
105 : :
106 : 0 : static notifier_t *alloc_notifier(void *mem_ctx, void *object)
107 : : {
108 : : notifier_t *notifier;
109 : :
110 : 0 : if (!object) {
111 : : return NULL;
112 : : }
113 : :
114 : 0 : notifier = dwc_alloc(mem_ctx, sizeof(notifier_t));
115 : 0 : if (!notifier) {
116 : : return NULL;
117 : : }
118 : :
119 : 0 : DWC_CIRCLEQ_INIT(¬ifier->observers);
120 : 0 : DWC_CIRCLEQ_INIT_ENTRY(notifier, list_entry);
121 : :
122 : 0 : notifier->mem_ctx = mem_ctx;
123 : 0 : notifier->object = object;
124 : 0 : return notifier;
125 : : }
126 : :
127 : 0 : static void free_notifier(notifier_t *notifier)
128 : : {
129 : : observer_t *observer;
130 : :
131 : 0 : DWC_CIRCLEQ_FOREACH(observer, ¬ifier->observers, list_entry) {
132 : : free_observer(notifier->mem_ctx, observer);
133 : : }
134 : :
135 : 0 : dwc_free(notifier->mem_ctx, notifier);
136 : 0 : }
137 : :
138 : : static notifier_t *find_notifier(void *object)
139 : : {
140 : : notifier_t *notifier;
141 : :
142 : : DWC_ASSERT(manager, "Notification manager not found");
143 : :
144 : 0 : if (!object) {
145 : : return NULL;
146 : : }
147 : :
148 : 0 : DWC_CIRCLEQ_FOREACH(notifier, &manager->notifiers, list_entry) {
149 : 0 : if (notifier->object == object) {
150 : 0 : return notifier;
151 : : }
152 : : }
153 : :
154 : : return NULL;
155 : : }
156 : :
157 : 3 : int dwc_alloc_notification_manager(void *mem_ctx, void *wkq_ctx)
158 : : {
159 : 3 : return create_manager(mem_ctx, wkq_ctx);
160 : : }
161 : :
162 : 0 : void dwc_free_notification_manager(void)
163 : : {
164 : 0 : free_manager();
165 : 0 : }
166 : :
167 : 0 : dwc_notifier_t *dwc_register_notifier(void *mem_ctx, void *object)
168 : : {
169 : : notifier_t *notifier;
170 : :
171 : : DWC_ASSERT(manager, "Notification manager not found");
172 : :
173 : : notifier = find_notifier(object);
174 : 0 : if (notifier) {
175 : 0 : DWC_ERROR("Notifier %p is already registered\n", object);
176 : 0 : return NULL;
177 : : }
178 : :
179 : 0 : notifier = alloc_notifier(mem_ctx, object);
180 : 0 : if (!notifier) {
181 : : return NULL;
182 : : }
183 : :
184 : 0 : DWC_CIRCLEQ_INSERT_TAIL(&manager->notifiers, notifier, list_entry);
185 : :
186 : 0 : DWC_INFO("Notifier %p registered", object);
187 : : dump_manager();
188 : :
189 : 0 : return notifier;
190 : : }
191 : :
192 : 0 : void dwc_unregister_notifier(dwc_notifier_t *notifier)
193 : : {
194 : : DWC_ASSERT(manager, "Notification manager not found");
195 : :
196 : 0 : if (!DWC_CIRCLEQ_EMPTY(¬ifier->observers)) {
197 : : observer_t *o;
198 : :
199 : 0 : DWC_ERROR("Notifier %p has active observers when removing\n", notifier->object);
200 : 0 : DWC_CIRCLEQ_FOREACH(o, ¬ifier->observers, list_entry) {
201 : : DWC_DEBUGC(" %p watching %s\n", o->observer, o->notification);
202 : : }
203 : :
204 : : DWC_ASSERT(DWC_CIRCLEQ_EMPTY(¬ifier->observers),
205 : : "Notifier %p has active observers when removing", notifier);
206 : : }
207 : :
208 : 0 : DWC_CIRCLEQ_REMOVE_INIT(&manager->notifiers, notifier, list_entry);
209 : 0 : free_notifier(notifier);
210 : :
211 : 0 : DWC_INFO("Notifier unregistered");
212 : : dump_manager();
213 : 0 : }
214 : :
215 : : /* Add an observer to observe the notifier for a particular state, event, or notification. */
216 : 0 : int dwc_add_observer(void *observer, void *object, char *notification,
217 : : dwc_notifier_callback_t callback, void *data)
218 : : {
219 : : notifier_t *notifier = find_notifier(object);
220 : : observer_t *new_observer;
221 : :
222 : 0 : if (!notifier) {
223 : 0 : DWC_ERROR("Notifier %p is not found when adding observer\n", object);
224 : 0 : return -DWC_E_INVALID;
225 : : }
226 : :
227 : : new_observer = alloc_observer(notifier->mem_ctx, observer, notification, callback, data);
228 : 0 : if (!new_observer) {
229 : : return -DWC_E_NO_MEMORY;
230 : : }
231 : :
232 : 0 : DWC_CIRCLEQ_INSERT_TAIL(¬ifier->observers, new_observer, list_entry);
233 : :
234 : 0 : DWC_INFO("Added observer %p to notifier %p observing notification %s, callback=%p, data=%p",
235 : : observer, object, notification, callback, data);
236 : :
237 : : dump_manager();
238 : 0 : return 0;
239 : : }
240 : :
241 : 0 : int dwc_remove_observer(void *observer)
242 : : {
243 : : notifier_t *n;
244 : :
245 : : DWC_ASSERT(manager, "Notification manager not found");
246 : :
247 : 0 : DWC_CIRCLEQ_FOREACH(n, &manager->notifiers, list_entry) {
248 : : observer_t *o;
249 : : observer_t *o2;
250 : :
251 : 0 : DWC_CIRCLEQ_FOREACH_SAFE(o, o2, &n->observers, list_entry) {
252 : 0 : if (o->observer == observer) {
253 : 0 : DWC_CIRCLEQ_REMOVE_INIT(&n->observers, o, list_entry);
254 : 0 : DWC_INFO("Removing observer %p from notifier %p watching notification %s:",
255 : : o->observer, n->object, o->notification);
256 : : free_observer(n->mem_ctx, o);
257 : : }
258 : : }
259 : : }
260 : :
261 : : dump_manager();
262 : 0 : return 0;
263 : : }
264 : :
265 : : typedef struct callback_data {
266 : : void *mem_ctx;
267 : : dwc_notifier_callback_t cb;
268 : : void *observer;
269 : : void *data;
270 : : void *object;
271 : : char *notification;
272 : : void *notification_data;
273 : : } cb_data_t;
274 : :
275 : 0 : static void cb_task(void *data)
276 : : {
277 : : cb_data_t *cb = (cb_data_t *)data;
278 : :
279 : 0 : cb->cb(cb->object, cb->notification, cb->observer, cb->notification_data, cb->data);
280 : 0 : dwc_free(cb->mem_ctx, cb);
281 : 0 : }
282 : :
283 : 0 : void dwc_notify(dwc_notifier_t *notifier, char *notification, void *notification_data)
284 : : {
285 : : observer_t *o;
286 : :
287 : : DWC_ASSERT(manager, "Notification manager not found");
288 : :
289 : 0 : DWC_CIRCLEQ_FOREACH(o, ¬ifier->observers, list_entry) {
290 : 0 : int len = DWC_STRLEN(notification);
291 : :
292 : 0 : if (DWC_STRLEN(o->notification) != len) {
293 : 0 : continue;
294 : : }
295 : :
296 : 0 : if (DWC_STRNCMP(o->notification, notification, len) == 0) {
297 : 0 : cb_data_t *cb_data = dwc_alloc(notifier->mem_ctx, sizeof(cb_data_t));
298 : :
299 : 0 : if (!cb_data) {
300 : 0 : DWC_ERROR("Failed to allocate callback data\n");
301 : 0 : return;
302 : : }
303 : :
304 : 0 : cb_data->mem_ctx = notifier->mem_ctx;
305 : 0 : cb_data->cb = o->callback;
306 : 0 : cb_data->observer = o->observer;
307 : 0 : cb_data->data = o->data;
308 : 0 : cb_data->object = notifier->object;
309 : 0 : cb_data->notification = notification;
310 : 0 : cb_data->notification_data = notification_data;
311 : : DWC_DEBUGC("Observer found %p for notification %s\n", o->observer, notification);
312 : 0 : DWC_WORKQ_SCHEDULE(manager->wq, cb_task, cb_data,
313 : : "Notify callback from %p for Notification %s, to observer %p",
314 : : cb_data->object, notification, cb_data->observer);
315 : : }
316 : : }
317 : : }
318 : :
319 : : #endif /* DWC_NOTIFYLIB */
|