mirror of https://github.com/ARMmbed/mbed-os.git
Merge pull request #11342 from maciejbocianski/event_queue_static_alloc
add queue static allocation supportpull/11394/head
commit
bdd6cb8dee
|
@ -39,7 +39,10 @@ static void pass_func(void *eh)
|
|||
|
||||
static void simple_func(void *p)
|
||||
{
|
||||
(*(reinterpret_cast<uint8_t *>(p)))++;
|
||||
uint8_t *d = reinterpret_cast<uint8_t *>(p);
|
||||
if (*d < 255) {
|
||||
(*d)++;
|
||||
}
|
||||
}
|
||||
|
||||
static void sloth_func(void *p)
|
||||
|
@ -977,6 +980,65 @@ static void test_equeue_sibling()
|
|||
equeue_destroy(&q);
|
||||
}
|
||||
|
||||
struct user_allocated_event {
|
||||
struct equeue_event e;
|
||||
uint8_t touched;
|
||||
};
|
||||
|
||||
/** Test that equeue executes user allocated events passed by equeue_post.
|
||||
*
|
||||
* Given queue is initialized and its size is set to store one event at max in its internal memory.
|
||||
* When post events allocated in queues internal memory (what is done by calling equeue_call).
|
||||
* Then only one event can be posted due to queue memory size.
|
||||
* When post user allocated events.
|
||||
* Then number of posted events is not limited by queue memory size.
|
||||
* When both queue allocaded and user allocated events are posted and equeue_dispatch is called.
|
||||
* Then both types of events are executed properly.
|
||||
*/
|
||||
static void test_equeue_user_allocated_event_post()
|
||||
{
|
||||
equeue_t q;
|
||||
int err = equeue_create(&q, EQUEUE_EVENT_SIZE);
|
||||
TEST_ASSERT_EQUAL_INT(0, err);
|
||||
|
||||
uint8_t touched = 0;
|
||||
user_allocated_event e1 = { { 0, 0, 0, NULL, NULL, NULL, 0, -1, NULL, NULL }, 0 };
|
||||
user_allocated_event e2 = { { 0, 0, 0, NULL, NULL, NULL, 1, -1, NULL, NULL }, 0 };
|
||||
user_allocated_event e3 = { { 0, 0, 0, NULL, NULL, NULL, 1, -1, NULL, NULL }, 0 };
|
||||
user_allocated_event e4 = { { 0, 0, 0, NULL, NULL, NULL, 1, -1, NULL, NULL }, 0 };
|
||||
user_allocated_event e5 = { { 0, 0, 0, NULL, NULL, NULL, 0, -1, NULL, NULL }, 0 };
|
||||
|
||||
TEST_ASSERT_NOT_EQUAL(0, equeue_call(&q, simple_func, &touched));
|
||||
TEST_ASSERT_EQUAL_INT(0, equeue_call(&q, simple_func, &touched));
|
||||
TEST_ASSERT_EQUAL_INT(0, equeue_call(&q, simple_func, &touched));
|
||||
|
||||
equeue_post_user_allocated(&q, simple_func, &e1.e);
|
||||
equeue_post_user_allocated(&q, simple_func, &e2.e);
|
||||
equeue_post_user_allocated(&q, simple_func, &e3.e);
|
||||
equeue_post_user_allocated(&q, simple_func, &e4.e);
|
||||
equeue_post_user_allocated(&q, simple_func, &e5.e);
|
||||
equeue_cancel_user_allocated(&q, &e3.e);
|
||||
|
||||
equeue_dispatch(&q, 1);
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT8(1, touched);
|
||||
TEST_ASSERT_EQUAL_UINT8(1, e1.touched);
|
||||
TEST_ASSERT_EQUAL_UINT8(1, e2.touched);
|
||||
TEST_ASSERT_EQUAL_UINT8(0, e3.touched);
|
||||
TEST_ASSERT_EQUAL_UINT8(1, e4.touched);
|
||||
TEST_ASSERT_EQUAL_UINT8(1, e5.touched);
|
||||
|
||||
equeue_dispatch(&q, 10);
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT8(1, touched);
|
||||
TEST_ASSERT_EQUAL_UINT8(1, e1.touched);
|
||||
TEST_ASSERT_EQUAL_UINT8(1, e2.touched);
|
||||
TEST_ASSERT_EQUAL_UINT8(0, e3.touched);
|
||||
TEST_ASSERT_EQUAL_UINT8(1, e4.touched);
|
||||
TEST_ASSERT_EQUAL_UINT8(1, e5.touched);
|
||||
|
||||
equeue_destroy(&q);
|
||||
}
|
||||
|
||||
Case cases[] = {
|
||||
Case("simple call test", test_equeue_simple_call),
|
||||
|
@ -1006,7 +1068,8 @@ Case cases[] = {
|
|||
Case("fragmenting barrage test", test_equeue_fragmenting_barrage<10>),
|
||||
Case("multithreaded barrage test", test_equeue_multithreaded_barrage<10>),
|
||||
Case("break request cleared on timeout test", test_equeue_break_request_cleared_on_timeout),
|
||||
Case("sibling test", test_equeue_sibling)
|
||||
Case("sibling test", test_equeue_sibling),
|
||||
Case("user allocated event test", test_equeue_user_allocated_event_post)
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
* Copyright (c) 2017-2019 ARM Limited
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -322,6 +322,145 @@ void time_left_test()
|
|||
TEST_ASSERT_EQUAL(-1, queue.time_left(0));
|
||||
}
|
||||
|
||||
void f5(int a1, int a2, int a3, int a4, int a5)
|
||||
{
|
||||
touched = true;
|
||||
}
|
||||
|
||||
class EventTest {
|
||||
public:
|
||||
EventTest() : counter() {}
|
||||
void f0()
|
||||
{
|
||||
counter++;
|
||||
}
|
||||
void f1(int a)
|
||||
{
|
||||
counter += a;
|
||||
}
|
||||
void f5(int a, int b, int c, int d, int e)
|
||||
{
|
||||
counter += a + b + c + d + e;
|
||||
}
|
||||
uint32_t counter;
|
||||
};
|
||||
|
||||
/** Test that queue executes both dynamic and user allocated events.
|
||||
*
|
||||
* Given queue is initialized and its size is set to store three Event at max in its internal memory.
|
||||
* When post queue allocated event.
|
||||
* Then only three event can be posted due to queue memory size.
|
||||
* When post user allocated evens.
|
||||
* Then number of posted events is not limited by queue memory size.
|
||||
* When both Event and UserAllocatedEvent are posted and queue dispatch is called.
|
||||
* Then both types of events are executed properly.
|
||||
*
|
||||
*/
|
||||
void mixed_dynamic_static_events_queue_test()
|
||||
{
|
||||
{
|
||||
EventQueue queue(9 * EVENTS_EVENT_SIZE);
|
||||
|
||||
EventTest e1_test;
|
||||
Event<void()> e1 = queue.event(&e1_test, &EventTest::f0);
|
||||
int id1 = e1.post();
|
||||
TEST_ASSERT_NOT_EQUAL(0, id1);
|
||||
EventTest e2_test;
|
||||
Event<void()> e2 = queue.event(&e2_test, &EventTest::f1, 3);
|
||||
int id2 = e2.post();
|
||||
TEST_ASSERT_NOT_EQUAL(0, id2);
|
||||
EventTest e3_test;
|
||||
Event<void()> e3 = queue.event(&e3_test, &EventTest::f5, 1, 2, 3, 4, 5);
|
||||
int id3 = e3.post();
|
||||
TEST_ASSERT_NOT_EQUAL(0, id3);
|
||||
|
||||
|
||||
auto ue0 = make_user_allocated_event(func0);
|
||||
EventTest ue1_test;
|
||||
auto ue1 = make_user_allocated_event(&ue1_test, &EventTest::f0);
|
||||
EventTest ue2_test;
|
||||
auto ue2 = make_user_allocated_event(&ue2_test, &EventTest::f1, 3);
|
||||
EventTest ue3_test;
|
||||
auto ue3 = make_user_allocated_event(&ue3_test, &EventTest::f5, 1, 2, 3, 4, 5);
|
||||
EventTest ue4_test;
|
||||
auto ue4 = make_user_allocated_event(&ue4_test, &EventTest::f5, 1, 2, 3, 4, 5);
|
||||
|
||||
touched = false;
|
||||
|
||||
ue0.call_on(&queue);
|
||||
TEST_ASSERT_EQUAL(false, ue0.try_call());
|
||||
ue1.call_on(&queue);
|
||||
TEST_ASSERT_EQUAL(false, ue1.try_call());
|
||||
ue2.call_on(&queue);
|
||||
TEST_ASSERT_EQUAL(false, ue2.try_call());
|
||||
ue3.call_on(&queue);
|
||||
TEST_ASSERT_EQUAL(false, ue3.try_call());
|
||||
ue4.call_on(&queue);
|
||||
ue4.cancel();
|
||||
TEST_ASSERT_EQUAL(true, ue4.try_call());
|
||||
ue4.cancel();
|
||||
e2.cancel();
|
||||
|
||||
queue.dispatch(1);
|
||||
|
||||
TEST_ASSERT_EQUAL(true, touched);
|
||||
TEST_ASSERT_EQUAL(1, ue1_test.counter);
|
||||
TEST_ASSERT_EQUAL(3, ue2_test.counter);
|
||||
TEST_ASSERT_EQUAL(15, ue3_test.counter);
|
||||
TEST_ASSERT_EQUAL(0, ue4_test.counter);
|
||||
TEST_ASSERT_EQUAL(1, e1_test.counter);
|
||||
TEST_ASSERT_EQUAL(0, e2_test.counter);
|
||||
TEST_ASSERT_EQUAL(15, e3_test.counter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static EventQueue g_queue(0);
|
||||
|
||||
/** Test that static queue executes user allocated events.
|
||||
*
|
||||
* Given static queue is initialized
|
||||
* When post user allocated evens.
|
||||
* Then UserAllocatedEvent are posted and dispatched without any error.
|
||||
*/
|
||||
void static_events_queue_test()
|
||||
{
|
||||
// check that no dynamic event can be posted
|
||||
Event<void()> e0 = g_queue.event(func0);
|
||||
TEST_ASSERT_EQUAL(0, e0.post());
|
||||
|
||||
auto ue0 = g_queue.make_user_allocated_event(func0);
|
||||
EventTest test1;
|
||||
auto ue1 = make_user_allocated_event(&test1, &EventTest::f0);
|
||||
EventTest test2;
|
||||
auto ue2 = g_queue.make_user_allocated_event(&test2, &EventTest::f1, 3);
|
||||
EventTest test3;
|
||||
auto ue3 = make_user_allocated_event(&test3, &EventTest::f5, 1, 2, 3, 4, 5);
|
||||
EventTest test4;
|
||||
auto ue4 = g_queue.make_user_allocated_event(&test4, &EventTest::f5, 1, 2, 3, 4, 5);
|
||||
|
||||
ue0.call();
|
||||
TEST_ASSERT_EQUAL(false, ue0.try_call());
|
||||
ue1.call_on(&g_queue);
|
||||
TEST_ASSERT_EQUAL(false, ue1.try_call());
|
||||
ue2();
|
||||
TEST_ASSERT_EQUAL(false, ue2.try_call());
|
||||
ue3.call_on(&g_queue);
|
||||
TEST_ASSERT_EQUAL(false, ue3.try_call());
|
||||
ue4.call();
|
||||
ue4.cancel();
|
||||
TEST_ASSERT_EQUAL(true, ue4.try_call());
|
||||
g_queue.cancel(&ue4);
|
||||
|
||||
g_queue.dispatch(1);
|
||||
|
||||
TEST_ASSERT_EQUAL(1, test1.counter);
|
||||
TEST_ASSERT_EQUAL(3, test2.counter);
|
||||
TEST_ASSERT_EQUAL(15, test3.counter);
|
||||
TEST_ASSERT_EQUAL(0, test4.counter);
|
||||
|
||||
}
|
||||
|
||||
// Test setup
|
||||
utest::v1::status_t test_setup(const size_t number_of_cases)
|
||||
{
|
||||
|
@ -348,6 +487,9 @@ const Case cases[] = {
|
|||
Case("Testing the event inference", event_inference_test),
|
||||
|
||||
Case("Testing time_left", time_left_test),
|
||||
Case("Testing mixed dynamic & static events queue", mixed_dynamic_static_events_queue_test),
|
||||
Case("Testing static events queue", static_events_queue_test)
|
||||
|
||||
};
|
||||
|
||||
Specification specification(test_setup, cases);
|
||||
|
|
|
@ -45,7 +45,10 @@ static void pass_func(void *eh)
|
|||
|
||||
static void simple_func(void *p)
|
||||
{
|
||||
(*(reinterpret_cast<uint8_t *>(p)))++;
|
||||
uint8_t *d = reinterpret_cast<uint8_t *>(p);
|
||||
if (*d < 255) {
|
||||
(*d)++;
|
||||
}
|
||||
}
|
||||
|
||||
static void sloth_func(void *p)
|
||||
|
@ -994,4 +997,63 @@ TEST_F(TestEqueue, test_equeue_sibling)
|
|||
equeue_cancel(&q, id1);
|
||||
equeue_cancel(&q, id2);
|
||||
equeue_destroy(&q);
|
||||
}
|
||||
}
|
||||
|
||||
/** Test that equeue executes user allocated events passed by equeue_post.
|
||||
*
|
||||
* Given queue is initialized and its size is set to store one event at max in its internal memory.
|
||||
* When post events allocated in queues internal memory (what is done by calling equeue_call).
|
||||
* Then only one event can be posted due to queue memory size.
|
||||
* When post user allocated events.
|
||||
* Then number of posted events is not limited by queue memory size.
|
||||
* When both queue allocaded and user allocated events are posted and equeue_dispatch is called.
|
||||
* Then both types of events are executed properly.
|
||||
*/
|
||||
TEST_F(TestEqueue, test_equeue_user_allocated_event_post)
|
||||
{
|
||||
struct user_allocated_event {
|
||||
struct equeue_event e;
|
||||
uint8_t touched;
|
||||
};
|
||||
equeue_t q;
|
||||
int err = equeue_create(&q, EQUEUE_EVENT_SIZE);
|
||||
ASSERT_EQ(0, err);
|
||||
|
||||
uint8_t touched = 0;
|
||||
user_allocated_event e1 = { { 0, 0, 0, NULL, NULL, NULL, 0, -1, NULL, NULL }, 0 };
|
||||
user_allocated_event e2 = { { 0, 0, 0, NULL, NULL, NULL, 1, -1, NULL, NULL }, 0 };
|
||||
user_allocated_event e3 = { { 0, 0, 0, NULL, NULL, NULL, 1, -1, NULL, NULL }, 0 };
|
||||
user_allocated_event e4 = { { 0, 0, 0, NULL, NULL, NULL, 1, -1, NULL, NULL }, 0 };
|
||||
user_allocated_event e5 = { { 0, 0, 0, NULL, NULL, NULL, 0, -1, NULL, NULL }, 0 };
|
||||
|
||||
EXPECT_NE(0, equeue_call(&q, simple_func, &touched));
|
||||
EXPECT_EQ(0, equeue_call(&q, simple_func, &touched));
|
||||
EXPECT_EQ(0, equeue_call(&q, simple_func, &touched));
|
||||
|
||||
equeue_post_user_allocated(&q, simple_func, &e1.e);
|
||||
equeue_post_user_allocated(&q, simple_func, &e2.e);
|
||||
equeue_post_user_allocated(&q, simple_func, &e3.e);
|
||||
equeue_post_user_allocated(&q, simple_func, &e4.e);
|
||||
equeue_post_user_allocated(&q, simple_func, &e5.e);
|
||||
equeue_cancel_user_allocated(&q, &e3.e);
|
||||
|
||||
equeue_dispatch(&q, 1);
|
||||
|
||||
EXPECT_EQ(1, touched);
|
||||
EXPECT_EQ(1, e1.touched);
|
||||
EXPECT_EQ(1, e2.touched);
|
||||
EXPECT_EQ(0, e3.touched);
|
||||
EXPECT_EQ(1, e4.touched);
|
||||
EXPECT_EQ(1, e5.touched);
|
||||
|
||||
equeue_dispatch(&q, 10);
|
||||
|
||||
EXPECT_EQ(1, touched);
|
||||
EXPECT_EQ(1, e1.touched);
|
||||
EXPECT_EQ(1, e2.touched);
|
||||
EXPECT_EQ(0, e3.touched);
|
||||
EXPECT_EQ(1, e4.touched);
|
||||
EXPECT_EQ(1, e5.touched);
|
||||
|
||||
equeue_destroy(&q);
|
||||
}
|
||||
|
|
|
@ -40,7 +40,10 @@ unsigned equeue_tick(void)
|
|||
// Mutex operations
|
||||
int equeue_mutex_create(equeue_mutex_t *m)
|
||||
{
|
||||
return pthread_mutex_init(m, 0);
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
return pthread_mutex_init(m, &attr);
|
||||
}
|
||||
|
||||
void equeue_mutex_destroy(equeue_mutex_t *m)
|
||||
|
|
|
@ -45,6 +45,8 @@ namespace events {
|
|||
// Predeclared classes
|
||||
template <typename F>
|
||||
class Event;
|
||||
template <typename F, typename A>
|
||||
class UserAllocatedEvent;
|
||||
|
||||
/**
|
||||
* \defgroup events_EventQueue EventQueue class
|
||||
|
@ -60,10 +62,17 @@ public:
|
|||
/** Create an EventQueue
|
||||
*
|
||||
* Create an event queue. The event queue either allocates a buffer of
|
||||
* the specified size with malloc or uses the user provided buffer.
|
||||
* the specified size with malloc or uses the user provided buffer or
|
||||
* uses 1B dummy buffer if 0 size passed.
|
||||
*
|
||||
* 0 size queue is a special purpose queue to dispatch static events
|
||||
* only (see UserAllocatedEvent). Such a queue gives the guarantee
|
||||
* that no dynamic memory allocation will take place while queue
|
||||
* creation and events posting & dispatching.
|
||||
*
|
||||
* @param size Size of buffer to use for events in bytes
|
||||
* (default to EVENTS_QUEUE_SIZE)
|
||||
* If 0 provided then 1B dummy buffer is used
|
||||
* @param buffer Pointer to buffer to use for events
|
||||
* (default to NULL)
|
||||
*/
|
||||
|
@ -139,6 +148,34 @@ public:
|
|||
*/
|
||||
bool cancel(int id);
|
||||
|
||||
/** Cancel an in-flight user allocated event
|
||||
*
|
||||
* Attempts to cancel an UserAllocatedEvent referenced by its address
|
||||
* It is safe to call cancel after an event has already been dispatched.
|
||||
*
|
||||
* Event must be valid i.e. event must have not finished executing
|
||||
* and must have been bound to this queue.
|
||||
*
|
||||
* The cancel function is IRQ safe.
|
||||
*
|
||||
* If called while the event queue's dispatch loop is active in another thread,
|
||||
* the cancel function does not guarantee that the event will not execute after it
|
||||
* returns, as the event may have already begun executing. A call made from
|
||||
* the same thread as the dispatch loop will always succeed with a valid id.
|
||||
*
|
||||
* @param event Address of the event
|
||||
* @return true if event was successfully cancelled
|
||||
* false if event was not cancelled (invalid queue or executing already begun)
|
||||
*/
|
||||
template<typename... Args>
|
||||
bool cancel(UserAllocatedEvent<Args...> *event)
|
||||
{
|
||||
if (event->_equeue != &_equeue) {
|
||||
return false;
|
||||
}
|
||||
return equeue_cancel_user_allocated(&_equeue, event);
|
||||
}
|
||||
|
||||
/** Query how much time is left for delayed event
|
||||
*
|
||||
* If the event is delayed, this function can be used to query how much time
|
||||
|
@ -158,6 +195,33 @@ public:
|
|||
*/
|
||||
int time_left(int id);
|
||||
|
||||
/** Query how much time is left for delayed UserAllocatedEvent
|
||||
*
|
||||
* If the event is delayed, this function can be used to query how much time
|
||||
* is left until the event is due to be dispatched.
|
||||
*
|
||||
* Event must be valid i.e. event must have not finished executing
|
||||
* and must have been bound to this queue.
|
||||
*
|
||||
* This function is IRQ safe.
|
||||
*
|
||||
* @param event Address of the event
|
||||
*
|
||||
* @return Remaining time in milliseconds or
|
||||
* 0 if event is already due to be dispatched or
|
||||
* is currently executing.
|
||||
* Undefined if id is invalid.
|
||||
*
|
||||
*/
|
||||
template<typename... Args>
|
||||
int time_left(UserAllocatedEvent<Args...> *event)
|
||||
{
|
||||
if (event && event->_equeue != &_equeue) {
|
||||
return -1;
|
||||
}
|
||||
return equeue_timeleft_user_allocated(&_equeue, &event->_e);
|
||||
}
|
||||
|
||||
/** Background an event queue onto a single-shot timer-interrupt
|
||||
*
|
||||
* When updated, the event queue will call the provided update function
|
||||
|
@ -597,6 +661,53 @@ public:
|
|||
template <typename R, typename ...BoundArgs, typename ...ContextArgs, typename ...Args>
|
||||
Event<void(Args...)> event(mbed::Callback<R(BoundArgs..., Args...)> cb, ContextArgs ...context_args);
|
||||
|
||||
/** Creates an user allocated event bound to the event queue
|
||||
*
|
||||
* Constructs an user allocated 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.
|
||||
*
|
||||
* @code
|
||||
* #include "mbed.h"
|
||||
*
|
||||
* void handler(int data) { ... }
|
||||
*
|
||||
* class Device {
|
||||
* public:
|
||||
* void handler(int data) { ... }
|
||||
* };
|
||||
*
|
||||
* Device dev;
|
||||
*
|
||||
* // queue with not internal storage for dynamic events
|
||||
* // accepts only user allocated events
|
||||
* static EventQueue queue(0);
|
||||
* // Create events
|
||||
* static auto e1 = make_user_allocated_event(&dev, Device::handler, 2);
|
||||
* static auto e2 = queue.make_user_allocated_event(handler, 3);
|
||||
*
|
||||
* int main()
|
||||
* {
|
||||
* e1.call_on(&queue);
|
||||
* e2.call();
|
||||
*
|
||||
* queue.dispatch(1);
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @param f Function to execute when the event is dispatched
|
||||
* @return Event that will dispatch on the specific queue
|
||||
*/
|
||||
template <typename F, typename... ArgTs>
|
||||
UserAllocatedEvent<F, void(ArgTs...)> make_user_allocated_event(F f, ArgTs... args);
|
||||
|
||||
/** Creates an user allocated event bound to the event queue
|
||||
* @see EventQueue::make_user_allocated_event
|
||||
*/
|
||||
template <typename T, typename R, typename... ArgTs>
|
||||
UserAllocatedEvent<mbed::Callback<void(ArgTs...)>, void(ArgTs...)> make_user_allocated_event(T *obj, R(T::*method)(ArgTs... args), ArgTs... args);
|
||||
|
||||
|
||||
#else
|
||||
|
||||
/** Calls an event on the queue
|
||||
|
@ -1068,12 +1179,50 @@ public:
|
|||
*/
|
||||
template <typename R, typename B0, typename B1, typename B2, typename B3, typename B4, typename C0, typename C1, typename C2, typename C3, typename C4, typename... ArgTs>
|
||||
Event<void(ArgTs...)> event(mbed::Callback<R(B0, B1, B2, B3, B4, ArgTs...)> cb, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4);
|
||||
|
||||
/** Creates an user allocated event bound to the event queue
|
||||
*
|
||||
* Constructs an user allocated 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
|
||||
* @return Event that will dispatch on the specific queue
|
||||
*/
|
||||
template <typename F, typename... ArgTs>
|
||||
UserAllocatedEvent<F, void(ArgTs...)> make_user_allocated_event(F f, ArgTs... args);
|
||||
|
||||
/** Creates an user allocated event bound to the event queue
|
||||
* @see EventQueue::make_user_allocated_event
|
||||
*/
|
||||
template <typename T, typename R, typename... ArgTs>
|
||||
UserAllocatedEvent<mbed::Callback<void(ArgTs...)>, void(ArgTs...)> make_user_allocated_event(T *obj, R(T::*method)(ArgTs... args), ArgTs... args);
|
||||
|
||||
/** Creates an user allocated event bound to the event queue
|
||||
* @see EventQueue::make_user_allocated_event
|
||||
*/
|
||||
template <typename T, typename R, typename... ArgTs>
|
||||
UserAllocatedEvent<mbed::Callback<void(ArgTs...)>, void(ArgTs...)> make_user_allocated_event(const T *obj, R(T::*method)(ArgTs... args) const, ArgTs... args);
|
||||
|
||||
/** Creates an user allocated event bound to the event queue
|
||||
* @see EventQueue::make_user_allocated_event
|
||||
*/
|
||||
template <typename T, typename R, typename... ArgTs>
|
||||
UserAllocatedEvent<mbed::Callback<void(ArgTs...)>, void(ArgTs...)> make_user_allocated_event(volatile T *obj, R(T::*method)(ArgTs... args) volatile, ArgTs... args);
|
||||
|
||||
/** Creates an user allocated event bound to the event queue
|
||||
* @see EventQueue::make_user_allocated_event
|
||||
*/
|
||||
template <typename T, typename R, typename... ArgTs>
|
||||
UserAllocatedEvent<mbed::Callback<void(ArgTs...)>, void(ArgTs...)> make_user_allocated_event(const volatile T *obj, R(T::*method)(ArgTs... args) const volatile, ArgTs... args);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
#if !defined(DOXYGEN_ONLY)
|
||||
template <typename F>
|
||||
friend class Event;
|
||||
template <typename F, typename A>
|
||||
friend class UserAllocatedEvent;
|
||||
struct equeue _equeue;
|
||||
mbed::Callback<void(int)> _update;
|
||||
|
||||
|
@ -1098,7 +1247,7 @@ protected:
|
|||
struct context<F> {
|
||||
F f;
|
||||
|
||||
context(F f)
|
||||
constexpr context(F f)
|
||||
: f(f) {}
|
||||
|
||||
template <typename... ArgTs>
|
||||
|
@ -1113,7 +1262,7 @@ protected:
|
|||
F f;
|
||||
C0 c0;
|
||||
|
||||
context(F f, C0 c0)
|
||||
constexpr context(F f, C0 c0)
|
||||
: f(f), c0(c0) {}
|
||||
|
||||
template <typename... ArgTs>
|
||||
|
@ -1129,7 +1278,7 @@ protected:
|
|||
C0 c0;
|
||||
C1 c1;
|
||||
|
||||
context(F f, C0 c0, C1 c1)
|
||||
constexpr context(F f, C0 c0, C1 c1)
|
||||
: f(f), c0(c0), c1(c1) {}
|
||||
|
||||
template <typename... ArgTs>
|
||||
|
@ -1146,7 +1295,7 @@ protected:
|
|||
C1 c1;
|
||||
C2 c2;
|
||||
|
||||
context(F f, C0 c0, C1 c1, C2 c2)
|
||||
constexpr context(F f, C0 c0, C1 c1, C2 c2)
|
||||
: f(f), c0(c0), c1(c1), c2(c2) {}
|
||||
|
||||
template <typename... ArgTs>
|
||||
|
@ -1164,7 +1313,7 @@ protected:
|
|||
C2 c2;
|
||||
C3 c3;
|
||||
|
||||
context(F f, C0 c0, C1 c1, C2 c2, C3 c3)
|
||||
constexpr context(F f, C0 c0, C1 c1, C2 c2, C3 c3)
|
||||
: f(f), c0(c0), c1(c1), c2(c2), c3(c3) {}
|
||||
|
||||
template <typename... ArgTs>
|
||||
|
@ -1183,7 +1332,7 @@ protected:
|
|||
C3 c3;
|
||||
C4 c4;
|
||||
|
||||
context(F f, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4)
|
||||
constexpr context(F f, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4)
|
||||
: f(f), c0(c0), c1(c1), c2(c2), c3(c3), c4(c4) {}
|
||||
|
||||
template <typename... ArgTs>
|
||||
|
|
|
@ -0,0 +1,460 @@
|
|||
/*
|
||||
* Copyright (c) 2019 ARM Limited
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 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 USER_ALLOCATED_EVENT_H
|
||||
#define USER_ALLOCATED_EVENT_H
|
||||
|
||||
#include "events/EventQueue.h"
|
||||
#include "platform/mbed_assert.h"
|
||||
#include "platform/mbed_atomic.h"
|
||||
|
||||
namespace events {
|
||||
/**
|
||||
* \addtogroup events-public-api Events
|
||||
* \ingroup mbed-os-public
|
||||
* @{
|
||||
*/
|
||||
template <typename F, typename A>
|
||||
class UserAllocatedEvent;
|
||||
|
||||
/**
|
||||
* \defgroup events_Event UserAllocatedEvent class
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** UserAllocatedEvent
|
||||
*
|
||||
* Representation of an static event for fine-grain dispatch control.
|
||||
*
|
||||
* UserAllocatedEvent provides mechanism for event posting and dispatching
|
||||
* without utilization of queue internal memory. It embeds all underlying
|
||||
* event data and doesn't require any memory allocation while posting and dispatching.
|
||||
* All of these makes it cannot fail due to memory exhaustion while posting
|
||||
*
|
||||
* Usage:
|
||||
* @code
|
||||
* #include "mbed.h"
|
||||
*
|
||||
* void handler(int data) { ... }
|
||||
*
|
||||
* class Device {
|
||||
* public:
|
||||
* void handler(int data) { ... }
|
||||
* };
|
||||
*
|
||||
* Device dev;
|
||||
*
|
||||
* // queue with not internal storage for dynamic events
|
||||
* // accepts only user allocated events
|
||||
* static EventQueue queue(0);
|
||||
* // Create events
|
||||
* static auto e1 = make_user_allocated_event(&dev, Device::handler, 2);
|
||||
* static auto e2 = queue.make_user_allocated_event(handler, 3);
|
||||
*
|
||||
* int main()
|
||||
* {
|
||||
* e1.call_on(&queue);
|
||||
* e2.call();
|
||||
*
|
||||
* queue.dispatch(1);
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
template <typename F, typename... ArgTs>
|
||||
class UserAllocatedEvent<F, void(ArgTs...)> {
|
||||
public:
|
||||
typedef EventQueue::context<F, ArgTs...> C;
|
||||
|
||||
/** Create an event
|
||||
*
|
||||
* Constructs an event. 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 args Arguments to bind to the callback
|
||||
*/
|
||||
constexpr UserAllocatedEvent(F f, ArgTs... args) : _e(get_default_equeue_event()), _c(f, args...), _equeue(), _post_ref()
|
||||
{
|
||||
}
|
||||
|
||||
/** Create an event
|
||||
*
|
||||
* Constructs an event. 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 queue Event queue to dispatch on
|
||||
* @param f Function to execute when the event is dispatched
|
||||
* @param args Arguments to bind to the callback
|
||||
*/
|
||||
constexpr UserAllocatedEvent(EventQueue *queue, F f, ArgTs... args) : _e(get_default_equeue_event()), _c(f, args...), _equeue(&queue->_equeue), _post_ref()
|
||||
{
|
||||
}
|
||||
|
||||
/** Destructor for events
|
||||
*/
|
||||
#if !defined(NDEBUG)
|
||||
// Remove the user provided destructor in release to allow constexpr optimization
|
||||
// constexpr requires destructor to be trivial and only default one is treated as trivial
|
||||
~UserAllocatedEvent()
|
||||
{
|
||||
MBED_ASSERT(!_post_ref);
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Posts an event onto the underlying event queue, returning void
|
||||
*
|
||||
* The event is posted to the underlying queue and is executed in the
|
||||
* context of the event queue's dispatch loop.
|
||||
*
|
||||
* This call cannot fail due queue memory exhaustion
|
||||
* because it doesn't allocate any memory
|
||||
*
|
||||
* The post function is IRQ safe and can act as a mechanism for moving
|
||||
* events out of IRQ contexts.
|
||||
*
|
||||
*/
|
||||
void call()
|
||||
{
|
||||
MBED_ASSERT(!_post_ref);
|
||||
MBED_ASSERT(_equeue);
|
||||
MBED_UNUSED bool status = post();
|
||||
MBED_ASSERT(status);
|
||||
}
|
||||
|
||||
/** Posts an event onto the event queue passed as argument, returning void
|
||||
*
|
||||
* The event is posted to the event queue passed as argument
|
||||
* and is executed in the context of the event queue's dispatch loop.
|
||||
*
|
||||
* This call cannot fail due queue memory exhaustion
|
||||
* because it doesn't allocate any memory
|
||||
*
|
||||
* The post function is IRQ safe and can act as a mechanism for moving
|
||||
* events out of IRQ contexts.
|
||||
*
|
||||
* @param queue Event queue to dispatch on. Will replace earlier bound EventQueue.
|
||||
*
|
||||
*/
|
||||
void call_on(EventQueue *queue)
|
||||
{
|
||||
MBED_ASSERT(!_post_ref);
|
||||
MBED_UNUSED bool status = post_on(queue);
|
||||
MBED_ASSERT(status);
|
||||
}
|
||||
|
||||
/** Posts an event onto the underlying event queue
|
||||
*
|
||||
* The event is posted to the event queue passed as argument
|
||||
* and is executed in the context of the event queue's dispatch loop.
|
||||
*
|
||||
* This call cannot fail due queue memory exhaustion
|
||||
* because it doesn't allocate any memory
|
||||
*
|
||||
* @return False if the event was already posted
|
||||
* true otherwise
|
||||
*
|
||||
*/
|
||||
bool try_call()
|
||||
{
|
||||
return post();
|
||||
}
|
||||
|
||||
/** Posts an event onto the event queue passed as argument,
|
||||
*
|
||||
* The event is posted to the underlying queue and is executed in the
|
||||
* context of the event queue's dispatch loop.
|
||||
*
|
||||
* This call cannot fail due queue memory exhaustion
|
||||
* because it doesn't allocate any memory
|
||||
*
|
||||
* @param queue Event queue to dispatch on. Will replace earlier bound EventQueue.
|
||||
* @return False if the event was already posted
|
||||
* true otherwise
|
||||
*
|
||||
*/
|
||||
bool try_call_on(EventQueue *queue)
|
||||
{
|
||||
return post_on(queue);
|
||||
}
|
||||
|
||||
/** Posts an event onto the underlying event queue, returning void
|
||||
*
|
||||
* The event is posted to the underlying queue and is executed in the
|
||||
* context of the event queue's dispatch loop.
|
||||
*
|
||||
* This call cannot fail due queue memory exhaustion
|
||||
* because it doesn't allocate any memory
|
||||
*
|
||||
* The post function is IRQ safe and can act as a mechanism for moving
|
||||
* events out of IRQ contexts.
|
||||
*
|
||||
*/
|
||||
void operator()()
|
||||
{
|
||||
return call();
|
||||
}
|
||||
|
||||
/** Configure the delay of an event
|
||||
*
|
||||
* @param delay Millisecond delay before dispatching the event
|
||||
*/
|
||||
void delay(int delay)
|
||||
{
|
||||
equeue_event_delay(&_e + 1, delay);
|
||||
}
|
||||
|
||||
/** Configure the period of an event
|
||||
*
|
||||
* @param period Millisecond period for repeatedly dispatching an event
|
||||
*/
|
||||
void period(int period)
|
||||
{
|
||||
equeue_event_period(&_e + 1, period);
|
||||
}
|
||||
|
||||
/** Cancels posted event
|
||||
*
|
||||
* Attempts to cancel 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 guarantee that the event will not execute after it
|
||||
* returns, as the event may have already begun executing.
|
||||
*
|
||||
* @return true if event was successfully cancelled
|
||||
*/
|
||||
bool cancel()
|
||||
{
|
||||
return equeue_cancel_user_allocated(_equeue, &_e);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
friend class EventQueue;
|
||||
struct equeue_event _e;
|
||||
C _c;
|
||||
struct equeue *_equeue;
|
||||
uint8_t _post_ref;
|
||||
|
||||
bool post()
|
||||
{
|
||||
if (_post_ref) {
|
||||
return false;
|
||||
}
|
||||
core_util_atomic_incr_u8(&_post_ref, 1);
|
||||
equeue_post_user_allocated(_equeue, &EventQueue::function_call<C>, &_e);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool post_on(EventQueue *queue)
|
||||
{
|
||||
if (_post_ref) {
|
||||
return false;
|
||||
}
|
||||
_equeue = &(queue->_equeue);
|
||||
core_util_atomic_incr_u8(&_post_ref, 1);
|
||||
equeue_post_user_allocated(_equeue, &EventQueue::function_call<C>, &_e);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void event_dtor(void *p)
|
||||
{
|
||||
UserAllocatedEvent<F, void(ArgTs...)> *instance = (UserAllocatedEvent<F, void(ArgTs...)> *)(((equeue_event *)p) - 1);
|
||||
core_util_atomic_decr_u8(&instance->_post_ref, 1);
|
||||
MBED_ASSERT(!instance->_post_ref);
|
||||
}
|
||||
|
||||
constexpr static equeue_event get_default_equeue_event()
|
||||
{
|
||||
return equeue_event{ 0, 0, 0, NULL, NULL, NULL, 0, -1, &UserAllocatedEvent::event_dtor, NULL };
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/** Create an event
|
||||
* @see UserAllocatedEvent::UserAllocatedEvent
|
||||
*/
|
||||
template <typename T, typename R>
|
||||
constexpr UserAllocatedEvent(T *obj, R(T::*method)(ArgTs...), ArgTs... args) :
|
||||
UserAllocatedEvent(mbed::callback(obj, method), args...) { }
|
||||
|
||||
/** Create an event
|
||||
* @see UserAllocatedEvent::UserAllocatedEvent
|
||||
*/
|
||||
template <typename T, typename R>
|
||||
constexpr UserAllocatedEvent(EventQueue *q, T *obj, R(T::*method)(ArgTs...), ArgTs... args) :
|
||||
UserAllocatedEvent(q, mbed::callback(obj, method), args...) { }
|
||||
|
||||
/** Create an event
|
||||
* @see UserAllocatedEvent::UserAllocatedEvent
|
||||
*/
|
||||
template <typename T, typename R>
|
||||
constexpr UserAllocatedEvent(const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) :
|
||||
UserAllocatedEvent(mbed::callback(obj, method), args...) { }
|
||||
|
||||
/** Create an event
|
||||
* @see UserAllocatedEvent::UserAllocatedEvent
|
||||
*/
|
||||
template <typename T, typename R>
|
||||
constexpr UserAllocatedEvent(EventQueue *q, const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) :
|
||||
UserAllocatedEvent(q, mbed::callback(obj, method), args...) { }
|
||||
|
||||
/** Create an event
|
||||
* @see UserAllocatedEvent::UserAllocatedEvent
|
||||
*/
|
||||
template <typename T, typename R>
|
||||
constexpr UserAllocatedEvent(volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) :
|
||||
UserAllocatedEvent(mbed::callback(obj, method), args...) { }
|
||||
|
||||
/** Create an event
|
||||
* @see UserAllocatedEvent::UserAllocatedEvent
|
||||
*/
|
||||
template <typename T, typename R>
|
||||
constexpr UserAllocatedEvent(EventQueue *q, volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) :
|
||||
UserAllocatedEvent(q, mbed::callback(obj, method), args...) { }
|
||||
|
||||
/** Create an event
|
||||
* @see UserAllocatedEvent::UserAllocatedEvent
|
||||
*/
|
||||
template <typename T, typename R>
|
||||
constexpr UserAllocatedEvent(const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) :
|
||||
UserAllocatedEvent(mbed::callback(obj, method), args...) { }
|
||||
|
||||
/** Create an event
|
||||
* @see UserAllocatedEvent::UserAllocatedEvent
|
||||
*/
|
||||
template <typename T, typename R>
|
||||
constexpr UserAllocatedEvent(EventQueue *q, const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) :
|
||||
UserAllocatedEvent(q, mbed::callback(obj, method), args...) { }
|
||||
};
|
||||
|
||||
// Convenience functions declared here to avoid cyclic
|
||||
// dependency between Event and EventQueue
|
||||
template <typename F, typename... ArgTs>
|
||||
UserAllocatedEvent<F, void(ArgTs...)> EventQueue::make_user_allocated_event(F f, ArgTs... args)
|
||||
{
|
||||
return UserAllocatedEvent<F, void(ArgTs...)>(this, f, args...);
|
||||
}
|
||||
|
||||
template <typename T, typename R, typename... ArgTs>
|
||||
UserAllocatedEvent<mbed::Callback<void(ArgTs...)>, void(ArgTs...)> EventQueue::make_user_allocated_event(T *obj, R(T::*method)(ArgTs... args), ArgTs... args)
|
||||
{
|
||||
return UserAllocatedEvent<mbed::Callback<void(ArgTs...)>, void(ArgTs...)>(this, mbed::callback(obj, method), args...);
|
||||
}
|
||||
|
||||
template <typename T, typename R, typename... ArgTs>
|
||||
UserAllocatedEvent<mbed::Callback<void(ArgTs...)>, void(ArgTs...)> EventQueue::make_user_allocated_event(const T *obj, R(T::*method)(ArgTs... args) const, ArgTs... args)
|
||||
{
|
||||
return UserAllocatedEvent<mbed::Callback<void(ArgTs...)>, void(ArgTs...)>(this, mbed::callback(obj, method), args...);
|
||||
}
|
||||
|
||||
template <typename T, typename R, typename... ArgTs>
|
||||
UserAllocatedEvent<mbed::Callback<void(ArgTs...)>, void(ArgTs...)> EventQueue::make_user_allocated_event(volatile T *obj, R(T::*method)(ArgTs... args) volatile, ArgTs... args)
|
||||
{
|
||||
return UserAllocatedEvent<mbed::Callback<void(ArgTs...)>, void(ArgTs...)>(this, mbed::callback(obj, method), args...);
|
||||
}
|
||||
|
||||
template <typename T, typename R, typename... ArgTs>
|
||||
UserAllocatedEvent<mbed::Callback<void(ArgTs...)>, void(ArgTs...)> EventQueue::make_user_allocated_event(const volatile T *obj, R(T::*method)(ArgTs... args) const volatile, ArgTs... args)
|
||||
{
|
||||
return UserAllocatedEvent<mbed::Callback<void(ArgTs...)>, void(ArgTs...)>(this, mbed::callback(obj, method), args...);
|
||||
}
|
||||
|
||||
|
||||
/** Creates a UserAllocatedEvent object, deducing the target type from the types of arguments
|
||||
*
|
||||
* UserAllocatedEvent doesn't utilize EventQueue internal memory,
|
||||
* therefore it can be posted on the queue without being afraid
|
||||
* of post fail due to queue memory exhaustion
|
||||
*
|
||||
* @return UserAllocatedEvent object instance
|
||||
*
|
||||
*/
|
||||
template <typename F, typename... ArgTs>
|
||||
constexpr UserAllocatedEvent<F, void(ArgTs...)> make_user_allocated_event(F f, ArgTs... args)
|
||||
{
|
||||
return UserAllocatedEvent<F, void(ArgTs...)>(f, args...);
|
||||
}
|
||||
|
||||
/** Creates a UserAllocatedEvent object, deducing the target type from the types of arguments
|
||||
*
|
||||
* UserAllocatedEvent doesn't utilize EventQueue internal memory,
|
||||
* therefore it can be posted on the queue without being afraid
|
||||
* of post fail due to queue memory exhaustion
|
||||
*
|
||||
* @return UserAllocatedEvent object instance
|
||||
*
|
||||
*/
|
||||
template <typename T, typename R, typename... ArgTs>
|
||||
constexpr UserAllocatedEvent<mbed::Callback<void(ArgTs...)>, void(ArgTs...)> make_user_allocated_event(T *obj, R(T::*method)(ArgTs... args), ArgTs... args)
|
||||
{
|
||||
return UserAllocatedEvent<mbed::Callback<void(ArgTs...)>, void(ArgTs...)>(mbed::callback(obj, method), args...);
|
||||
}
|
||||
|
||||
/** Creates a UserAllocatedEvent object, deducing the target type from the types of arguments
|
||||
*
|
||||
* UserAllocatedEvent doesn't utilize EventQueue internal memory,
|
||||
* therefore it can be posted on the queue without being afraid
|
||||
* of post fail due to queue memory exhaustion
|
||||
*
|
||||
* @return UserAllocatedEvent object instance
|
||||
*
|
||||
*/
|
||||
template <typename T, typename R, typename... ArgTs>
|
||||
constexpr UserAllocatedEvent<mbed::Callback<void(ArgTs...)>, void(ArgTs...)> make_user_allocated_event(const T *obj, R(T::*method)(ArgTs... args) const, ArgTs... args)
|
||||
{
|
||||
return UserAllocatedEvent<mbed::Callback<void(ArgTs...)>, void(ArgTs...)>(mbed::callback(obj, method), args...);
|
||||
}
|
||||
|
||||
/** Creates a UserAllocatedEvent object, deducing the target type from the types of arguments
|
||||
*
|
||||
* UserAllocatedEvent doesn't utilize EventQueue internal memory,
|
||||
* therefore it can be posted on the queue without being afraid
|
||||
* of post fail due to queue memory exhaustion
|
||||
*
|
||||
* @return UserAllocatedEvent object instance
|
||||
*
|
||||
*/
|
||||
template <typename T, typename R, typename... ArgTs>
|
||||
constexpr UserAllocatedEvent<mbed::Callback<void(ArgTs...)>, void(ArgTs...)> make_user_allocated_event(volatile T *obj, R(T::*method)(ArgTs... args) volatile, ArgTs... args)
|
||||
{
|
||||
return UserAllocatedEvent<mbed::Callback<void(ArgTs...)>, void(ArgTs...)>(mbed::callback(obj, method), args...);
|
||||
}
|
||||
|
||||
/** Creates a UserAllocatedEvent object, deducing the target type from the types of arguments
|
||||
*
|
||||
* UserAllocatedEvent doesn't utilize EventQueue internal memory,
|
||||
* therefore it can be posted on the queue without being afraid
|
||||
* of post fail due to queue memory exhaustion
|
||||
*
|
||||
* @return UserAllocatedEvent object instance
|
||||
*
|
||||
*/
|
||||
template <typename T, typename R, typename... ArgTs>
|
||||
constexpr UserAllocatedEvent<mbed::Callback<void(ArgTs...)>, void(ArgTs...)> make_user_allocated_event(const volatile T *obj, R(T::*method)(ArgTs... args) const volatile, ArgTs... args)
|
||||
{
|
||||
return UserAllocatedEvent<mbed::Callback<void(ArgTs...)>, void(ArgTs...)>(mbed::callback(obj, method), args...);
|
||||
}
|
||||
|
||||
/** @}*/
|
||||
|
||||
/** @}*/
|
||||
}
|
||||
#endif
|
|
@ -176,6 +176,17 @@ void equeue_event_dtor(void *event, void (*dtor)(void *));
|
|||
// be passed to equeue_cancel.
|
||||
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
|
||||
//
|
||||
// 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.
|
||||
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
|
||||
//
|
||||
// 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);
|
||||
|
||||
// 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
|
||||
//
|
||||
// The provided update function will be called to indicate when the queue
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "events/EventQueue.h"
|
||||
#include "events/Event.h"
|
||||
#include "events/UserAllocatedEvent.h"
|
||||
|
||||
#include "events/mbed_shared_queues.h"
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* events
|
||||
* Copyright (c) 2016 ARM Limited
|
||||
* Copyright (c) 2016-2019 ARM Limited
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
@ -23,10 +23,16 @@ namespace events {
|
|||
|
||||
EventQueue::EventQueue(unsigned event_size, unsigned char *event_pointer)
|
||||
{
|
||||
if (!event_pointer) {
|
||||
equeue_create(&_equeue, event_size);
|
||||
if (event_size == 0) {
|
||||
// As static queue (EventQueue(0)) won't perform any access to its data buffer
|
||||
// we can pass (0, NULL)
|
||||
equeue_create_inplace(&_equeue, 0, NULL);
|
||||
} else {
|
||||
equeue_create_inplace(&_equeue, event_size, event_pointer);
|
||||
if (!event_pointer) {
|
||||
equeue_create(&_equeue, event_size);
|
||||
} else {
|
||||
equeue_create_inplace(&_equeue, event_size, event_pointer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
#include <stdint.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) ((q->buffer == NULL) || ((uintptr_t)(e) < (uintptr_t)q->buffer) || ((uintptr_t)(e) > ((uintptr_t)q->slab.data)))
|
||||
|
||||
// calculate the relative-difference between absolute times while
|
||||
// correctly handling overflow conditions
|
||||
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
|
||||
// ensure buffer and size are aligned
|
||||
q->buffer = (void *)(((uintptr_t) buffer + sizeof(void *) -1) & ~(sizeof(void *) -1));
|
||||
size -= (char *) q->buffer - (char *) buffer;
|
||||
size &= ~(sizeof(void *) -1);
|
||||
if (size >= sizeof(void *)) {
|
||||
q->buffer = (void *)(((uintptr_t) buffer + sizeof(void *) -1) & ~(sizeof(void *) -1));
|
||||
size -= (char *) q->buffer - (char *) buffer;
|
||||
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;
|
||||
|
||||
|
@ -220,15 +229,13 @@ void equeue_dealloc(equeue_t *q, void *p)
|
|||
e->dtor(e + 1);
|
||||
}
|
||||
|
||||
equeue_mem_dealloc(q, e);
|
||||
if (!EQUEUE_IS_USER_ALLOCATED_EVENT(e)) {
|
||||
equeue_mem_dealloc(q, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// equeue scheduling functions
|
||||
static int equeue_enqueue(equeue_t *q, struct equeue_event *e, unsigned tick)
|
||||
void 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->generation = q->generation;
|
||||
|
||||
|
@ -254,7 +261,6 @@ static int equeue_enqueue(equeue_t *q, struct equeue_event *e, unsigned tick)
|
|||
if (e->next) {
|
||||
e->next->ref = &e->next;
|
||||
}
|
||||
|
||||
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,
|
||||
equeue_clampdiff(e->target, tick));
|
||||
}
|
||||
|
||||
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
|
||||
struct equeue_event *e = (struct equeue_event *)
|
||||
&q->buffer[id & ((1 << q->npw2) - 1)];
|
||||
// setup event and hash local id with buffer offset for unique id
|
||||
return ((e->id << q->npw2) | ((unsigned char *)e - q->buffer));
|
||||
}
|
||||
|
||||
static struct equeue_event *equeue_unqueue_by_address(equeue_t *q, struct equeue_event *e)
|
||||
{
|
||||
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
|
||||
e->cb = 0;
|
||||
e->period = -1;
|
||||
|
@ -310,6 +311,26 @@ static struct equeue_event *equeue_unqueue(equeue_t *q, int id)
|
|||
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_mutex_unlock(&q->queuelock);
|
||||
|
@ -369,18 +390,30 @@ int equeue_post(equeue_t *q, void (*cb)(void *), void *p)
|
|||
e->cb = cb;
|
||||
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);
|
||||
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)
|
||||
{
|
||||
if (!id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct equeue_event *e = equeue_unqueue(q, id);
|
||||
struct equeue_event *e = equeue_unqueue_by_id(q, id);
|
||||
if (e) {
|
||||
equeue_dealloc(q, e + 1);
|
||||
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 ret = -1;
|
||||
|
@ -409,6 +457,21 @@ int equeue_timeleft(equeue_t *q, int id)
|
|||
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)
|
||||
{
|
||||
equeue_mutex_lock(&q->queuelock);
|
||||
|
|
|
@ -40,7 +40,10 @@ unsigned equeue_tick(void)
|
|||
// Mutex operations
|
||||
int equeue_mutex_create(equeue_mutex_t *m)
|
||||
{
|
||||
return pthread_mutex_init(m, 0);
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
return pthread_mutex_init(m, &attr);
|
||||
}
|
||||
|
||||
void equeue_mutex_destroy(equeue_mutex_t *m)
|
||||
|
|
|
@ -802,6 +802,56 @@ void sibling_test(void)
|
|||
equeue_destroy(&q);
|
||||
}
|
||||
|
||||
struct user_allocated_event {
|
||||
struct equeue_event e;
|
||||
bool touched;
|
||||
};
|
||||
|
||||
void user_allocated_event_test()
|
||||
{
|
||||
equeue_t q;
|
||||
int err = equeue_create(&q, EQUEUE_EVENT_SIZE);
|
||||
test_assert(!err);
|
||||
|
||||
bool touched = false;
|
||||
struct user_allocated_event e1 = { { 0, 0, 0, NULL, NULL, NULL, 0, -1, NULL, NULL }, 0 };
|
||||
struct user_allocated_event e2 = { { 0, 0, 0, NULL, NULL, NULL, 1, -1, NULL, NULL }, 0 };
|
||||
struct user_allocated_event e3 = { { 0, 0, 0, NULL, NULL, NULL, 1, -1, NULL, NULL }, 0 };
|
||||
struct user_allocated_event e4 = { { 0, 0, 0, NULL, NULL, NULL, 1, -1, NULL, NULL }, 0 };
|
||||
struct user_allocated_event e5 = { { 0, 0, 0, NULL, NULL, NULL, 0, -1, NULL, NULL }, 0 };
|
||||
|
||||
test_assert(0 != equeue_call(&q, simple_func, &touched));
|
||||
test_assert(0 == equeue_call(&q, simple_func, &touched));
|
||||
test_assert(0 == equeue_call(&q, simple_func, &touched));
|
||||
|
||||
equeue_post_user_allocated(&q, simple_func, &e1.e);
|
||||
equeue_post_user_allocated(&q, simple_func, &e2.e);
|
||||
equeue_post_user_allocated(&q, simple_func, &e3.e);
|
||||
equeue_post_user_allocated(&q, simple_func, &e4.e);
|
||||
equeue_post_user_allocated(&q, simple_func, &e5.e);
|
||||
equeue_cancel_user_allocated(&q, &e3.e);
|
||||
|
||||
equeue_dispatch(&q, 1);
|
||||
|
||||
test_assert(true == touched);
|
||||
test_assert(true == e1.touched);
|
||||
test_assert(true == e2.touched);
|
||||
test_assert(false == e3.touched);
|
||||
test_assert(true == e4.touched);
|
||||
test_assert(true == e5.touched);
|
||||
|
||||
equeue_dispatch(&q, 10);
|
||||
|
||||
test_assert(true == touched);
|
||||
test_assert(true == e1.touched);
|
||||
test_assert(true == e2.touched);
|
||||
test_assert(false == e3.touched);
|
||||
test_assert(true == e4.touched);
|
||||
test_assert(true == e5.touched);
|
||||
|
||||
equeue_destroy(&q);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("beginning tests...\n");
|
||||
|
@ -830,6 +880,7 @@ int main()
|
|||
test_run(multithreaded_barrage_test, 20);
|
||||
test_run(break_request_cleared_on_timeout);
|
||||
test_run(sibling_test);
|
||||
test_run(user_allocated_event_test);
|
||||
printf("done!\n");
|
||||
return test_failure;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue