kevent: refactor callback system
This commit is contained in:
parent
30404f60d4
commit
4359f43b0e
5 changed files with 77 additions and 52 deletions
|
@ -134,11 +134,8 @@ void irq_uart(void)
|
||||||
tmp = REG_UART_RHR;
|
tmp = REG_UART_RHR;
|
||||||
ringbuf_write(arch_serial_default_device.device.rx, &tmp, sizeof(tmp));
|
ringbuf_write(arch_serial_default_device.device.rx, &tmp, sizeof(tmp));
|
||||||
|
|
||||||
/* TODO: we need some error handling mechanism for event creation */
|
device_kevent_create_and_dispatch(&serial_default_device->device,
|
||||||
struct device_kevent *event = device_kevent_create(&serial_default_device->device,
|
DEVICE_CHANNEL_IN);
|
||||||
DEVICE_CHANNEL_IN);
|
|
||||||
if (event != NULL)
|
|
||||||
kevent_dispatch(&event->event);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* REG_UART_PDC_TCR has reached zero */
|
/* REG_UART_PDC_TCR has reached zero */
|
||||||
|
@ -153,11 +150,8 @@ void irq_uart(void)
|
||||||
if (arch_serial_default_device.tx_current == NULL)
|
if (arch_serial_default_device.tx_current == NULL)
|
||||||
REG_UART_IDR = REG_UART_IDR_ENDTX_MASK;
|
REG_UART_IDR = REG_UART_IDR_ENDTX_MASK;
|
||||||
|
|
||||||
/* TODO: we need some error handling mechanism for event creation */
|
device_kevent_create_and_dispatch(&serial_default_device->device,
|
||||||
struct device_kevent *event = device_kevent_create(&serial_default_device->device,
|
DEVICE_CHANNEL_OUT);
|
||||||
DEVICE_CHANNEL_OUT);
|
|
||||||
if (event != NULL)
|
|
||||||
kevent_dispatch(&event->event);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check for error conditions */
|
/* check for error conditions */
|
||||||
|
|
|
@ -50,6 +50,14 @@ __always_inline struct device *kevent_to_device(struct kevent *event)
|
||||||
*/
|
*/
|
||||||
struct device_kevent *device_kevent_create(struct device *device, enum device_channel channel);
|
struct device_kevent *device_kevent_create(struct device *device, enum device_channel channel);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convenience wrapper for creating and immediately dispatching a device kevent.
|
||||||
|
*
|
||||||
|
* @param device Device the event refers to
|
||||||
|
* @param channel Which channel (in or out) the event applies to
|
||||||
|
*/
|
||||||
|
void device_kevent_create_and_dispatch(struct device *device, enum device_channel channel);
|
||||||
|
|
||||||
/** Initialize the devices subsystem. */
|
/** Initialize the devices subsystem. */
|
||||||
int devices_init(void);
|
int devices_init(void);
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,31 @@ struct kevent {
|
||||||
enum kevent_kind kind;
|
enum kevent_kind kind;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Flags for kevent callback return values.
|
||||||
|
*/
|
||||||
|
enum kevent_cb_flags {
|
||||||
|
/** @brief No particular action is taken. */
|
||||||
|
KEVENT_CB_NONE = 0,
|
||||||
|
/** @brief Stop processing the other callbacks in the queue after the callback. */
|
||||||
|
KEVENT_CB_STOP = (1 << 0),
|
||||||
|
/** @brief Call `kevent_listener_del()` after the callback. */
|
||||||
|
KEVENT_CB_LISTENER_DEL = (1 << 1),
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Identifies a single kevent listener.
|
||||||
|
*
|
||||||
|
* This is returned by `kevent_listener_add()` and must be passed to
|
||||||
|
* `kevent_listener_del()` when the listener is no longer needed.
|
||||||
|
* You shouldn't modify the members yourself.
|
||||||
|
*/
|
||||||
|
struct kevent_listener {
|
||||||
|
struct list_head link;
|
||||||
|
int (*cb)(struct kevent *event, void *extra);
|
||||||
|
void *extra;
|
||||||
|
};
|
||||||
|
|
||||||
/** @brief Initialize the kevent subsystem. */
|
/** @brief Initialize the kevent subsystem. */
|
||||||
void kevents_init(void);
|
void kevents_init(void);
|
||||||
|
|
||||||
|
@ -55,27 +80,24 @@ void kevent_dispatch(struct kevent *event);
|
||||||
/**
|
/**
|
||||||
* @brief Add an event listener to the end of the listener queue.
|
* @brief Add an event listener to the end of the listener queue.
|
||||||
* The callback will be invoked for every event that is dispatched and matches
|
* The callback will be invoked for every event that is dispatched and matches
|
||||||
* the event kind. If its return value is nonzero, event handler processing
|
* the event kind. The return value of this callback is a set of flags, see
|
||||||
* stops after that callback. This is useful if you know for sure that you are
|
* `enum kevent_cb_flags` for details.
|
||||||
* the only one interested in the event, for example when waiting for I/O.
|
|
||||||
*
|
*
|
||||||
* @param kind Kind of kevent to listen for
|
* @param kind Kind of kevent to listen for
|
||||||
* @param cb Callback that will be invoked for every kevent that is dispatched
|
* @param cb Callback that will be invoked for every kevent that is dispatched
|
||||||
* @param extra An optional extra pointer that will be passed to the callback
|
* @param extra An optional extra pointer that will be passed to the callback
|
||||||
* @returns 0 on success, or a negtive error number on failure
|
* @returns The listener (pass to `kevent_listener_del()` when no longer needed)
|
||||||
*/
|
*/
|
||||||
int kevent_add_listener(enum kevent_kind kind,
|
struct kevent_listener *kevent_listener_add(enum kevent_kind kind,
|
||||||
int (*cb)(struct kevent *event, void *extra),
|
int (*cb)(struct kevent *event, void *extra),
|
||||||
void *extra);
|
void *extra);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Remove an event listener.
|
* @brief Remove an event listener.
|
||||||
*
|
*
|
||||||
* @param kind Kind that was passed to `kevent_add_listener()`
|
* @param listener The listener returned by `kevent_listener_add()`
|
||||||
* @param cb Callback that was passed to `kevent_add_listener()`
|
|
||||||
*/
|
*/
|
||||||
void kevent_remove_listener(enum kevent_kind kind,
|
void kevent_listener_del(struct kevent_listener *listener);
|
||||||
int (*cb)(struct kevent *event, void *extra));
|
|
||||||
|
|
||||||
__always_inline void kevent_get(struct kevent *event)
|
__always_inline void kevent_get(struct kevent *event)
|
||||||
{
|
{
|
||||||
|
|
|
@ -77,6 +77,13 @@ struct device_kevent *device_kevent_create(struct device *device, enum device_ch
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void device_kevent_create_and_dispatch(struct device *device, enum device_channel channel)
|
||||||
|
{
|
||||||
|
struct device_kevent *event = device_kevent_create(device, channel);
|
||||||
|
if (event != NULL)
|
||||||
|
kevent_dispatch(&event->event);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This file is part of Ardix.
|
* This file is part of Ardix.
|
||||||
* Copyright (c) 2020, 2021 Felix Kopp <owo@fef.moe>.
|
* Copyright (c) 2020, 2021 Felix Kopp <owo@fef.moe>.
|
||||||
|
|
|
@ -31,20 +31,13 @@ static struct kevent_queue kev_queues[KEVENT_KIND_COUNT];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* irqs cannot sleep or block, so we can only *try* to claim the lock on
|
* irqs cannot sleep or block, so we can only *try* to claim the lock on
|
||||||
* an event queue when dispatching an event. If this fails (which shouldn't
|
* an event queue when dispatching an event. If this fails (which should
|
||||||
* happen only rarely), we store the event in this (unsorted) pile and sort them
|
* happen only rarely), we store the event in this (unsorted) pile and sort them
|
||||||
* out before processing the event queue. We technically need a lock for this
|
* out before processing the event queue.
|
||||||
* queue as well, but that's okay because we don't
|
|
||||||
*/
|
*/
|
||||||
LIST_HEAD(kev_cache);
|
LIST_HEAD(kev_cache);
|
||||||
MUTEX(kev_cache_lock);
|
MUTEX(kev_cache_lock);
|
||||||
|
|
||||||
struct kevent_listener {
|
|
||||||
struct list_head link; /* -> kevent_queue::list */
|
|
||||||
void *extra;
|
|
||||||
int (*cb)(struct kevent *event, void *extra);
|
|
||||||
};
|
|
||||||
|
|
||||||
void kevents_init(void)
|
void kevents_init(void)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < KEVENT_KIND_COUNT; i++) {
|
for (int i = 0; i < KEVENT_KIND_COUNT; i++) {
|
||||||
|
@ -71,7 +64,13 @@ static inline void process_single_queue(struct kevent_queue *queue, struct list_
|
||||||
|
|
||||||
list_for_each_entry(listeners, listener, link) {
|
list_for_each_entry(listeners, listener, link) {
|
||||||
int cb_ret = listener->cb(event, listener->extra);
|
int cb_ret = listener->cb(event, listener->extra);
|
||||||
if (cb_ret != 0)
|
|
||||||
|
if (cb_ret & KEVENT_CB_LISTENER_DEL) {
|
||||||
|
list_delete(&listener->link);
|
||||||
|
free(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cb_ret & KEVENT_CB_STOP)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,36 +148,31 @@ void kevent_dispatch(struct kevent *event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int kevent_add_listener(enum kevent_kind kind, int (*cb)(struct kevent *, void *), void *extra)
|
struct kevent_listener *kevent_listener_add(enum kevent_kind kind,
|
||||||
|
int (*cb)(struct kevent *, void *),
|
||||||
|
void *extra)
|
||||||
{
|
{
|
||||||
struct kevent_listener *listener = malloc(sizeof(*listener));
|
struct kevent_listener *listener = malloc(sizeof(*listener));
|
||||||
if (listener == NULL)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
listener->cb = cb;
|
if (listener != NULL) {
|
||||||
listener->extra = extra;
|
listener->cb = cb;
|
||||||
|
listener->extra = extra;
|
||||||
|
|
||||||
mutex_lock(&kev_listeners_lock);
|
mutex_lock(&kev_listeners_lock);
|
||||||
list_insert(&kev_listeners[kind], &listener->link);
|
list_insert(&kev_listeners[kind], &listener->link);
|
||||||
mutex_unlock(&kev_listeners_lock);
|
mutex_unlock(&kev_listeners_lock);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
void kevent_remove_listener(enum kevent_kind kind, int (*cb)(struct kevent *, void *))
|
void kevent_listener_del(struct kevent_listener *listener)
|
||||||
{
|
{
|
||||||
struct kevent_listener *listener, *tmp;
|
mutex_lock(&kev_listeners_lock);
|
||||||
|
list_delete(&listener->link);
|
||||||
|
mutex_unlock(&kev_listeners_lock);
|
||||||
|
|
||||||
list_for_each_entry_safe(&kev_listeners[kind], listener, tmp, link) {
|
free(listener);
|
||||||
if (listener->cb == cb) {
|
|
||||||
mutex_lock(&kev_listeners_lock);
|
|
||||||
list_delete(&listener->link);
|
|
||||||
mutex_unlock(&kev_listeners_lock);
|
|
||||||
|
|
||||||
free(listener);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in a new issue