/*
* Create the list which holds the workers.
*/
- fr_dlist_init(&sc->workers, offsetof(fr_schedule_worker_t, entry));
+ fr_dlist_init(&sc->workers, fr_schedule_worker_t, entry);
memset(&sc->semaphore, 0, sizeof(sc->semaphore));
if (sem_init(&sc->semaphore, 0, SEMAPHORE_LOCKED) != 0) {
/*
* See if all of the workers have started.
*/
- for (sw = fr_dlist_first(&sc->workers);
+ for (sw = fr_dlist_head(&sc->workers);
sw != NULL;
sw = next) {
/*
* Signal all of the workers to exit.
*/
- for (sw = fr_dlist_first(&sc->workers);
+ for (sw = fr_dlist_head(&sc->workers);
sw != NULL;
sw = fr_dlist_next(&sc->workers, sw)) {
fr_worker_exit(sw->worker);
/*
* Clean up the exited workers.
*/
- while ((sw = fr_dlist_first(&sc->workers)) != NULL) {
+ while ((sw = fr_dlist_head(&sc->workers)) != NULL) {
sc->num_workers--;
fr_dlist_remove(&sc->workers, sw);
tt->start = when;
tt->resumed = when;
- fr_dlist_init(&(worker->list), offsetof(fr_time_tracking_t, list.entry));
+ fr_dlist_init(&(worker->list), fr_time_tracking_t, list.entry);
fr_dlist_entry_init(&tt->list.entry);
}
* the same code.
*/
#define WORKER_HEAP_INIT(_name, _func) do { \
- fr_dlist_init(&worker->_name.list, offsetof(fr_channel_data_t, request.entry)); \
+ fr_dlist_init(&worker->_name.list, fr_channel_data_t, request.entry); \
worker->_name.heap = fr_heap_create(worker, _func, fr_channel_data_t, channel.heap_id); \
if (!worker->_name.heap) { \
(void) fr_event_user_delete(worker->el, fr_worker_evfilt_user, worker); \
* the worker thread is running.
*/
memset(&worker->tracking, 0, sizeof(worker->tracking));
- fr_dlist_init(&worker->tracking.list, offsetof(fr_time_tracking_t, list.entry));
+ fr_dlist_init(&worker->tracking.list, fr_time_tracking_t, list.entry);
worker->kq = fr_event_list_kq(worker->el);
rad_assert(worker->kq >= 0);
*/
RCSIDH(dlist_h, "$Id$")
-/**
- * A doubly linked list.
+/** Entry in a doubly linked list
+ *
*/
-typedef struct fr_dlist_t {
- struct fr_dlist_t *prev;
- struct fr_dlist_t *next;
+typedef struct fr_dlist {
+ struct fr_dlist *prev;
+ struct fr_dlist *next;
} fr_dlist_t;
-typedef struct fr_dlist_head_t {
- size_t offset;
- fr_dlist_t entry;
+/** Head of a doubly linked list
+ *
+ * Holds additional information about the list items,
+ * like at which offset the next/prev pointers can be found.
+ */
+typedef struct {
+ size_t offset; //!< Positive offset from start of structure to #fr_dlist_t.
+ char const *type; //!< of items contained within the list. Used for talloc
+ ///< validation.
+ fr_dlist_t entry; //!< Struct holding the head and fail of the list.
} fr_dlist_head_t;
-/*
- * Functions to manage a doubly linked list.
+/** Initialise a linked list without metadata
+ *
*/
static inline void fr_dlist_entry_init(fr_dlist_t *entry)
{
entry->prev = entry->next = entry;
}
-static inline void fr_dlist_init(fr_dlist_head_t *head, size_t offset)
+/** Initialise the head structure of a doubly linked list
+ *
+ * @note This variant does not perform talloc validation.
+ *
+ @code{.c}
+ typedef struct {
+ fr_dlist_t dlist;
+ char const *field_a;
+ int *field_b;
+ ...
+ } my_struct_t;
+
+ int my_func(my_struct_t *a, my_struct_t *b)
+ {
+ fr_dlist_head_t head;
+
+ fr_dlist_init(&head, my_struct_t, dlist);
+ fr_dlist_insert_head(&head, a);
+ fr_dlist_insert_head(&head, b);
+ }
+ @endcode
+ *
+ * @param[in] _head structure to initialise.
+ * @param[in] _type of structure being stored in the list,
+ * e.g. fr_value_box_t, fr_dict_attr_t etc...
+ * @param[in] _field Containing the #fr_dlist_t structure within
+ * structure being stored.
+ */
+#define fr_dlist_init(_head, _type, _field) _fr_dlist_init(_head, offsetof(_type, _field), NULL)
+
+/** Initialise the head structure of a doubly linked list
+ *
+ * @note This variant *DOES* perform talloc validation. All items inserted
+ * into the list must be allocated with talloc.
+ *
+ * @copybrief fr_dlist_init.
+ *
+ * @param[in] _head structure to initialise.
+ * @param[in] _type of structure being stored in the list,
+ * e.g. fr_value_box_t, fr_dict_attr_t etc...
+ * @param[in] _field Containing the #fr_dlist_t within
+ * structure being stored.
+ */
+#define fr_dlist_talloc_init(_head, _type, _field) _fr_dlist_init(_head, offsetof(_type, _field), STRINGIFY(_type))
+
+static inline void _fr_dlist_init(fr_dlist_head_t *head, size_t offset, char const *type)
{
fr_dlist_entry_init(&head->entry);
head->offset = offset;
+ head->type = type;
}
+/** Insert an item into the head of the list
+ *
+ * @note If #fr_dlist_talloc_init was used to initialise #fr_dlist_head_t
+ * ptr must be a talloced chunk of the type passed to #fr_dlist_talloc_init.
+ *
+ * @param[in] list_head to insert ptr into.
+ * @param[in] ptr to insert.
+ */
static inline void fr_dlist_insert_head(fr_dlist_head_t *list_head, void *ptr)
{
- fr_dlist_t *entry = (fr_dlist_t *) (((uint8_t *) ptr) + list_head->offset);
- fr_dlist_t *head = &(list_head->entry);
+ fr_dlist_t *entry;
+ fr_dlist_t *head;
+
+#ifndef TALLOC_GET_TYPE_ABORT_NOOP
+ if (list_head->type) ptr = _talloc_get_type_abort(ptr, list_head->type, __location__);
+#endif
+
+ entry = (fr_dlist_t *) (((uint8_t *) ptr) + list_head->offset);
+ head = &(list_head->entry);
if (!fr_cond_assert(head->next != NULL)) return;
if (!fr_cond_assert(head->prev != NULL)) return;
head->next = entry;
}
+/** Insert an item into the tail of the list
+ *
+ * @note If #fr_dlist_talloc_init was used to initialise #fr_dlist_head_t
+ * ptr must be a talloced chunk of the type passed to #fr_dlist_talloc_init.
+ *
+ * @param[in] list_head to insert ptr into.
+ * @param[in] ptr to insert.
+ */
static inline void fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
{
- fr_dlist_t *entry = (fr_dlist_t *) (((uint8_t *) ptr) + list_head->offset);
- fr_dlist_t *head = &(list_head->entry);
+ fr_dlist_t *entry;
+ fr_dlist_t *head;
+
+#ifndef TALLOC_GET_TYPE_ABORT_NOOP
+ if (list_head->type) ptr = _talloc_get_type_abort(ptr, list_head->type, __location__);
+#endif
+
+ entry = (fr_dlist_t *) (((uint8_t *) ptr) + list_head->offset);
+ head = &(list_head->entry);
if (!fr_cond_assert(head->next != NULL)) return;
if (!fr_cond_assert(head->prev != NULL)) return;
head->prev = entry;
}
-#if 0
-/*
- * Insert one list into the tail of another
+/** Return the HEAD item of a list or NULL if the list is empty
+ *
+ * @param[in] list_head to return the HEAD item from.
+ * @return
+ * - The HEAD item.
+ * - NULL if no items exist in the list.
*/
-static inline void fr_dlist_insert_tail_list(fr_dlist_head_t *list_head, fr_dlist_t *list)
+static inline void *fr_dlist_head(fr_dlist_head_t *list_head)
{
fr_dlist_t *head = &(list_head->entry);
- if (!fr_cond_assert(head->next != NULL)) return;
- if (!fr_cond_assert(head->prev != NULL)) return;
-
- list->prev->next = head;
- list->next->prev = head->prev;
+ if (head->next == head) return NULL;
- head->prev->next = list->next;
- head->prev = list->prev;
+ return (void *) (((uint8_t *) head->next) - list_head->offset);
- list->prev = list->next = &list;
}
-#endif
-static inline void fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
+/** Return the TAIL item of a list or NULL if the list is empty
+ *
+ * @param[in] list_head to return the HEAD item from.
+ * @return
+ * - The TAIL item.
+ * - NULL if no items exist in the list.
+ */
+static inline void *fr_dlist_tail(fr_dlist_head_t *list_head)
{
- fr_dlist_t *entry = (fr_dlist_t *) (((uint8_t *) ptr) + list_head->offset);
+ fr_dlist_t *head = &(list_head->entry);
- if (!fr_cond_assert(entry->next != NULL)) return;
- if (!fr_cond_assert(entry->prev != NULL)) return;
+ if (head->prev == head) return NULL;
+
+ return (void *) (((uint8_t *) head->prev) - list_head->offset);
- entry->prev->next = entry->next;
- entry->next->prev = entry->prev;
- entry->prev = entry->next = entry;
}
+/** Get the next item in a list
+ *
+ * @note If #fr_dlist_talloc_init was used to initialise #fr_dlist_head_t
+ * ptr must be a talloced chunk of the type passed to #fr_dlist_talloc_init.
+ *
+ * @param[in] list_head containing ptr.
+ * @param[in] ptr to retrieve the next item from.
+ */
static inline void *fr_dlist_next(fr_dlist_head_t *list_head, void *ptr)
{
- fr_dlist_t *entry = (fr_dlist_t *) (((uint8_t *) ptr) + list_head->offset);
- fr_dlist_t *head = &(list_head->entry);
+ fr_dlist_t *entry;
+ fr_dlist_t *head;
+
+#ifndef TALLOC_GET_TYPE_ABORT_NOOP
+ if (list_head->type) ptr = _talloc_get_type_abort(ptr, list_head->type, __location__);
+#endif
+
+ entry = (fr_dlist_t *) (((uint8_t *) ptr) + list_head->offset);
+ head = &(list_head->entry);
if (entry->next == head) return NULL;
entry = entry->next;
return (void *) (((uint8_t *) entry) - list_head->offset);
}
-static inline void *fr_dlist_first(fr_dlist_head_t *list_head)
+/** Remove an item from the list
+ *
+ * @note If #fr_dlist_talloc_init was used to initialise #fr_dlist_head_t
+ * ptr must be a talloced chunk of the type passed to #fr_dlist_talloc_init.
+ *
+ * @param[in] list_head to remove ptr from.
+ * @param[in] ptr to remove.
+ */
+static inline void fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
{
- fr_dlist_t *head = &(list_head->entry);
+ fr_dlist_t *entry;
- if (head->next == head) return NULL;
+#ifndef TALLOC_GET_TYPE_ABORT_NOOP
+ if (list_head->type) ptr = _talloc_get_type_abort(ptr, list_head->type, __location__);
+#endif
- return (void *) (((uint8_t *) head->next) - list_head->offset);
+ entry = (fr_dlist_t *) (((uint8_t *) ptr) + list_head->offset);
+
+ if (!fr_cond_assert(entry->next != NULL)) return;
+ if (!fr_cond_assert(entry->prev != NULL)) return;
+ entry->prev->next = entry->next;
+ entry->next->prev = entry->prev;
+ entry->prev = entry->next = entry;
}
-static inline void *fr_dlist_tail(fr_dlist_head_t *list_head)
+/** Check all items in the list are valid
+ *
+ * Checks item talloc headers and types to ensure they're consistent
+ * with what we expect.
+ *
+ * Does nothing if the list was not initialised with #fr_dlist_talloc_init.
+ */
+#ifndef TALLOC_GET_TYPE_ABORT_NOOP
+static inline void fr_dlist_verify(fr_dlist_head_t *list_head)
{
- fr_dlist_t *head = &(list_head->entry);
-
- if (head->prev == head) return NULL;
+ void *item;
- return (void *) (((uint8_t *) head->prev) - list_head->offset);
+ if (!list_head->type) return;
+ for (item = fr_dlist_head(list_head);
+ item;
+ item = fr_dlist_next(list_head, item)) {
+ item = _talloc_get_type_abort(item, list_head->type, __location__);
+ }
}
-
-#if 0
-#ifdef WITH_VERIFY_PTR
-# define FR_DLIST_VERIFY(_head, _type, _member) \
-do { \
- fr_dlist_t *_next; \
- for (_next = FR_DLIST_FIRST(_head); \
- _next; \
- _next = FR_DLIST_NEXT(_head, _next)) { \
- (void)talloc_get_type_abort(fr_ptr_to_type(_type, _member, _next), _type); \
- } \
-} while(0)
#else
-# define FR_DLIST_VERIFY(_head, _type, _member)
-#endif
+# define fr_list_verify(_head)
#endif
-/** Convert a pointer to a member into a pointer to the parent structure.
+/** Merge two lists, inserting the tail of one into the other
*
*/
-#define fr_ptr_to_type(TYPE, MEMBER, PTR) (TYPE *) (((char *)PTR) - offsetof(TYPE, MEMBER))
+static inline void fr_dlist_insert_tail_list(fr_dlist_head_t *list_head_a, fr_dlist_head_t *list_head_b)
+{
+ fr_dlist_t *head_a = &(list_head_a->entry);
+ fr_dlist_t *head_b = &(list_head_b->entry);
+
+#ifdef WITH_VERIFY_PTR
+ /*
+ * Must be both talloced or both not
+ */
+ if (!fr_cond_assert(list_head_a->type == list_head_b->type)) return;
+
+ /*
+ * Must be of the same type
+ */
+ if (!fr_cond_assert(!list_head_a->type) || (strcmp(list_head_a->type, list_head_b->type) == 0)) return;
+#endif
+
+ if (!fr_cond_assert(head_a->next != NULL)) return;
+ if (!fr_cond_assert(head_a->prev != NULL)) return;
+
+ head_b->prev->next = head_a;
+ head_b->next->prev = head_a->prev;
+
+ head_a->prev->next = head_b->next;
+ head_a->prev = head_b->prev;
+
+ head_b->prev = head_b->next = head_b;
+}
{
fr_event_user_t *user, *next;
- for (user = fr_dlist_first(&el->user_callbacks);
+ for (user = fr_dlist_head(&el->user_callbacks);
user != NULL;
user = next) {
next = fr_dlist_next(&el->user_callbacks, user);
{
fr_event_pre_t *pre, *next;
- for (pre = fr_dlist_first(&el->pre_callbacks);
+ for (pre = fr_dlist_head(&el->pre_callbacks);
pre != NULL;
pre = next) {
next = fr_dlist_next(&el->pre_callbacks, pre);
{
fr_event_post_t *post, *next;
- for (post = fr_dlist_first(&el->post_callbacks);
+ for (post = fr_dlist_head(&el->post_callbacks);
post != NULL;
post = next) {
next = fr_dlist_next(&el->post_callbacks, post);
* application has more work to do, in which case we
* re-set the timeout to be instant.
*/
- for (pre = fr_dlist_first(&el->pre_callbacks);
+ for (pre = fr_dlist_head(&el->pre_callbacks);
pre != NULL;
pre = fr_dlist_next(&el->pre_callbacks, pre)) {
if (pre->callback(pre->uctx, wake) > 0) {
/*
* Run all of the post-processing events.
*/
- for (post = fr_dlist_first(&el->post_callbacks);
+ for (post = fr_dlist_head(&el->post_callbacks);
post != NULL;
post = fr_dlist_next(&el->post_callbacks, post)) {
when = el->now;
goto error;
}
- fr_dlist_init(&el->pre_callbacks, offsetof(fr_event_pre_t, entry));
- fr_dlist_init(&el->post_callbacks, offsetof(fr_event_post_t, entry));
- fr_dlist_init(&el->user_callbacks, offsetof(fr_event_user_t, entry));
+ fr_dlist_init(&el->pre_callbacks, fr_event_pre_t, entry);
+ fr_dlist_init(&el->post_callbacks, fr_event_post_t, entry);
+ fr_dlist_init(&el->user_callbacks, fr_event_user_t, entry);
if (status) (void) fr_event_pre_insert(el, status, status_uctx);
return -1;
}
- fr_dlist_init(list, offsetof(fr_child_t, entry));
+ fr_dlist_init(list, fr_child_t, entry);
fr_thread_local_set_destructor(fr_children, _fr_children_free, list);
} else {
* Clean up the children. ALL of them. This is
* slow as heck, but correct. :(
*/
- for (child = fr_dlist_first(fr_children);
+ for (child = fr_dlist_head(fr_children);
child != NULL;
child = next) {
int status;
DEBUG4("Freeing state tree %p", state);
- while ((entry = fr_dlist_first(&state->to_expire))) {
+ while ((entry = fr_dlist_head(&state->to_expire))) {
state_entry_unlink(state, entry);
talloc_free(entry);
}
return NULL;
}
- fr_dlist_init(&state->to_expire, offsetof(fr_state_entry_t, list));
+ fr_dlist_init(&state->to_expire, fr_state_entry_t, list);
/*
* We need to do controlled freeing of the
bool too_many = false;
fr_dlist_head_t to_free;
- fr_dlist_init(&to_free, offsetof(fr_state_entry_t, list));
+ fr_dlist_init(&to_free, fr_state_entry_t, list);
/*
* Clean up old entries.
*/
- for (entry = fr_dlist_first(&state->to_expire);
+ for (entry = fr_dlist_head(&state->to_expire);
entry != NULL;
entry = next) {
(void)talloc_get_type_abort(entry, fr_state_entry_t); /* Allow examination */
* be freed also, and it may have complex destructors associated
* with it.
*/
- while ((entry = fr_dlist_first(&to_free)) != NULL) {
+ while ((entry = fr_dlist_head(&to_free)) != NULL) {
fr_dlist_remove(&to_free, entry);
talloc_free(entry);
}
* Process retransmissions before anything else in the
* file.
*/
- track = fr_dlist_first(&inst->list);
+ track = fr_dlist_head(&inst->list);
if (track) {
fr_dlist_remove(&inst->list, track);
proto_detail_work_t *inst = talloc_get_type_abort(instance, proto_detail_work_t);
RADCLIENT *client;
- fr_dlist_init(&inst->list, offsetof(fr_detail_entry_t, entry));
+ fr_dlist_init(&inst->list, fr_detail_entry_t, entry);
client = inst->client = talloc_zero(inst, RADCLIENT);
if (!inst->client) return 0;
t->inst = instance;
t->el = el;
- fr_dlist_init(&t->running, offsetof(rlm_radius_link_t, entry));
+ fr_dlist_init(&t->running, rlm_radius_link_t, entry);
/*
* Allocate thread-specific data. The connections should
* marked DONE, and (in an ideal world) resumed / cleaned
* up before this memory is freed.
*/
- if (fr_dlist_first(&t->running) != NULL) {
+ if (fr_dlist_head(&t->running) != NULL) {
ERROR("Module still has running requests!");
return -1;
}
/*
* No outstanding packets, we're idle.
*/
- if (fr_dlist_first(&c->sent) == NULL) {
+ if (fr_dlist_head(&c->sent) == NULL) {
break;
}
/*
* Move "sent" packets back to the thread queue,
*/
- while ((u = fr_dlist_first(&c->sent)) != NULL) {
+ while ((u = fr_dlist_head(&c->sent)) != NULL) {
state_transition(u, PACKET_STATE_THREAD);
}
}
rad_assert(c->zombie_ev == NULL);
memset(&c->zombie_start, 0, sizeof(c->zombie_start));
- fr_dlist_init(&c->sent, offsetof(rlm_radius_udp_request_t, entry));
+ fr_dlist_init(&c->sent, rlm_radius_udp_request_t, entry);
/*
* Status-Server checks. Manually build the packet, and
/*
* Move "sent" packets back to the main thread queue
*/
- while ((u = fr_dlist_first(&c->sent)) != NULL) {
+ while ((u = fr_dlist_head(&c->sent)) != NULL) {
rad_assert(u->state == PACKET_STATE_SENT);
rad_assert(u->c == c);
talloc_free(c);
return;
}
- fr_dlist_init(&c->sent, offsetof(rlm_radius_udp_request_t, entry));
+ fr_dlist_init(&c->sent, rlm_radius_udp_request_t, entry);
c->conn = fr_connection_alloc(c, t->el, &inst->parent->connection_timeout, &inst->parent->reconnection_delay,
_conn_init,
/*
* Only open one new connection at a time.
*/
- if (!fr_dlist_first(&t->opening)) conn_alloc(inst, t);
+ if (!fr_dlist_head(&t->opening)) conn_alloc(inst, t);
/*
* Add the request to the backlog. It will be
t->el = el;
t->queued = fr_heap_talloc_create(t, queue_cmp, rlm_radius_udp_request_t, heap_id);
- fr_dlist_init(&t->blocked, offsetof(rlm_radius_udp_connection_t, entry));
- fr_dlist_init(&t->full, offsetof(rlm_radius_udp_connection_t, entry));
- fr_dlist_init(&t->zombie, offsetof(rlm_radius_udp_connection_t, entry));
- fr_dlist_init(&t->opening, offsetof(rlm_radius_udp_connection_t, entry));
+ fr_dlist_init(&t->blocked, rlm_radius_udp_connection_t, entry);
+ fr_dlist_init(&t->full, rlm_radius_udp_connection_t, entry);
+ fr_dlist_init(&t->zombie, rlm_radius_udp_connection_t, entry);
+ fr_dlist_init(&t->opening, rlm_radius_udp_connection_t, entry);
t->active = fr_heap_talloc_create(t, conn_cmp, rlm_radius_udp_connection_t, heap_id);
*/
talloc_free_children(t);
- if (fr_dlist_first(&t->opening) != NULL) {
+ if (fr_dlist_head(&t->opening) != NULL) {
ERROR("There are still partially open sockets");
return -1;
}
id = talloc_zero(ctx, rlm_radius_id_t);
if (!id) return NULL;
- fr_dlist_init(&id->free_list, offsetof(rlm_radius_request_t, entry));
+ fr_dlist_init(&id->free_list, rlm_radius_request_t, entry);
for (i = 0; i < 256; i++) {
id->id[i].id = i;
rlm_radius_request_t *rr;
retry:
- rr = fr_dlist_first(&id->free_list);
+ rr = fr_dlist_head(&id->free_list);
if (rr) {
rad_assert(id->num_free > 0);
* Loop over all of the other thread instances, locking
* them, and adding their statistics in.
*/
- for (other = fr_dlist_first(&t->inst->list);
+ for (other = fr_dlist_head(&t->inst->list);
other != NULL;
other = fr_dlist_next(&t->inst->list, other)) {
int i;
pthread_mutex_init(&inst->mutex, NULL);
#endif
- fr_dlist_init(&inst->list, offsetof(rlm_stats_thread_t, entry));
+ fr_dlist_init(&inst->list, rlm_stats_thread_t, entry);
return 0;
}
* Instead, all of it is done in the context of the
* parent.
*/
- fr_dlist_init(&child->async->tracking.list, 0);
+ fr_dlist_init(&child->async->tracking.list, fr_time_tracking_t, list.entry);
/*
* create {...} creates an empty copy.