*
*/
struct fr_event_timer_t {
- fr_event_callback_t callback; //!< Callback to execute when the timer fires.
- void const *ctx; //!< Context pointer to pass to the callback.
struct timeval when; //!< When this timer should fire.
+ fr_event_callback_t callback; //!< Callback to execute when the timer fires.
+ void const *uctx; //!< Context pointer to pass to the callback.
+ TALLOC_CTX *linked_ctx; //!< talloc ctx this event was bound to.
fr_event_timer_t **parent; //!< Previous timer.
int heap; //!< Where to store opaque heap data.
bool deferred_delete; //!< Deferred deletion flag. Delete this event *after*
//!< the handlers complete.
- void *ctx; //!< Context pointer to pass to each file descriptor callback.
+ void *uctx; //!< Context pointer to pass to each file descriptor callback.
} fr_event_fd_t;
/** Callbacks to perform when the event handler is about to check the events.
typedef struct fr_event_pre_t {
fr_dlist_t entry; //!< linked list of callback
fr_event_status_t callback; //!< the callback to call
- void *ctx; //!< context for the callback.
+ void *uctx; //!< context for the callback.
} fr_event_pre_t;
typedef struct fr_event_post_t {
fr_dlist_t entry; //!< linked list of callback
fr_event_callback_t callback; //!< the callback to call
- void *ctx; //!< context for the callback.
+ void *uctx; //!< context for the callback.
} fr_event_post_t;
fr_dlist_t entry; //!< linked list of callback
uintptr_t ident; //!< the identifier of this event
fr_event_user_handler_t callback; //!< the callback to call
- void *ctx; //!< context for the callback.
+ void *uctx; //!< context for the callback.
} fr_event_user_t;
* @param[in] read_fn function to call when fd is readable.
* @param[in] write_fn function to call when fd is writable.
* @param[in] error function to call when an error occurs on the fd.
- * @param[in] ctx to pass to handler.
+ * @param[in] uctx to pass to handler.
* @return
* - 0 on succes.
* - -1 on failure.
fr_event_fd_handler_t read_fn,
fr_event_fd_handler_t write_fn,
fr_event_fd_error_handler_t error,
- void *ctx)
+ void *uctx)
{
int count = 0;
struct kevent evset[2];
return -1;
}
- ef->ctx = ctx;
+ ef->uctx = uctx;
ef->read = read_fn;
ef->write = write_fn;
ef->error = error;
* deferred_delete flag.
*/
ef->deferred_delete = false;
- ef->ctx = ctx;
+ ef->uctx = uctx;
ef->read = read_fn;
ef->write = write_fn;
ef->error = error;
/** Delete a timer event from the event list
*
* @param[in] el to delete event from.
- * @param[in] parent of the event being deleted.
+ * @param[in] ev_p of the event being deleted.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
*/
-int fr_event_timer_delete(fr_event_list_t *el, fr_event_timer_t **parent)
+int fr_event_timer_delete(fr_event_list_t *el, fr_event_timer_t **ev_p)
{
int ret;
- fr_event_timer_t *ev;
+ if (!*ev_p) return 0;
- if (!el) {
- fr_strerror_printf("Invalid argument: NULL event list");
- return -1;
- }
+ rad_assert(talloc_parent(*ev_p) == el);
- if (!parent) {
- fr_strerror_printf("Invalid arguments: NULL event pointer");
- return -1;
- }
+ ret = talloc_free(*ev_p);
+ if (ret == 0) *ev_p = NULL;
- if (!*parent) {
- fr_strerror_printf("Invalid arguments: NULL event");
- return -1;
- }
+ return ret;
+}
- /*
- * Validate the event_t struct to detect memory issues early.
- */
- ev = talloc_get_type_abort(*parent, fr_event_timer_t);
- if (ev->parent) {
- (void)fr_cond_assert(*(ev->parent) == ev);
- *ev->parent = NULL;
- }
- *parent = NULL;
+/** Remove an event from the event loop
+ *
+ * @param[in] ev to free.
+ * @return
+ * - 0 on success.
+ * - -1 on failure.
+ */
+static int _event_timer_free(fr_event_timer_t *ev)
+{
+ int ret;
+ fr_event_list_t *el = talloc_parent(ev);
ret = fr_heap_extract(el->times, ev);
*/
if (!fr_cond_assert(ret == 1)) {
fr_strerror_printf("Event not found in heap");
- talloc_free(ev);
return -1;
}
- talloc_free(ev);
- return ret;
+ return 0;
}
/** Insert a timer event into an event list
*
- * @param[in] el to insert event into.
- * @param[in] callback function to execute if the event fires.
- * @param[in] ctx for callback function.
- * @param[in] when we should run the event.
- * @param[in] parent If not NULL modify this event instead of creating a new one. This is a parent
- * in a temporal sense, not in a memory structure or dependency sense.
+ * @param[in] ctx to bind lifetime of the event to.
+ * @param[in] el to insert event into.
+ * @param[in,out] ev_p If not NULL modify this event instead of creating a new one. This is a parent
+ * in a temporal sense, not in a memory structure or dependency sense.
+ * @param[in] when we should run the event.
+ * @param[in] callback function to execute if the event fires.
+ * @param[in] uctx user data to pass to the event.
* @return
* - 0 on success.
* - -1 on failure.
*/
-int fr_event_timer_insert(fr_event_list_t *el, fr_event_callback_t callback, void const *ctx,
- struct timeval *when, fr_event_timer_t **parent)
+int fr_event_timer_insert(TALLOC_CTX *ctx, fr_event_list_t *el, fr_event_timer_t **ev_p,
+ struct timeval *when, fr_event_callback_t callback, void const *uctx)
{
fr_event_timer_t *ev;
return -1;
}
- if (!parent) {
- fr_strerror_printf("Invalid arguments: NULL parent");
+ if (!ev_p) {
+ fr_strerror_printf("Invalid arguments: NULL ev_p");
return -1;
}
* If there is an event, re-use it instead of freeing it
* and allocating a new one.
*/
- if (*parent) {
+ if (!*ev_p) {
+ new_event:
+ ev = talloc_zero(el, fr_event_timer_t);
+ if (!ev) return -1;
+
+ /*
+ * Bind the lifetime of the event to the specified
+ * talloc ctx. If the talloc ctx is freed, the
+ * event will also be freed.
+ */
+ if (ctx) fr_talloc_link_ctx(ctx, ev);
+
+ talloc_set_destructor(ev, _event_timer_free);
+ } else {
int ret;
- ev = talloc_get_type_abort(*parent, fr_event_timer_t);
+ ev = talloc_get_type_abort(*ev_p, fr_event_timer_t);
+
+ /*
+ * We can't disarm the linking context due to
+ * limitations in talloc, so if the linking
+ * context changes, we need to free the old
+ * event, and allocate a new one.
+ *
+ * Freeing the event also removes it from the heap.
+ */
+ if (ev->linked_ctx != ctx) {
+ talloc_free(ev);
+ goto new_event;
+ }
ret = fr_heap_extract(el->times, ev);
if (!fr_cond_assert(ret == 1)) return -1; /* events MUST be in the heap */
-
- memset(ev, 0, sizeof(*ev));
- } else {
- ev = talloc_zero(el, fr_event_timer_t);
- if (!ev) return -1;
}
- ev->callback = callback;
- ev->ctx = ctx;
ev->when = *when;
- ev->parent = parent;
+ ev->callback = callback;
+ ev->uctx = uctx;
+ ev->linked_ctx = ctx;
+ ev->parent = ev_p;
if (!fr_heap_insert(el->times, ev)) {
fr_strerror_printf("Failed inserting event into heap");
return -1;
}
- *parent = ev;
+ *ev_p = ev;
return 0;
}
user = talloc(el, fr_event_user_t);
user->callback = callback;
- user->ctx = uctx;
+ user->uctx = uctx;
user->ident = (uintptr_t) user;
fr_dlist_insert_tail(&el->user_callbacks, &user->entry);
user = fr_ptr_to_type(fr_event_user_t, entry, entry);
if ((user->callback == callback) &&
- (user->ctx == uctx)) {
+ (user->uctx == uctx)) {
fr_dlist_remove(entry);
talloc_free(user);
return 0;
return -1;
}
-
/** Add a pre-event callback to the event list.
*
* Events are serviced in insert order. i.e. insert A, B, we then
pre = talloc(el, fr_event_pre_t);
pre->callback = callback;
- pre->ctx = uctx;
+ pre->uctx = uctx;
fr_dlist_insert_tail(&el->pre_callbacks, &pre->entry);
return 0;
}
-
/** Delete a pre-event callback from the event list.
*
* @param[in] el containing the timer events.
pre = fr_ptr_to_type(fr_event_pre_t, entry, entry);
if ((pre->callback == callback) &&
- (pre->ctx == uctx)) {
+ (pre->uctx == uctx)) {
fr_dlist_remove(entry);
talloc_free(pre);
return 0;
return -1;
}
-
/** Add a post-event callback to the event list.
*
* Events are serviced in insert order. i.e. insert A, B, we then
post = talloc(el, fr_event_post_t);
post->callback = callback;
- post->ctx = uctx;
+ post->uctx = uctx;
fr_dlist_insert_tail(&el->post_callbacks, &post->entry);
return 0;
}
-
/** Delete a post-event callback from the event list.
*
* @param[in] el containing the timer events.
post = fr_ptr_to_type(fr_event_post_t, entry, entry);
if ((post->callback == callback) &&
- (post->ctx == uctx)) {
+ (post->uctx == uctx)) {
fr_dlist_remove(entry);
talloc_free(post);
return 0;
return -1;
}
-
/** Run a single scheduled timer event
*
* @param[in] el containing the timer events.
int fr_event_timer_run(fr_event_list_t *el, struct timeval *when)
{
fr_event_callback_t callback;
- void *ctx;
+ void *uctx;
fr_event_timer_t *ev;
if (!el) return 0;
}
callback = ev->callback;
- memcpy(&ctx, &ev->ctx, sizeof(ctx));
+ memcpy(&uctx, &ev->uctx, sizeof(uctx));
/*
* Delete the event before calling it.
*/
fr_event_timer_delete(el, ev->parent);
- callback(el, when, ctx);
+ callback(el, when, uctx);
return 1;
}
fr_event_pre_t *pre;
pre = fr_ptr_to_type(fr_event_pre_t, entry, entry);
- if (pre->callback(pre->ctx, wake) > 0) {
+ if (pre->callback(pre->uctx, wake) > 0) {
wake = &when;
when.tv_sec = 0;
when.tv_usec = 0;
(void) talloc_get_type_abort(user, fr_event_user_t);
rad_assert(user->ident == el->events[i].ident);
- user->callback(el->kq, &el->events[i], user->ctx);
+ user->callback(el->kq, &el->events[i], user->uctx);
continue;
}
* Call the error handler which should
* tear down the connection.
*/
- if (ev->error) ev->error(el, ev->fd, flags, fd_errno, ev->ctx);
+ if (ev->error) ev->error(el, ev->fd, flags, fd_errno, ev->uctx);
fr_event_fd_delete(el, ev->fd);
continue;
}
service:
ev->in_handler = true;
if (ev->read && (el->events[i].filter == EVFILT_READ)) {
- ev->read(el, ev->fd, flags, ev->ctx);
+ ev->read(el, ev->fd, flags, ev->uctx);
}
if (ev->write && (el->events[i].filter == EVFILT_WRITE) && !ev->deferred_delete) {
- ev->write(el, ev->fd, flags, ev->ctx);
+ ev->write(el, ev->fd, flags, ev->uctx);
}
ev->in_handler = false;
when = el->now;
post = fr_ptr_to_type(fr_event_post_t, entry, entry);
- post->callback(el, &when, post->ctx);
+ post->callback(el, &when, post->uctx);
}
}
/** Initialise a new event list
*
- * @param[in] ctx to allocate memory in.
- * @param[in] status callback, called on each iteration of the event list.
- * @param[in] status_ctx context for the status callback
+ * @param[in] ctx to allocate memory in.
+ * @param[in] status callback, called on each iteration of the event list.
+ * @param[in] status_uctx context for the status callback
* @return
* - A pointer to a new event list on success (free with talloc_free).
* - NULL on error.
*/
-fr_event_list_t *fr_event_list_alloc(TALLOC_CTX *ctx, fr_event_status_t status, void *status_ctx)
+fr_event_list_t *fr_event_list_alloc(TALLOC_CTX *ctx, fr_event_status_t status, void *status_uctx)
{
fr_event_list_t *el;
struct kevent kev;
FR_DLIST_INIT(el->post_callbacks);
FR_DLIST_INIT(el->user_callbacks);
- if (status) (void) fr_event_pre_insert(el, status, status_ctx);
+ if (status) (void) fr_event_pre_insert(el, status, status_uctx);
/*
* Set our "exit" callback as ident 0.
array[i].tv_usec -= 1000000;
array[i].tv_sec++;
}
- fr_event_timer_insert(el, print_time, &array[i], &array[i]);
+ fr_event_timer_insert(NULL, el, &array[i], print_time, &array[i]);
}
while (fr_event_list_num_elements(el)) {
now->tv_sec += conf->stats.interval;
now->tv_usec = 0;
- if (fr_event_timer_insert(el, rs_stats_process, ctx, now, &event) < 0) {
+ if (fr_event_timer_insert(NULL, el, &event,
+ now, rs_stats_process, ctx) < 0) {
ERROR("Failed inserting stats interval event");
}
}
rs_tv_add_ms(now, conf->stats.timeout, &(stats->quiet));
}
- if (fr_event_timer_insert(events, rs_stats_process, (void *) &update, now, &event) < 0) {
+ if (fr_event_timer_insert(NULL, events, (void *) &update,
+ now, rs_stats_process, &event) < 0) {
ERROR("Failed inserting stats event");
return -1;
}
*/
original->linked = talloc_steal(original, current);
rs_tv_add_ms(&header->ts, conf->stats.timeout, &original->when);
- if (fr_event_timer_insert(event->list, _rs_event, original, &original->when, &original->event) < 0) {
+ if (fr_event_timer_insert(NULL, event->list, &original->event,
+ &original->when, _rs_event, original) < 0) {
REDEBUG("Failed inserting new event");
/*
* Delete the original request/event, it's no longer valid
*/
original->packet->timestamp = header->ts;
rs_tv_add_ms(&header->ts, conf->stats.timeout, &original->when);
- if (fr_event_timer_insert(event->list, _rs_event, original,
- &original->when, &original->event) < 0) {
+ if (fr_event_timer_insert(NULL, event->list, &original->event,
+ &original->when, _rs_event, original) < 0) {
REDEBUG("Failed inserting new event");
talloc_free(original);
ERROR("Will attempt to re-establish connection in %i ms", RS_SOCKET_REOPEN_DELAY);
rs_tv_add_ms(now, RS_SOCKET_REOPEN_DELAY, &when);
- if (fr_event_timer_insert(el, rs_collectd_reopen, el, &when, &event) < 0) {
+ if (fr_event_timer_insert(NULL, el, &event,
+ &when, rs_collectd_reopen, el) < 0) {
ERROR("Failed inserting re-open event");
RS_ASSERT(0);
}
PERROR("Failed reinitialising sync, will retry in %pT seconds", &inst->sync_retry_interval);
fr_timeval_add(&when, now, &inst->sync_retry_interval);
- if (fr_event_timer_insert(el, proto_ldap_sync_reinit, user_ctx, &when, &inst->sync_retry_ev) < 0) {
+ if (fr_event_timer_insert(inst, el, &inst->sync_retry_ev,
+ &when, proto_ldap_sync_reinit, user_ctx) < 0) {
radlog_fatal("Failed inserting event: %s", fr_strerror());
}
}
memcpy(&ctx, &config, sizeof(ctx));
gettimeofday(&now, 0);
fr_timeval_add(&when, &now, &inst->sync_retry_interval);
- if (fr_event_timer_insert(inst->el, proto_ldap_sync_reinit, ctx, &when, &inst->sync_retry_ev) < 0) {
+ if (fr_event_timer_insert(inst, inst->el, &inst->sync_retry_ev,
+ &when, proto_ldap_sync_reinit, ctx) < 0) {
radlog_fatal("Failed inserting event: %s", fr_strerror());
}
return 1;
memcpy(&ctx, &config, sizeof(ctx));
gettimeofday(&now, 0);
fr_timeval_add(&when, &now, &inst->conn_retry_interval);
- if (fr_event_timer_insert(inst->el, proto_ldap_conn_init, listen, &when, &inst->conn_retry_ev) < 0) {
+ if (fr_event_timer_insert(inst, inst->el, &inst->conn_retry_ev,
+ &when, proto_ldap_conn_init, listen) < 0) {
radlog_fatal("Failed inserting event: %s", fr_strerror());
}
fr_timeval_add(&when, &now, &inst->conn_retry_interval);
- if (fr_event_timer_insert(inst->el, proto_ldap_conn_init,
- listen, &when, &inst->conn_retry_ev) < 0) {
+ if (fr_event_timer_insert(inst, inst->el, &inst->conn_retry_ev,
+ &when, proto_ldap_conn_init, listen) < 0) {
radlog_fatal("Failed inserting event: %s", fr_strerror());
}