3 #include "dwc_notifier.h"
6 typedef struct dwc_observer {
8 dwc_notifier_callback_t callback;
11 DWC_CIRCLEQ_ENTRY(dwc_observer) list_entry;
14 DWC_CIRCLEQ_HEAD(observer_queue, dwc_observer);
16 typedef struct dwc_notifier {
19 struct observer_queue observers;
20 DWC_CIRCLEQ_ENTRY(dwc_notifier) list_entry;
23 DWC_CIRCLEQ_HEAD(notifier_queue, dwc_notifier);
25 typedef struct manager {
29 /* dwc_mutex_t *mutex; */
30 struct notifier_queue notifiers;
33 static manager_t *manager;
35 static int create_manager(void *mem_ctx, void *wkq_ctx)
37 manager = dwc_alloc(mem_ctx, sizeof(manager_t));
39 return -DWC_E_NO_MEMORY;
42 DWC_CIRCLEQ_INIT(&manager->notifiers);
44 manager->wq = dwc_workq_alloc(wkq_ctx, "DWC Notification WorkQ");
46 return -DWC_E_NO_MEMORY;
52 static void free_manager(void)
54 dwc_workq_free(manager->wq);
56 /* All notifiers must have unregistered themselves before this module
57 * can be removed. Hitting this assertion indicates a programmer
59 DWC_ASSERT(DWC_CIRCLEQ_EMPTY(&manager->notifiers),
60 "Notification manager being freed before all notifiers have been removed");
61 dwc_free(manager->mem_ctx, manager);
65 static void dump_manager(void)
70 DWC_ASSERT(manager, "Notification manager not found");
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);
81 #define dump_manager(...)
84 static observer_t *alloc_observer(void *mem_ctx, void *observer, char *notification,
85 dwc_notifier_callback_t callback, void *data)
87 observer_t *new_observer = dwc_alloc(mem_ctx, sizeof(observer_t));
93 DWC_CIRCLEQ_INIT_ENTRY(new_observer, list_entry);
94 new_observer->observer = observer;
95 new_observer->notification = notification;
96 new_observer->callback = callback;
97 new_observer->data = data;
101 static void free_observer(void *mem_ctx, observer_t *observer)
103 dwc_free(mem_ctx, observer);
106 static notifier_t *alloc_notifier(void *mem_ctx, void *object)
108 notifier_t *notifier;
114 notifier = dwc_alloc(mem_ctx, sizeof(notifier_t));
119 DWC_CIRCLEQ_INIT(¬ifier->observers);
120 DWC_CIRCLEQ_INIT_ENTRY(notifier, list_entry);
122 notifier->mem_ctx = mem_ctx;
123 notifier->object = object;
127 static void free_notifier(notifier_t *notifier)
129 observer_t *observer;
131 DWC_CIRCLEQ_FOREACH(observer, ¬ifier->observers, list_entry) {
132 free_observer(notifier->mem_ctx, observer);
135 dwc_free(notifier->mem_ctx, notifier);
138 static notifier_t *find_notifier(void *object)
140 notifier_t *notifier;
142 DWC_ASSERT(manager, "Notification manager not found");
148 DWC_CIRCLEQ_FOREACH(notifier, &manager->notifiers, list_entry) {
149 if (notifier->object == object) {
157 int dwc_alloc_notification_manager(void *mem_ctx, void *wkq_ctx)
159 return create_manager(mem_ctx, wkq_ctx);
162 void dwc_free_notification_manager(void)
167 dwc_notifier_t *dwc_register_notifier(void *mem_ctx, void *object)
169 notifier_t *notifier;
171 DWC_ASSERT(manager, "Notification manager not found");
173 notifier = find_notifier(object);
175 DWC_ERROR("Notifier %p is already registered\n", object);
179 notifier = alloc_notifier(mem_ctx, object);
184 DWC_CIRCLEQ_INSERT_TAIL(&manager->notifiers, notifier, list_entry);
186 DWC_INFO("Notifier %p registered", object);
192 void dwc_unregister_notifier(dwc_notifier_t *notifier)
194 DWC_ASSERT(manager, "Notification manager not found");
196 if (!DWC_CIRCLEQ_EMPTY(¬ifier->observers)) {
199 DWC_ERROR("Notifier %p has active observers when removing\n", notifier->object);
200 DWC_CIRCLEQ_FOREACH(o, ¬ifier->observers, list_entry) {
201 DWC_DEBUG(" %p watching %s\n", o->observer, o->notification);
204 DWC_ASSERT(DWC_CIRCLEQ_EMPTY(¬ifier->observers),
205 "Notifier %p has active observers when removing", notifier);
208 DWC_CIRCLEQ_REMOVE_INIT(&manager->notifiers, notifier, list_entry);
209 free_notifier(notifier);
211 DWC_INFO("Notifier unregistered");
215 /* Add an observer to observe the notifier for a particular state, event, or notification. */
216 int dwc_add_observer(void *observer, void *object, char *notification,
217 dwc_notifier_callback_t callback, void *data)
219 notifier_t *notifier = find_notifier(object);
220 observer_t *new_observer;
223 DWC_ERROR("Notifier %p is not found when adding observer\n", object);
224 return -DWC_E_INVALID;
227 new_observer = alloc_observer(notifier->mem_ctx, observer, notification, callback, data);
229 return -DWC_E_NO_MEMORY;
232 DWC_CIRCLEQ_INSERT_TAIL(¬ifier->observers, new_observer, list_entry);
234 DWC_INFO("Added observer %p to notifier %p observing notification %s, callback=%p, data=%p",
235 observer, object, notification, callback, data);
241 int dwc_remove_observer(void *observer)
245 DWC_ASSERT(manager, "Notification manager not found");
247 DWC_CIRCLEQ_FOREACH(n, &manager->notifiers, list_entry) {
251 DWC_CIRCLEQ_FOREACH_SAFE(o, o2, &n->observers, list_entry) {
252 if (o->observer == observer) {
253 DWC_CIRCLEQ_REMOVE_INIT(&n->observers, o, list_entry);
254 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);
265 typedef struct callback_data {
267 dwc_notifier_callback_t cb;
272 void *notification_data;
275 static void cb_task(void *data)
277 cb_data_t *cb = (cb_data_t *)data;
279 cb->cb(cb->object, cb->notification, cb->observer, cb->notification_data, cb->data);
280 dwc_free(cb->mem_ctx, cb);
283 void dwc_notify(dwc_notifier_t *notifier, char *notification, void *notification_data)
287 DWC_ASSERT(manager, "Notification manager not found");
289 DWC_CIRCLEQ_FOREACH(o, ¬ifier->observers, list_entry) {
290 int len = DWC_STRLEN(notification);
292 if (DWC_STRLEN(o->notification) != len) {
296 if (DWC_STRNCMP(o->notification, notification, len) == 0) {
297 cb_data_t *cb_data = dwc_alloc(notifier->mem_ctx, sizeof(cb_data_t));
300 DWC_ERROR("Failed to allocate callback data\n");
304 cb_data->mem_ctx = notifier->mem_ctx;
305 cb_data->cb = o->callback;
306 cb_data->observer = o->observer;
307 cb_data->data = o->data;
308 cb_data->object = notifier->object;
309 cb_data->notification = notification;
310 cb_data->notification_data = notification_data;
311 DWC_DEBUG("Observer found %p for notification %s\n", o->observer, notification);
312 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);
319 #endif /* DWC_NOTIFYLIB */