From 7223e1217f808e727d428404de8c23ed88ce52cd Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sun, 21 Aug 2016 16:02:22 -0500 Subject: [PATCH] Added the Event class for fine-grain control of event dispatch For a more fine-grain control of event dispatch, the Event class can be manually instantiated and configured. More information and examples can be found in the updated readme and documentation in Event.h --- Event.h | 1186 +++++++++++++++++++++++++++++++++++ EventQueue.h | 455 ++++++++++++-- README.md | 37 +- TESTS/events/queue/main.cpp | 31 + mbed_events.h | 1 + 5 files changed, 1652 insertions(+), 58 deletions(-) create mode 100644 Event.h diff --git a/Event.h b/Event.h new file mode 100644 index 0000000..f74e3b0 --- /dev/null +++ b/Event.h @@ -0,0 +1,1186 @@ +/* events + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef EVENT_H +#define EVENT_H + +#include "EventQueue.h" +#include "mbed_assert.h" + +namespace events { + +/** Event + * + * Representation of an event for fine-grain dispatch control + */ +template +class Event; + +/** Event + * + * Representation of an event for fine-grain dispatch control + */ +template <> +class Event<> { +public: + /** Event lifetime + * + * Constructs an event bound to the specified event queue. The specified + * callback acts as the target for the event and is executed in the + * context of the event queue's dispatch loop once posted. + * + * @param f Function to execute when the event is dispatched + * @param a0..a4 Arguments to pass to the callback + */ + template + Event(EventQueue *q, F f) { + struct local { + static int post(struct event *e) { + typedef EventQueue::context00 C; + struct local { + static void call(void *p) { (*static_cast(p))(); } + static void dtor(void *p) { static_cast(p)->~C(); } + }; + + void *p = equeue_alloc(e->equeue, sizeof(C)); + if (!p) { + return 0; + } + + new (p) C(*reinterpret_cast(e+1)); + equeue_event_delay(p, e->delay); + equeue_event_period(p, e->period); + equeue_event_dtor(p, &local::dtor); + return equeue_post(e->equeue, &local::call, p); + } + + static void dtor(struct event *e) { + reinterpret_cast(e+1)->~F(); + } + }; + + _event = static_cast( + equeue_alloc(&q->_equeue, sizeof(struct event) + sizeof(F))); + if (_event) { + _event->equeue = &q->_equeue; + _event->id = 0; + _event->delay = 0; + _event->period = -1; + + _event->post = &local::post; + _event->dtor = &local::dtor; + + new (_event+1) F(f); + + _event->ref = 1; + } + } + + template + Event(EventQueue *q, F f, B0 b0) { + new (this) Event(q, EventQueue:: + context10(f, b0)); + } + + template + Event(EventQueue *q, F f, B0 b0, B1 b1) { + new (this) Event(q, EventQueue:: + context20(f, b0, b1)); + } + + template + Event(EventQueue *q, F f, B0 b0, B1 b1, B2 b2) { + new (this) Event(q, EventQueue:: + context30(f, b0, b1, b2)); + } + + template + Event(EventQueue *q, F f, B0 b0, B1 b1, B2 b2, B3 b3) { + new (this) Event(q, EventQueue:: + context40(f, b0, b1, b2, b3)); + } + + template + Event(EventQueue *q, F f, B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) { + new (this) Event(q, EventQueue:: + context50(f, b0, b1, b2, b3, b4)); + } + + Event(const Event &e) { + _event = 0; + if (e._event) { + _event = e._event; + _event->ref += 1; + } + } + + Event &operator=(const Event &that) { + if (this != &that) { + this->~Event(); + new (this) Event(that); + } + + return *this; + } + + ~Event() { + if (_event) { + _event->ref -= 1; + if (_event->ref == 0) { + _event->dtor(_event); + equeue_dealloc(_event->equeue, _event); + } + } + } + + /** Configure the delay of an event + * + * @param delay Millisecond delay before dispatching the event + */ + void delay(int delay) { + if (_event) { + _event->delay = delay; + } + } + + /** Configure the period of an event + * + * @param period Millisecond period for repeatedly dispatching an event + */ + void period(int period) { + if (_event) { + _event->period = period; + } + } + + /** Posts an event onto the underlying event queue + * + * The event is posted to the underlying queue and is executed in the + * context of the event queue's dispatch loop. + * + * The post function is irq safe and can act as a mechanism for moving + * events out of irq contexts. + * + * @param a0..a4 Arguments to pass to the event + * @return A unique id that represents the posted event and can + * be passed to EventQueue::cancel, or an id of 0 if + * there is not enough memory to allocate the event. + */ + int post() { + if (!_event) { + return 0; + } + + _event->id = _event->post(_event); + return _event->id; + } + + /** Cancels the most recently posted event + * + * Attempts to cancel the most recently posted event. It is safe to call + * cancel after an event has already been dispatched. + * + * The cancel function is irq safe. + * + * If called while the event queue's dispatch loop is active, the cancel + * function does not garuntee that the event will not execute after it + * returns, as the event may have already begun executing. + */ + void cancel() { + if (_event) { + equeue_cancel(_event->equeue, _event->id); + } + } + +private: + struct event { + unsigned ref; + equeue_t *equeue; + int id; + + int delay; + int period; + + int (*post)(struct event *); + void (*dtor)(struct event *); + + // F follows + } *_event; +}; + +/** Event + * + * Representation of an event for fine-grain dispatch control + */ +template +class Event { +public: + /** Event lifetime + * + * Constructs an event bound to the specified event queue. The specified + * callback acts as the target for the event and is executed in the + * context of the event queue's dispatch loop once posted. + * + * @param f Function to execute when the event is dispatched + * @param a0..a4 Arguments to pass to the callback + */ + template + Event(EventQueue *q, F f) { + struct local { + static int post(struct event *e, A0 a0) { + typedef EventQueue::context10 C; + struct local { + static void call(void *p) { (*static_cast(p))(); } + static void dtor(void *p) { static_cast(p)->~C(); } + }; + + void *p = equeue_alloc(e->equeue, sizeof(C)); + if (!p) { + return 0; + } + + new (p) C(*reinterpret_cast(e+1), a0); + equeue_event_delay(p, e->delay); + equeue_event_period(p, e->period); + equeue_event_dtor(p, &local::dtor); + return equeue_post(e->equeue, &local::call, p); + } + + static void dtor(struct event *e) { + reinterpret_cast(e+1)->~F(); + } + }; + + _event = static_cast( + equeue_alloc(&q->_equeue, sizeof(struct event) + sizeof(F))); + if (_event) { + _event->equeue = &q->_equeue; + _event->id = 0; + _event->delay = 0; + _event->period = -1; + + _event->post = &local::post; + _event->dtor = &local::dtor; + + new (_event+1) F(f); + + _event->ref = 1; + } + } + + template + Event(EventQueue *q, F f, B0 b0) { + new (this) Event(q, EventQueue:: + context11(f, b0)); + } + + template + Event(EventQueue *q, F f, B0 b0, B1 b1) { + new (this) Event(q, EventQueue:: + context21(f, b0, b1)); + } + + template + Event(EventQueue *q, F f, B0 b0, B1 b1, B2 b2) { + new (this) Event(q, EventQueue:: + context31(f, b0, b1, b2)); + } + + template + Event(EventQueue *q, F f, B0 b0, B1 b1, B2 b2, B3 b3) { + new (this) Event(q, EventQueue:: + context41(f, b0, b1, b2, b3)); + } + + template + Event(EventQueue *q, F f, B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) { + new (this) Event(q, EventQueue:: + context51(f, b0, b1, b2, b3, b4)); + } + + Event(const Event &e) { + _event = 0; + if (e._event) { + _event = e._event; + _event->ref += 1; + } + } + + Event &operator=(const Event &that) { + if (this != &that) { + this->~Event(); + new (this) Event(that); + } + + return *this; + } + + ~Event() { + if (_event) { + _event->ref -= 1; + if (_event->ref == 0) { + _event->dtor(_event); + equeue_dealloc(_event->equeue, _event); + } + } + } + + /** Configure the delay of an event + * + * @param delay Millisecond delay before dispatching the event + */ + void delay(int delay) { + if (_event) { + _event->delay = delay; + } + } + + /** Configure the period of an event + * + * @param period Millisecond period for repeatedly dispatching an event + */ + void period(int period) { + if (_event) { + _event->period = period; + } + } + + /** Posts an event onto the underlying event queue + * + * The event is posted to the underlying queue and is executed in the + * context of the event queue's dispatch loop. + * + * The post function is irq safe and can act as a mechanism for moving + * events out of irq contexts. + * + * @param a0..a4 Arguments to pass to the event + * @return A unique id that represents the posted event and can + * be passed to EventQueue::cancel, or an id of 0 if + * there is not enough memory to allocate the event. + */ + int post(A0 a0) { + if (!_event) { + return 0; + } + + _event->id = _event->post(_event, a0); + return _event->id; + } + + /** Cancels the most recently posted event + * + * Attempts to cancel the most recently posted event. It is safe to call + * cancel after an event has already been dispatched. + * + * The cancel function is irq safe. + * + * If called while the event queue's dispatch loop is active, the cancel + * function does not garuntee that the event will not execute after it + * returns, as the event may have already begun executing. + */ + void cancel() { + if (_event) { + equeue_cancel(_event->equeue, _event->id); + } + } + +private: + struct event { + unsigned ref; + equeue_t *equeue; + int id; + + int delay; + int period; + + int (*post)(struct event *, A0 a0); + void (*dtor)(struct event *); + + // F follows + } *_event; +}; + +/** Event + * + * Representation of an event for fine-grain dispatch control + */ +template +class Event { +public: + /** Event lifetime + * + * Constructs an event bound to the specified event queue. The specified + * callback acts as the target for the event and is executed in the + * context of the event queue's dispatch loop once posted. + * + * @param f Function to execute when the event is dispatched + * @param a0..a4 Arguments to pass to the callback + */ + template + Event(EventQueue *q, F f) { + struct local { + static int post(struct event *e, A0 a0, A1 a1) { + typedef EventQueue::context20 C; + struct local { + static void call(void *p) { (*static_cast(p))(); } + static void dtor(void *p) { static_cast(p)->~C(); } + }; + + void *p = equeue_alloc(e->equeue, sizeof(C)); + if (!p) { + return 0; + } + + new (p) C(*reinterpret_cast(e+1), a0, a1); + equeue_event_delay(p, e->delay); + equeue_event_period(p, e->period); + equeue_event_dtor(p, &local::dtor); + return equeue_post(e->equeue, &local::call, p); + } + + static void dtor(struct event *e) { + reinterpret_cast(e+1)->~F(); + } + }; + + _event = static_cast( + equeue_alloc(&q->_equeue, sizeof(struct event) + sizeof(F))); + if (_event) { + _event->equeue = &q->_equeue; + _event->id = 0; + _event->delay = 0; + _event->period = -1; + + _event->post = &local::post; + _event->dtor = &local::dtor; + + new (_event+1) F(f); + + _event->ref = 1; + } + } + + template + Event(EventQueue *q, F f, B0 b0) { + new (this) Event(q, EventQueue:: + context12(f, b0)); + } + + template + Event(EventQueue *q, F f, B0 b0, B1 b1) { + new (this) Event(q, EventQueue:: + context22(f, b0, b1)); + } + + template + Event(EventQueue *q, F f, B0 b0, B1 b1, B2 b2) { + new (this) Event(q, EventQueue:: + context32(f, b0, b1, b2)); + } + + template + Event(EventQueue *q, F f, B0 b0, B1 b1, B2 b2, B3 b3) { + new (this) Event(q, EventQueue:: + context42(f, b0, b1, b2, b3)); + } + + template + Event(EventQueue *q, F f, B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) { + new (this) Event(q, EventQueue:: + context52(f, b0, b1, b2, b3, b4)); + } + + Event(const Event &e) { + _event = 0; + if (e._event) { + _event = e._event; + _event->ref += 1; + } + } + + Event &operator=(const Event &that) { + if (this != &that) { + this->~Event(); + new (this) Event(that); + } + + return *this; + } + + ~Event() { + if (_event) { + _event->ref -= 1; + if (_event->ref == 0) { + _event->dtor(_event); + equeue_dealloc(_event->equeue, _event); + } + } + } + + /** Configure the delay of an event + * + * @param delay Millisecond delay before dispatching the event + */ + void delay(int delay) { + if (_event) { + _event->delay = delay; + } + } + + /** Configure the period of an event + * + * @param period Millisecond period for repeatedly dispatching an event + */ + void period(int period) { + if (_event) { + _event->period = period; + } + } + + /** Posts an event onto the underlying event queue + * + * The event is posted to the underlying queue and is executed in the + * context of the event queue's dispatch loop. + * + * The post function is irq safe and can act as a mechanism for moving + * events out of irq contexts. + * + * @param a0..a4 Arguments to pass to the event + * @return A unique id that represents the posted event and can + * be passed to EventQueue::cancel, or an id of 0 if + * there is not enough memory to allocate the event. + */ + int post(A0 a0, A1 a1) { + if (!_event) { + return 0; + } + + _event->id = _event->post(_event, a0, a1); + return _event->id; + } + + /** Cancels the most recently posted event + * + * Attempts to cancel the most recently posted event. It is safe to call + * cancel after an event has already been dispatched. + * + * The cancel function is irq safe. + * + * If called while the event queue's dispatch loop is active, the cancel + * function does not garuntee that the event will not execute after it + * returns, as the event may have already begun executing. + */ + void cancel() { + if (_event) { + equeue_cancel(_event->equeue, _event->id); + } + } + +private: + struct event { + unsigned ref; + equeue_t *equeue; + int id; + + int delay; + int period; + + int (*post)(struct event *, A0 a0, A1 a1); + void (*dtor)(struct event *); + + // F follows + } *_event; +}; + +/** Event + * + * Representation of an event for fine-grain dispatch control + */ +template +class Event { +public: + /** Event lifetime + * + * Constructs an event bound to the specified event queue. The specified + * callback acts as the target for the event and is executed in the + * context of the event queue's dispatch loop once posted. + * + * @param f Function to execute when the event is dispatched + * @param a0..a4 Arguments to pass to the callback + */ + template + Event(EventQueue *q, F f) { + struct local { + static int post(struct event *e, A0 a0, A1 a1, A2 a2) { + typedef EventQueue::context30 C; + struct local { + static void call(void *p) { (*static_cast(p))(); } + static void dtor(void *p) { static_cast(p)->~C(); } + }; + + void *p = equeue_alloc(e->equeue, sizeof(C)); + if (!p) { + return 0; + } + + new (p) C(*reinterpret_cast(e+1), a0, a1, a2); + equeue_event_delay(p, e->delay); + equeue_event_period(p, e->period); + equeue_event_dtor(p, &local::dtor); + return equeue_post(e->equeue, &local::call, p); + } + + static void dtor(struct event *e) { + reinterpret_cast(e+1)->~F(); + } + }; + + _event = static_cast( + equeue_alloc(&q->_equeue, sizeof(struct event) + sizeof(F))); + if (_event) { + _event->equeue = &q->_equeue; + _event->id = 0; + _event->delay = 0; + _event->period = -1; + + _event->post = &local::post; + _event->dtor = &local::dtor; + + new (_event+1) F(f); + + _event->ref = 1; + } + } + + template + Event(EventQueue *q, F f, B0 b0) { + new (this) Event(q, EventQueue:: + context13(f, b0)); + } + + template + Event(EventQueue *q, F f, B0 b0, B1 b1) { + new (this) Event(q, EventQueue:: + context23(f, b0, b1)); + } + + template + Event(EventQueue *q, F f, B0 b0, B1 b1, B2 b2) { + new (this) Event(q, EventQueue:: + context33(f, b0, b1, b2)); + } + + template + Event(EventQueue *q, F f, B0 b0, B1 b1, B2 b2, B3 b3) { + new (this) Event(q, EventQueue:: + context43(f, b0, b1, b2, b3)); + } + + template + Event(EventQueue *q, F f, B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) { + new (this) Event(q, EventQueue:: + context53(f, b0, b1, b2, b3, b4)); + } + + Event(const Event &e) { + _event = 0; + if (e._event) { + _event = e._event; + _event->ref += 1; + } + } + + Event &operator=(const Event &that) { + if (this != &that) { + this->~Event(); + new (this) Event(that); + } + + return *this; + } + + ~Event() { + if (_event) { + _event->ref -= 1; + if (_event->ref == 0) { + _event->dtor(_event); + equeue_dealloc(_event->equeue, _event); + } + } + } + + /** Configure the delay of an event + * + * @param delay Millisecond delay before dispatching the event + */ + void delay(int delay) { + if (_event) { + _event->delay = delay; + } + } + + /** Configure the period of an event + * + * @param period Millisecond period for repeatedly dispatching an event + */ + void period(int period) { + if (_event) { + _event->period = period; + } + } + + /** Posts an event onto the underlying event queue + * + * The event is posted to the underlying queue and is executed in the + * context of the event queue's dispatch loop. + * + * The post function is irq safe and can act as a mechanism for moving + * events out of irq contexts. + * + * @param a0..a4 Arguments to pass to the event + * @return A unique id that represents the posted event and can + * be passed to EventQueue::cancel, or an id of 0 if + * there is not enough memory to allocate the event. + */ + int post(A0 a0, A1 a1, A2 a2) { + if (!_event) { + return 0; + } + + _event->id = _event->post(_event, a0, a1, a2); + return _event->id; + } + + /** Cancels the most recently posted event + * + * Attempts to cancel the most recently posted event. It is safe to call + * cancel after an event has already been dispatched. + * + * The cancel function is irq safe. + * + * If called while the event queue's dispatch loop is active, the cancel + * function does not garuntee that the event will not execute after it + * returns, as the event may have already begun executing. + */ + void cancel() { + if (_event) { + equeue_cancel(_event->equeue, _event->id); + } + } + +private: + struct event { + unsigned ref; + equeue_t *equeue; + int id; + + int delay; + int period; + + int (*post)(struct event *, A0 a0, A1 a1, A2 a2); + void (*dtor)(struct event *); + + // F follows + } *_event; +}; + +/** Event + * + * Representation of an event for fine-grain dispatch control + */ +template +class Event { +public: + /** Event lifetime + * + * Constructs an event bound to the specified event queue. The specified + * callback acts as the target for the event and is executed in the + * context of the event queue's dispatch loop once posted. + * + * @param f Function to execute when the event is dispatched + * @param a0..a4 Arguments to pass to the callback + */ + template + Event(EventQueue *q, F f) { + struct local { + static int post(struct event *e, A0 a0, A1 a1, A2 a2, A3 a3) { + typedef EventQueue::context40 C; + struct local { + static void call(void *p) { (*static_cast(p))(); } + static void dtor(void *p) { static_cast(p)->~C(); } + }; + + void *p = equeue_alloc(e->equeue, sizeof(C)); + if (!p) { + return 0; + } + + new (p) C(*reinterpret_cast(e+1), a0, a1, a2, a3); + equeue_event_delay(p, e->delay); + equeue_event_period(p, e->period); + equeue_event_dtor(p, &local::dtor); + return equeue_post(e->equeue, &local::call, p); + } + + static void dtor(struct event *e) { + reinterpret_cast(e+1)->~F(); + } + }; + + _event = static_cast( + equeue_alloc(&q->_equeue, sizeof(struct event) + sizeof(F))); + if (_event) { + _event->equeue = &q->_equeue; + _event->id = 0; + _event->delay = 0; + _event->period = -1; + + _event->post = &local::post; + _event->dtor = &local::dtor; + + new (_event+1) F(f); + + _event->ref = 1; + } + } + + template + Event(EventQueue *q, F f, B0 b0) { + new (this) Event(q, EventQueue:: + context14(f, b0)); + } + + template + Event(EventQueue *q, F f, B0 b0, B1 b1) { + new (this) Event(q, EventQueue:: + context24(f, b0, b1)); + } + + template + Event(EventQueue *q, F f, B0 b0, B1 b1, B2 b2) { + new (this) Event(q, EventQueue:: + context34(f, b0, b1, b2)); + } + + template + Event(EventQueue *q, F f, B0 b0, B1 b1, B2 b2, B3 b3) { + new (this) Event(q, EventQueue:: + context44(f, b0, b1, b2, b3)); + } + + template + Event(EventQueue *q, F f, B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) { + new (this) Event(q, EventQueue:: + context54(f, b0, b1, b2, b3, b4)); + } + + Event(const Event &e) { + _event = 0; + if (e._event) { + _event = e._event; + _event->ref += 1; + } + } + + Event &operator=(const Event &that) { + if (this != &that) { + this->~Event(); + new (this) Event(that); + } + + return *this; + } + + ~Event() { + if (_event) { + _event->ref -= 1; + if (_event->ref == 0) { + _event->dtor(_event); + equeue_dealloc(_event->equeue, _event); + } + } + } + + /** Configure the delay of an event + * + * @param delay Millisecond delay before dispatching the event + */ + void delay(int delay) { + if (_event) { + _event->delay = delay; + } + } + + /** Configure the period of an event + * + * @param period Millisecond period for repeatedly dispatching an event + */ + void period(int period) { + if (_event) { + _event->period = period; + } + } + + /** Posts an event onto the underlying event queue + * + * The event is posted to the underlying queue and is executed in the + * context of the event queue's dispatch loop. + * + * The post function is irq safe and can act as a mechanism for moving + * events out of irq contexts. + * + * @param a0..a4 Arguments to pass to the event + * @return A unique id that represents the posted event and can + * be passed to EventQueue::cancel, or an id of 0 if + * there is not enough memory to allocate the event. + */ + int post(A0 a0, A1 a1, A2 a2, A3 a3) { + if (!_event) { + return 0; + } + + _event->id = _event->post(_event, a0, a1, a2, a3); + return _event->id; + } + + /** Cancels the most recently posted event + * + * Attempts to cancel the most recently posted event. It is safe to call + * cancel after an event has already been dispatched. + * + * The cancel function is irq safe. + * + * If called while the event queue's dispatch loop is active, the cancel + * function does not garuntee that the event will not execute after it + * returns, as the event may have already begun executing. + */ + void cancel() { + if (_event) { + equeue_cancel(_event->equeue, _event->id); + } + } + +private: + struct event { + unsigned ref; + equeue_t *equeue; + int id; + + int delay; + int period; + + int (*post)(struct event *, A0 a0, A1 a1, A2 a2, A3 a3); + void (*dtor)(struct event *); + + // F follows + } *_event; +}; + +/** Event + * + * Representation of an event for fine-grain dispatch control + */ +template +class Event { +public: + /** Event lifetime + * + * Constructs an event bound to the specified event queue. The specified + * callback acts as the target for the event and is executed in the + * context of the event queue's dispatch loop once posted. + * + * @param f Function to execute when the event is dispatched + * @param a0..a4 Arguments to pass to the callback + */ + template + Event(EventQueue *q, F f) { + struct local { + static int post(struct event *e, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) { + typedef EventQueue::context50 C; + struct local { + static void call(void *p) { (*static_cast(p))(); } + static void dtor(void *p) { static_cast(p)->~C(); } + }; + + void *p = equeue_alloc(e->equeue, sizeof(C)); + if (!p) { + return 0; + } + + new (p) C(*reinterpret_cast(e+1), a0, a1, a2, a3, a4); + equeue_event_delay(p, e->delay); + equeue_event_period(p, e->period); + equeue_event_dtor(p, &local::dtor); + return equeue_post(e->equeue, &local::call, p); + } + + static void dtor(struct event *e) { + reinterpret_cast(e+1)->~F(); + } + }; + + _event = static_cast( + equeue_alloc(&q->_equeue, sizeof(struct event) + sizeof(F))); + if (_event) { + _event->equeue = &q->_equeue; + _event->id = 0; + _event->delay = 0; + _event->period = -1; + + _event->post = &local::post; + _event->dtor = &local::dtor; + + new (_event+1) F(f); + + _event->ref = 1; + } + } + + template + Event(EventQueue *q, F f, B0 b0) { + new (this) Event(q, EventQueue:: + context15(f, b0)); + } + + template + Event(EventQueue *q, F f, B0 b0, B1 b1) { + new (this) Event(q, EventQueue:: + context25(f, b0, b1)); + } + + template + Event(EventQueue *q, F f, B0 b0, B1 b1, B2 b2) { + new (this) Event(q, EventQueue:: + context35(f, b0, b1, b2)); + } + + template + Event(EventQueue *q, F f, B0 b0, B1 b1, B2 b2, B3 b3) { + new (this) Event(q, EventQueue:: + context45(f, b0, b1, b2, b3)); + } + + template + Event(EventQueue *q, F f, B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) { + new (this) Event(q, EventQueue:: + context55(f, b0, b1, b2, b3, b4)); + } + + Event(const Event &e) { + _event = 0; + if (e._event) { + _event = e._event; + _event->ref += 1; + } + } + + Event &operator=(const Event &that) { + if (this != &that) { + this->~Event(); + new (this) Event(that); + } + + return *this; + } + + ~Event() { + if (_event) { + _event->ref -= 1; + if (_event->ref == 0) { + _event->dtor(_event); + equeue_dealloc(_event->equeue, _event); + } + } + } + + /** Configure the delay of an event + * + * @param delay Millisecond delay before dispatching the event + */ + void delay(int delay) { + if (_event) { + _event->delay = delay; + } + } + + /** Configure the period of an event + * + * @param period Millisecond period for repeatedly dispatching an event + */ + void period(int period) { + if (_event) { + _event->period = period; + } + } + + /** Posts an event onto the underlying event queue + * + * The event is posted to the underlying queue and is executed in the + * context of the event queue's dispatch loop. + * + * The post function is irq safe and can act as a mechanism for moving + * events out of irq contexts. + * + * @param a0..a4 Arguments to pass to the event + * @return A unique id that represents the posted event and can + * be passed to EventQueue::cancel, or an id of 0 if + * there is not enough memory to allocate the event. + */ + int post(A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) { + if (!_event) { + return 0; + } + + _event->id = _event->post(_event, a0, a1, a2, a3, a4); + return _event->id; + } + + /** Cancels the most recently posted event + * + * Attempts to cancel the most recently posted event. It is safe to call + * cancel after an event has already been dispatched. + * + * The cancel function is irq safe. + * + * If called while the event queue's dispatch loop is active, the cancel + * function does not garuntee that the event will not execute after it + * returns, as the event may have already begun executing. + */ + void cancel() { + if (_event) { + equeue_cancel(_event->equeue, _event->id); + } + } + +private: + struct event { + unsigned ref; + equeue_t *equeue; + int id; + + int delay; + int period; + + int (*post)(struct event *, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4); + void (*dtor)(struct event *); + + // F follows + } *_event; +}; + + +} + +#endif \ No newline at end of file diff --git a/EventQueue.h b/EventQueue.h index 583702c..716dbae 100644 --- a/EventQueue.h +++ b/EventQueue.h @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #ifndef EVENT_QUEUE_H #define EVENT_QUEUE_H @@ -23,7 +24,6 @@ namespace events { - /** EVENTS_EVENT_SIZE * Minimum size of an event * This size fits a Callback at minimum @@ -157,39 +157,44 @@ class EventQueue { */ template int call(F f) { + struct local { + static void call(void *p) { (*static_cast(p))(); } + static void dtor(void *p) { static_cast(p)->~F(); } + }; + void *p = equeue_alloc(&_equeue, sizeof(F)); if (!p) { return 0; } F *e = new (p) F(f); - equeue_event_dtor(e, &EventQueue::dtor); - return equeue_post(&_equeue, &EventQueue::call, e); + equeue_event_dtor(e, &local::dtor); + return equeue_post(&_equeue, &local::call, e); } template int call(F f, A0 a0) { - return call(Context1(f,a0)); + return call(context10(f, a0)); } template int call(F f, A0 a0, A1 a1) { - return call(Context2(f,a0,a1)); + return call(context20(f, a0, a1)); } template int call(F f, A0 a0, A1 a1, A2 a2) { - return call(Context3(f,a0,a1,a2)); + return call(context30(f, a0, a1, a2)); } template int call(F f, A0 a0, A1 a1, A2 a2, A3 a3) { - return call(Context4(f,a0,a1,a2,a3)); + return call(context40(f, a0, a1, a2, a3)); } template int call(F f, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) { - return call(Context5(f,a0,a1,a2,a3,a4)); + return call(context50(f, a0, a1, a2, a3, a4)); } /** Post an event to the queue after a specified delay @@ -209,6 +214,11 @@ class EventQueue { */ template int call_in(int ms, F f) { + struct local { + static void call(void *p) { (*static_cast(p))(); } + static void dtor(void *p) { static_cast(p)->~F(); } + }; + void *p = equeue_alloc(&_equeue, sizeof(F)); if (!p) { return 0; @@ -216,33 +226,33 @@ class EventQueue { F *e = new (p) F(f); equeue_event_delay(e, ms); - equeue_event_dtor(e, &EventQueue::dtor); - return equeue_post(&_equeue, &EventQueue::call, e); + equeue_event_dtor(e, &local::dtor); + return equeue_post(&_equeue, &local::call, e); } template int call_in(int ms, F f, A0 a0) { - return call_in(ms, Context1(f,a0)); + return call_in(ms, context10(f, a0)); } template int call_in(int ms, F f, A0 a0, A1 a1) { - return call_in(ms, Context2(f,a0,a1)); + return call_in(ms, context20(f, a0, a1)); } template int call_in(int ms, F f, A0 a0, A1 a1, A2 a2) { - return call_in(ms, Context3(f,a0,a1,a2)); + return call_in(ms, context30(f, a0, a1, a2)); } template int call_in(int ms, F f, A0 a0, A1 a1, A2 a2, A3 a3) { - return call_in(ms, Context4(f,a0,a1,a2,a3)); + return call_in(ms, context40(f, a0, a1, a2, a3)); } template int call_in(int ms, F f, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) { - return call_in(ms, Context5(f,a0,a1,a2,a3,a4)); + return call_in(ms, context50(f, a0, a1, a2, a3, a4)); } /** Post an event to the queue periodically @@ -262,6 +272,11 @@ class EventQueue { */ template int call_every(int ms, F f) { + struct local { + static void call(void *p) { (*static_cast(p))(); } + static void dtor(void *p) { static_cast(p)->~F(); } + }; + void *p = equeue_alloc(&_equeue, sizeof(F)); if (!p) { return 0; @@ -270,56 +285,89 @@ class EventQueue { F *e = new (p) F(f); equeue_event_delay(e, ms); equeue_event_period(e, ms); - equeue_event_dtor(e, &EventQueue::dtor); - return equeue_post(&_equeue, &EventQueue::call, e); + equeue_event_dtor(e, &local::dtor); + return equeue_post(&_equeue, &local::call, e); } template int call_every(int ms, F f, A0 a0) { - return call_every(ms, Context1(f,a0)); + return call_every(ms, context10(f, a0)); } template int call_every(int ms, F f, A0 a0, A1 a1) { - return call_every(ms, Context2(f,a0,a1)); + return call_every(ms, context20(f, a0, a1)); } template int call_every(int ms, F f, A0 a0, A1 a1, A2 a2) { - return call_every(ms, Context3(f,a0,a1,a2)); + return call_every(ms, context30(f, a0, a1, a2)); } template int call_every(int ms, F f, A0 a0, A1 a1, A2 a2, A3 a3) { - return call_every(ms, Context4(f,a0,a1,a2,a3)); + return call_every(ms, context40(f, a0, a1, a2, a3)); } template int call_every(int ms, F f, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) { - return call_every(ms, Context5(f,a0,a1,a2,a3,a4)); + return call_every(ms, context50(f, a0, a1, a2, a3, a4)); } protected: + template + friend class Event; struct equeue _equeue; mbed::Callback _update; - template - struct Context5 { - F f; A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; + template + struct context00 { + F f; + context00(F f) + : f(f) {} - Context5(F f, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) - : f(f), a0(a0), a1(a1), a2(a2), a3(a3), a4(a4) {} + void operator()() { + f(); + } + }; + + template + struct context10 { + F f; A0 a0; + context10(F f, A0 a0) + : f(f), a0(a0) {} void operator()() { - f(a0, a1, a2, a3, a4); + f(a0); + } + }; + + template + struct context20 { + F f; A0 a0; A1 a1; + context20(F f, A0 a0, A1 a1) + : f(f), a0(a0), a1(a1) {} + + void operator()() { + f(a0, a1); + } + }; + + template + struct context30 { + F f; A0 a0; A1 a1; A2 a2; + context30(F f, A0 a0, A1 a1, A2 a2) + : f(f), a0(a0), a1(a1), a2(a2) {} + + void operator()() { + f(a0, a1, a2); } }; template - struct Context4 { + struct context40 { F f; A0 a0; A1 a1; A2 a2; A3 a3; - - Context4(F f, A0 a0, A1 a1, A2 a2, A3 a3) + context40(F f, A0 a0, A1 a1, A2 a2, A3 a3) : f(f), a0(a0), a1(a1), a2(a2), a3(a3) {} void operator()() { @@ -327,54 +375,349 @@ class EventQueue { } }; - template - struct Context3 { - F f; A0 a0; A1 a1; A2 a2; + template + struct context50 { + F f; A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; + context50(F f, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) + : f(f), a0(a0), a1(a1), a2(a2), a3(a3), a4(a4) {} - Context3(F f, A0 a0, A1 a1, A2 a2) + void operator()() { + f(a0, a1, a2, a3, a4); + } + }; + + template + struct context01 { + F f; + context01(F f) + : f(f) {} + + void operator()(B0 b0) { + f(b0); + } + }; + + template + struct context11 { + F f; A0 a0; + context11(F f, A0 a0) + : f(f), a0(a0) {} + + void operator()(B0 b0) { + f(a0, b0); + } + }; + + template + struct context21 { + F f; A0 a0; A1 a1; + context21(F f, A0 a0, A1 a1) + : f(f), a0(a0), a1(a1) {} + + void operator()(B0 b0) { + f(a0, a1, b0); + } + }; + + template + struct context31 { + F f; A0 a0; A1 a1; A2 a2; + context31(F f, A0 a0, A1 a1, A2 a2) : f(f), a0(a0), a1(a1), a2(a2) {} - void operator()() { - f(a0, a1, a2); + void operator()(B0 b0) { + f(a0, a1, a2, b0); } }; - template - struct Context2 { + template + struct context41 { + F f; A0 a0; A1 a1; A2 a2; A3 a3; + context41(F f, A0 a0, A1 a1, A2 a2, A3 a3) + : f(f), a0(a0), a1(a1), a2(a2), a3(a3) {} + + void operator()(B0 b0) { + f(a0, a1, a2, a3, b0); + } + }; + + template + struct context51 { + F f; A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; + context51(F f, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) + : f(f), a0(a0), a1(a1), a2(a2), a3(a3), a4(a4) {} + + void operator()(B0 b0) { + f(a0, a1, a2, a3, a4, b0); + } + }; + + template + struct context02 { + F f; + context02(F f) + : f(f) {} + + void operator()(B0 b0, B1 b1) { + f(b0, b1); + } + }; + + template + struct context12 { + F f; A0 a0; + context12(F f, A0 a0) + : f(f), a0(a0) {} + + void operator()(B0 b0, B1 b1) { + f(a0, b0, b1); + } + }; + + template + struct context22 { F f; A0 a0; A1 a1; + context22(F f, A0 a0, A1 a1) + : f(f), a0(a0), a1(a1) {} + + void operator()(B0 b0, B1 b1) { + f(a0, a1, b0, b1); + } + }; + + template + struct context32 { + F f; A0 a0; A1 a1; A2 a2; + context32(F f, A0 a0, A1 a1, A2 a2) + : f(f), a0(a0), a1(a1), a2(a2) {} + + void operator()(B0 b0, B1 b1) { + f(a0, a1, a2, b0, b1); + } + }; + + template + struct context42 { + F f; A0 a0; A1 a1; A2 a2; A3 a3; + context42(F f, A0 a0, A1 a1, A2 a2, A3 a3) + : f(f), a0(a0), a1(a1), a2(a2), a3(a3) {} + + void operator()(B0 b0, B1 b1) { + f(a0, a1, a2, a3, b0, b1); + } + }; + + template + struct context52 { + F f; A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; + context52(F f, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) + : f(f), a0(a0), a1(a1), a2(a2), a3(a3), a4(a4) {} + + void operator()(B0 b0, B1 b1) { + f(a0, a1, a2, a3, a4, b0, b1); + } + }; + + template + struct context03 { + F f; + context03(F f) + : f(f) {} + + void operator()(B0 b0, B1 b1, B2 b2) { + f(b0, b1, b2); + } + }; + + template + struct context13 { + F f; A0 a0; + context13(F f, A0 a0) + : f(f), a0(a0) {} - Context2(F f, A0 a0, A1 a1) + void operator()(B0 b0, B1 b1, B2 b2) { + f(a0, b0, b1, b2); + } + }; + + template + struct context23 { + F f; A0 a0; A1 a1; + context23(F f, A0 a0, A1 a1) : f(f), a0(a0), a1(a1) {} - void operator()() { - f(a0, a1); + void operator()(B0 b0, B1 b1, B2 b2) { + f(a0, a1, b0, b1, b2); } }; - template - struct Context1 { + template + struct context33 { + F f; A0 a0; A1 a1; A2 a2; + context33(F f, A0 a0, A1 a1, A2 a2) + : f(f), a0(a0), a1(a1), a2(a2) {} + + void operator()(B0 b0, B1 b1, B2 b2) { + f(a0, a1, a2, b0, b1, b2); + } + }; + + template + struct context43 { + F f; A0 a0; A1 a1; A2 a2; A3 a3; + context43(F f, A0 a0, A1 a1, A2 a2, A3 a3) + : f(f), a0(a0), a1(a1), a2(a2), a3(a3) {} + + void operator()(B0 b0, B1 b1, B2 b2) { + f(a0, a1, a2, a3, b0, b1, b2); + } + }; + + template + struct context53 { + F f; A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; + context53(F f, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) + : f(f), a0(a0), a1(a1), a2(a2), a3(a3), a4(a4) {} + + void operator()(B0 b0, B1 b1, B2 b2) { + f(a0, a1, a2, a3, a4, b0, b1, b2); + } + }; + + template + struct context04 { + F f; + context04(F f) + : f(f) {} + + void operator()(B0 b0, B1 b1, B2 b2, B3 b3) { + f(b0, b1, b2, b3); + } + }; + + template + struct context14 { F f; A0 a0; + context14(F f, A0 a0) + : f(f), a0(a0) {} - Context1(F f, A0 a0) + void operator()(B0 b0, B1 b1, B2 b2, B3 b3) { + f(a0, b0, b1, b2, b3); + } + }; + + template + struct context24 { + F f; A0 a0; A1 a1; + context24(F f, A0 a0, A1 a1) + : f(f), a0(a0), a1(a1) {} + + void operator()(B0 b0, B1 b1, B2 b2, B3 b3) { + f(a0, a1, b0, b1, b2, b3); + } + }; + + template + struct context34 { + F f; A0 a0; A1 a1; A2 a2; + context34(F f, A0 a0, A1 a1, A2 a2) + : f(f), a0(a0), a1(a1), a2(a2) {} + + void operator()(B0 b0, B1 b1, B2 b2, B3 b3) { + f(a0, a1, a2, b0, b1, b2, b3); + } + }; + + template + struct context44 { + F f; A0 a0; A1 a1; A2 a2; A3 a3; + context44(F f, A0 a0, A1 a1, A2 a2, A3 a3) + : f(f), a0(a0), a1(a1), a2(a2), a3(a3) {} + + void operator()(B0 b0, B1 b1, B2 b2, B3 b3) { + f(a0, a1, a2, a3, b0, b1, b2, b3); + } + }; + + template + struct context54 { + F f; A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; + context54(F f, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) + : f(f), a0(a0), a1(a1), a2(a2), a3(a3), a4(a4) {} + + void operator()(B0 b0, B1 b1, B2 b2, B3 b3) { + f(a0, a1, a2, a3, a4, b0, b1, b2, b3); + } + }; + + template + struct context05 { + F f; + context05(F f) + : f(f) {} + + void operator()(B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) { + f(b0, b1, b2, b3, b4); + } + }; + + template + struct context15 { + F f; A0 a0; + context15(F f, A0 a0) : f(f), a0(a0) {} - void operator()() { - f(a0); + void operator()(B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) { + f(a0, b0, b1, b2, b3, b4); } }; - template - static void call(void *p) { - (*static_cast(p))(); - } + template + struct context25 { + F f; A0 a0; A1 a1; + context25(F f, A0 a0, A1 a1) + : f(f), a0(a0), a1(a1) {} - template - static void dtor(void *p) { - static_cast(p)->~T(); - } + void operator()(B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) { + f(a0, a1, b0, b1, b2, b3, b4); + } + }; + + template + struct context35 { + F f; A0 a0; A1 a1; A2 a2; + context35(F f, A0 a0, A1 a1, A2 a2) + : f(f), a0(a0), a1(a1), a2(a2) {} + + void operator()(B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) { + f(a0, a1, a2, b0, b1, b2, b3, b4); + } + }; + + template + struct context45 { + F f; A0 a0; A1 a1; A2 a2; A3 a3; + context45(F f, A0 a0, A1 a1, A2 a2, A3 a3) + : f(f), a0(a0), a1(a1), a2(a2), a3(a3) {} + + void operator()(B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) { + f(a0, a1, a2, a3, b0, b1, b2, b3, b4); + } + }; + + template + struct context55 { + F f; A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; + context55(F f, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) + : f(f), a0(a0), a1(a1), a2(a2), a3(a3), a4(a4) {} + + void operator()(B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) { + f(a0, a1, a2, a3, a4, b0, b1, b2, b3, b4); + } + }; }; } -#endif +#endif \ No newline at end of file diff --git a/README.md b/README.md index 7d6a90c..48293b1 100644 --- a/README.md +++ b/README.md @@ -92,8 +92,40 @@ if (id) { queue.cancel(id); ``` -Event queuest easily align with module boundaries, where internal state can -be implicitely synchronized through event dispatch. Multiple modules can +For a more fine-grain control of event dispatch, the `Event` class can be +manually instantiated and configured. An `Event` represents an event as +a C++ style function object and can be directly passed to other APIs that +expect a callback. + +``` cpp +// Creates an event bound to the specified event queue +EventQueue queue; +Event<> event(&queue, doit); + +// The event can be manually configured for special timing requirements +// specified in milliseconds +event.delay(10); +event.period(10000); + +// Posted events are dispatched in the context of the queue's +// dispatch function +queue.dispatch(); + +// Events can also pass arguments to the underlying callback when both +// initially constructed and posted. +Event event(&queue, printf, "recieved %d and %d\n"); + +// Events can be posted multiple times and enqueue gracefully until +// the dispatch function is called. +event.post(1, 2); +event.post(3, 4); +event.post(5, 6); + +queue.dispatch(); +``` + +Event queues easily align with module boundaries, where internal state can +be implicitly synchronized through event dispatch. Multiple modules can use independent event queues, but still be composed through the `EventQueue::chain` function. @@ -118,3 +150,4 @@ b.chain(&a); a.dispatch(); ``` + diff --git a/TESTS/events/queue/main.cpp b/TESTS/events/queue/main.cpp index c0e9474..c2ba3a3 100644 --- a/TESTS/events/queue/main.cpp +++ b/TESTS/events/queue/main.cpp @@ -143,6 +143,36 @@ void cancel_test1() { } +// Testing the dynamic arguments to the event class +unsigned counter = 0; + +void count5(unsigned a0, unsigned a1, unsigned a2, unsigned a3, unsigned a5) { + counter += a0 + a1 + a2 + a3 + a5; +} + +void event_class_test() { + EventQueue queue(2048); + + Event e5(&queue, count5); + Event e4(&queue, count5, 1); + Event e3(&queue, count5, 1, 1); + Event e2(&queue, count5, 1, 1, 1); + Event e1(&queue, count5, 1, 1, 1, 1); + Event<> e0(&queue, count5, 1, 1, 1, 1, 1); + + e5.post(1, 1, 1, 1, 1); + e4.post(1, 1, 1, 1); + e3.post(1, 1, 1); + e2.post(1, 1); + e1.post(1); + e0.post(); + + queue.dispatch(0); + + TEST_ASSERT_EQUAL(counter, 30); +} + + // Test setup utest::v1::status_t test_setup(const size_t number_of_cases) { GREENTEA_SETUP(20, "default_auto"); @@ -164,6 +194,7 @@ const Case cases[] = { Case("Testing allocate failure 2", allocate_failure_test2), Case("Testing event cancel 1", cancel_test1<20>), + Case("Testing the event class", event_class_test), }; Specification specification(test_setup, cases); diff --git a/mbed_events.h b/mbed_events.h index 5cde27b..257be47 100644 --- a/mbed_events.h +++ b/mbed_events.h @@ -23,6 +23,7 @@ #ifdef __cplusplus #include "EventQueue.h" +#include "Event.h" using namespace events;