mirror of https://github.com/ARMmbed/mbed-os.git
equeue: add user allocated event support
Allow posting events allocated outside queue memorypull/11342/head
parent
95fc8cfa2c
commit
66da694625
|
@ -176,6 +176,17 @@ void equeue_event_dtor(void *event, void (*dtor)(void *));
|
||||||
// be passed to equeue_cancel.
|
// be passed to equeue_cancel.
|
||||||
int equeue_post(equeue_t *queue, void (*cb)(void *), void *event);
|
int equeue_post(equeue_t *queue, void (*cb)(void *), void *event);
|
||||||
|
|
||||||
|
// Post an user allocated event onto the event queue
|
||||||
|
//
|
||||||
|
// The equeue_post_user_allocated function takes a callback and a pointer
|
||||||
|
// to an event allocated by user. The specified callback will be executed
|
||||||
|
// in the context of the event queue's dispatch loop with the allocated
|
||||||
|
// event as its argument.
|
||||||
|
//
|
||||||
|
// The equeue_post_user_allocated function is irq safe and can act as
|
||||||
|
// a mechanism for moving events out of irq contexts.
|
||||||
|
void equeue_post_user_allocated(equeue_t *queue, void (*cb)(void *), void *event);
|
||||||
|
|
||||||
// Cancel an in-flight event
|
// Cancel an in-flight event
|
||||||
//
|
//
|
||||||
// Attempts to cancel an event referenced by the unique id returned from
|
// Attempts to cancel an event referenced by the unique id returned from
|
||||||
|
@ -191,6 +202,20 @@ int equeue_post(equeue_t *queue, void (*cb)(void *), void *event);
|
||||||
// Returning false if invalid id or already started executing.
|
// Returning false if invalid id or already started executing.
|
||||||
bool equeue_cancel(equeue_t *queue, int id);
|
bool equeue_cancel(equeue_t *queue, int id);
|
||||||
|
|
||||||
|
// Cancel an in-flight user allocated event
|
||||||
|
//
|
||||||
|
// Attempts to cancel an event referenced by its address.
|
||||||
|
// It is safe to call equeue_cancel_user_allocated after an event
|
||||||
|
// has already been dispatched.
|
||||||
|
//
|
||||||
|
// The equeue_cancel_user_allocated function is irq safe.
|
||||||
|
//
|
||||||
|
// If called while the event queue's dispatch loop is active,
|
||||||
|
// equeue_cancel_user_allocated does not guarantee that the event
|
||||||
|
// will not not execute after it returns as the event may have
|
||||||
|
// already begun executing.
|
||||||
|
bool equeue_cancel_user_allocated(equeue_t *queue, void *event);
|
||||||
|
|
||||||
// Query how much time is left for delayed event
|
// Query how much time is left for delayed event
|
||||||
//
|
//
|
||||||
// If event is delayed, this function can be used to query how much time
|
// If event is delayed, this function can be used to query how much time
|
||||||
|
@ -200,6 +225,15 @@ bool equeue_cancel(equeue_t *queue, int id);
|
||||||
//
|
//
|
||||||
int equeue_timeleft(equeue_t *q, int id);
|
int equeue_timeleft(equeue_t *q, int id);
|
||||||
|
|
||||||
|
// Query how much time is left for delayed user allocated event
|
||||||
|
//
|
||||||
|
// If event is delayed, this function can be used to query how much time
|
||||||
|
// is left until the event is due to be dispatched.
|
||||||
|
//
|
||||||
|
// This function is irq safe.
|
||||||
|
//
|
||||||
|
int equeue_timeleft_user_allocated(equeue_t *q, void *event);
|
||||||
|
|
||||||
// Background an event queue onto a single-shot timer
|
// Background an event queue onto a single-shot timer
|
||||||
//
|
//
|
||||||
// The provided update function will be called to indicate when the queue
|
// The provided update function will be called to indicate when the queue
|
||||||
|
|
|
@ -21,6 +21,9 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
// check if the event is allocaded by user - event address is outside queues internal buffer address range
|
||||||
|
#define EQUEUE_IS_USER_ALLOCATED_EVENT(e) (((uintptr_t)(e) < (uintptr_t)q->buffer) || ((uintptr_t)(e) > ((uintptr_t)q->slab.data)))
|
||||||
|
|
||||||
// calculate the relative-difference between absolute times while
|
// calculate the relative-difference between absolute times while
|
||||||
// correctly handling overflow conditions
|
// correctly handling overflow conditions
|
||||||
static inline int equeue_tickdiff(unsigned a, unsigned b)
|
static inline int equeue_tickdiff(unsigned a, unsigned b)
|
||||||
|
@ -64,9 +67,15 @@ int equeue_create_inplace(equeue_t *q, size_t size, void *buffer)
|
||||||
{
|
{
|
||||||
// setup queue around provided buffer
|
// setup queue around provided buffer
|
||||||
// ensure buffer and size are aligned
|
// ensure buffer and size are aligned
|
||||||
|
if (size >= sizeof(void *)) {
|
||||||
q->buffer = (void *)(((uintptr_t) buffer + sizeof(void *) -1) & ~(sizeof(void *) -1));
|
q->buffer = (void *)(((uintptr_t) buffer + sizeof(void *) -1) & ~(sizeof(void *) -1));
|
||||||
size -= (char *) q->buffer - (char *) buffer;
|
size -= (char *) q->buffer - (char *) buffer;
|
||||||
size &= ~(sizeof(void *) -1);
|
size &= ~(sizeof(void *) -1);
|
||||||
|
} else {
|
||||||
|
// don't align when size less then pointer size
|
||||||
|
// e.g. static queue (size == 1)
|
||||||
|
q->buffer = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
q->allocated = 0;
|
q->allocated = 0;
|
||||||
|
|
||||||
|
@ -220,15 +229,13 @@ void equeue_dealloc(equeue_t *q, void *p)
|
||||||
e->dtor(e + 1);
|
e->dtor(e + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!EQUEUE_IS_USER_ALLOCATED_EVENT(e)) {
|
||||||
equeue_mem_dealloc(q, e);
|
equeue_mem_dealloc(q, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void equeue_enqueue(equeue_t *q, struct equeue_event *e, unsigned tick)
|
||||||
// equeue scheduling functions
|
|
||||||
static int equeue_enqueue(equeue_t *q, struct equeue_event *e, unsigned tick)
|
|
||||||
{
|
{
|
||||||
// setup event and hash local id with buffer offset for unique id
|
|
||||||
int id = (e->id << q->npw2) | ((unsigned char *)e - q->buffer);
|
|
||||||
e->target = tick + equeue_clampdiff(e->target, tick);
|
e->target = tick + equeue_clampdiff(e->target, tick);
|
||||||
e->generation = q->generation;
|
e->generation = q->generation;
|
||||||
|
|
||||||
|
@ -254,7 +261,6 @@ static int equeue_enqueue(equeue_t *q, struct equeue_event *e, unsigned tick)
|
||||||
if (e->next) {
|
if (e->next) {
|
||||||
e->next->ref = &e->next;
|
e->next->ref = &e->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
e->sibling = 0;
|
e->sibling = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,24 +273,19 @@ static int equeue_enqueue(equeue_t *q, struct equeue_event *e, unsigned tick)
|
||||||
q->background.update(q->background.timer,
|
q->background.update(q->background.timer,
|
||||||
equeue_clampdiff(e->target, tick));
|
equeue_clampdiff(e->target, tick));
|
||||||
}
|
}
|
||||||
|
|
||||||
equeue_mutex_unlock(&q->queuelock);
|
equeue_mutex_unlock(&q->queuelock);
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct equeue_event *equeue_unqueue(equeue_t *q, int id)
|
// equeue scheduling functions
|
||||||
|
static int equeue_event_id(equeue_t *q, struct equeue_event *e)
|
||||||
{
|
{
|
||||||
// decode event from unique id and check that the local id matches
|
// setup event and hash local id with buffer offset for unique id
|
||||||
struct equeue_event *e = (struct equeue_event *)
|
return ((e->id << q->npw2) | ((unsigned char *)e - q->buffer));
|
||||||
&q->buffer[id & ((1 << q->npw2) - 1)];
|
}
|
||||||
|
|
||||||
|
static struct equeue_event *equeue_unqueue_by_address(equeue_t *q, struct equeue_event *e)
|
||||||
|
{
|
||||||
equeue_mutex_lock(&q->queuelock);
|
equeue_mutex_lock(&q->queuelock);
|
||||||
if (e->id != id >> q->npw2) {
|
|
||||||
equeue_mutex_unlock(&q->queuelock);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear the event and check if already in-flight
|
// clear the event and check if already in-flight
|
||||||
e->cb = 0;
|
e->cb = 0;
|
||||||
e->period = -1;
|
e->period = -1;
|
||||||
|
@ -310,6 +311,26 @@ static struct equeue_event *equeue_unqueue(equeue_t *q, int id)
|
||||||
e->next->ref = e->ref;
|
e->next->ref = e->ref;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
equeue_mutex_unlock(&q->queuelock);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct equeue_event *equeue_unqueue_by_id(equeue_t *q, int id)
|
||||||
|
{
|
||||||
|
// decode event from unique id and check that the local id matches
|
||||||
|
struct equeue_event *e = (struct equeue_event *)
|
||||||
|
&q->buffer[id & ((1 << q->npw2) - 1)];
|
||||||
|
|
||||||
|
equeue_mutex_lock(&q->queuelock);
|
||||||
|
if (e->id != id >> q->npw2) {
|
||||||
|
equeue_mutex_unlock(&q->queuelock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 == equeue_unqueue_by_address(q, e)) {
|
||||||
|
equeue_mutex_unlock(&q->queuelock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
equeue_incid(q, e);
|
equeue_incid(q, e);
|
||||||
equeue_mutex_unlock(&q->queuelock);
|
equeue_mutex_unlock(&q->queuelock);
|
||||||
|
@ -369,18 +390,30 @@ int equeue_post(equeue_t *q, void (*cb)(void *), void *p)
|
||||||
e->cb = cb;
|
e->cb = cb;
|
||||||
e->target = tick + e->target;
|
e->target = tick + e->target;
|
||||||
|
|
||||||
int id = equeue_enqueue(q, e, tick);
|
equeue_enqueue(q, e, tick);
|
||||||
|
int id = equeue_event_id(q, e);
|
||||||
equeue_sema_signal(&q->eventsema);
|
equeue_sema_signal(&q->eventsema);
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void equeue_post_user_allocated(equeue_t *q, void (*cb)(void *), void *p)
|
||||||
|
{
|
||||||
|
struct equeue_event *e = (struct equeue_event *)p;
|
||||||
|
unsigned tick = equeue_tick();
|
||||||
|
e->cb = cb;
|
||||||
|
e->target = tick + e->target;
|
||||||
|
|
||||||
|
equeue_enqueue(q, e, tick);
|
||||||
|
equeue_sema_signal(&q->eventsema);
|
||||||
|
}
|
||||||
|
|
||||||
bool equeue_cancel(equeue_t *q, int id)
|
bool equeue_cancel(equeue_t *q, int id)
|
||||||
{
|
{
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct equeue_event *e = equeue_unqueue(q, id);
|
struct equeue_event *e = equeue_unqueue_by_id(q, id);
|
||||||
if (e) {
|
if (e) {
|
||||||
equeue_dealloc(q, e + 1);
|
equeue_dealloc(q, e + 1);
|
||||||
return true;
|
return true;
|
||||||
|
@ -389,6 +422,21 @@ bool equeue_cancel(equeue_t *q, int id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool equeue_cancel_user_allocated(equeue_t *q, void *e)
|
||||||
|
{
|
||||||
|
if (!e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct equeue_event *_e = equeue_unqueue_by_address(q, e);
|
||||||
|
if (_e) {
|
||||||
|
equeue_dealloc(q, _e + 1);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int equeue_timeleft(equeue_t *q, int id)
|
int equeue_timeleft(equeue_t *q, int id)
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
|
@ -409,6 +457,21 @@ int equeue_timeleft(equeue_t *q, int id)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int equeue_timeleft_user_allocated(equeue_t *q, void *e)
|
||||||
|
{
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
if (!e) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct equeue_event *_e = (struct equeue_event *)e;
|
||||||
|
equeue_mutex_lock(&q->queuelock);
|
||||||
|
ret = equeue_clampdiff(_e->target, equeue_tick());
|
||||||
|
equeue_mutex_unlock(&q->queuelock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
void equeue_break(equeue_t *q)
|
void equeue_break(equeue_t *q)
|
||||||
{
|
{
|
||||||
equeue_mutex_lock(&q->queuelock);
|
equeue_mutex_lock(&q->queuelock);
|
||||||
|
|
Loading…
Reference in New Issue