1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
|
/*
* Embedded Linux library
* Copyright (C) 2024 Cruise, LLC
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "private.h"
#include "queue.h"
#include "notifylist.h"
#include "useful.h"
struct l_notifylist {
uint32_t next_id;
struct l_queue *entries;
bool in_notify : 1;
bool stale_entries : 1;
bool pending_destroy : 1;
const struct l_notifylist_ops *ops;
};
static bool __notifylist_entry_match(const void *a, const void *b)
{
const struct l_notifylist_entry *entry = a;
uint32_t id = L_PTR_TO_UINT(b);
return entry->id == id;
}
static void __notifylist_entry_free(struct l_notifylist *list,
struct l_notifylist_entry *e)
{
if (e->destroy)
e->destroy(e->notify_data);
list->ops->free_entry(e);
}
static void __notifylist_clear(struct l_notifylist *list)
{
struct l_notifylist_entry *entry;
while ((entry = l_queue_pop_head(list->entries)))
__notifylist_entry_free(list, entry);
}
static void __notifylist_prune_stale(struct l_notifylist *list)
{
struct l_notifylist_entry *e;
while ((e = l_queue_remove_if(list->entries, __notifylist_entry_match,
L_UINT_TO_PTR(0))))
__notifylist_entry_free(list, e);
list->stale_entries = false;
}
static void __notifylist_destroy(struct l_notifylist *list)
{
__notifylist_clear(list);
l_queue_destroy(list->entries, NULL);
list->entries = NULL;
l_free(list);
}
static void __notifylist_notify(struct l_notifylist *list,
l_notifylist_entry_matches_func_t match_func,
const void *match_data,
int type, va_list args)
{
const struct l_queue_entry *entry = l_queue_get_entries(list->entries);
list->in_notify = true;
for (; entry; entry = entry->next) {
const struct l_notifylist_entry *e = entry->data;
va_list copy;
if (e->id == 0)
continue;
if (match_func && !match_func(e, match_data))
continue;
va_copy(copy, args);
list->ops->notify(e, type, copy);
va_end(copy);
if (list->pending_destroy)
break;
}
list->in_notify = false;
if (list->pending_destroy)
__notifylist_destroy(list);
else if (list->stale_entries)
__notifylist_prune_stale(list);
}
LIB_EXPORT struct l_notifylist *l_notifylist_new(
const struct l_notifylist_ops *ops)
{
struct l_notifylist *list = l_new(struct l_notifylist, 1);
list->entries = l_queue_new();
list->ops = ops;
list->next_id = 1;
return list;
}
LIB_EXPORT uint32_t l_notifylist_add(struct l_notifylist *list,
struct l_notifylist_entry *entry)
{
if (!list)
return 0;
entry->id = list->next_id++;
if (!list->next_id)
list->next_id = 1;
l_queue_push_tail(list->entries, entry);
return entry->id;
}
LIB_EXPORT bool l_notifylist_remove(struct l_notifylist *list, uint32_t id)
{
struct l_notifylist_entry *entry;
if (!list)
return false;
if (list->in_notify) {
entry = l_queue_find(list->entries, __notifylist_entry_match,
L_UINT_TO_PTR(id));
if (!entry)
return false;
entry->id = 0; /* Mark stale */
list->stale_entries = true;
return true;
}
entry = l_queue_remove_if(list->entries, __notifylist_entry_match,
L_UINT_TO_PTR(id));
if (!entry)
return false;
__notifylist_entry_free(list, entry);
return true;
}
LIB_EXPORT void l_notifylist_free(struct l_notifylist *list)
{
if (!list)
return;
if (list->in_notify) {
list->pending_destroy = true;
return;
}
__notifylist_destroy(list);
}
LIB_EXPORT bool l_notifylist_notify(struct l_notifylist *list,
int type, ...)
{
va_list args;
if (!list)
return false;
va_start(args, type);
__notifylist_notify(list, NULL, NULL, type, args);
va_end(args);
return true;
}
LIB_EXPORT bool l_notifylist_notify_matches(struct l_notifylist *list,
l_notifylist_entry_matches_func_t match_func,
const void *match_data, int type, ...)
{
va_list args;
if (!list)
return false;
if (!match_func)
return false;
va_start(args, type);
__notifylist_notify(list, match_func, match_data, type, args);
va_end(args);
return true;
}
|