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)
|
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)
|
static void sloth_func(void *p)
|
||||||
|
@ -977,6 +980,65 @@ static void test_equeue_sibling()
|
||||||
equeue_destroy(&q);
|
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 cases[] = {
|
||||||
Case("simple call test", test_equeue_simple_call),
|
Case("simple call test", test_equeue_simple_call),
|
||||||
|
@ -1006,7 +1068,8 @@ Case cases[] = {
|
||||||
Case("fragmenting barrage test", test_equeue_fragmenting_barrage<10>),
|
Case("fragmenting barrage test", test_equeue_fragmenting_barrage<10>),
|
||||||
Case("multithreaded barrage test", test_equeue_multithreaded_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("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
|
/* mbed Microcontroller Library
|
||||||
* Copyright (c) 2017 ARM Limited
|
* Copyright (c) 2017-2019 ARM Limited
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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));
|
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
|
// Test setup
|
||||||
utest::v1::status_t test_setup(const size_t number_of_cases)
|
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 the event inference", event_inference_test),
|
||||||
|
|
||||||
Case("Testing time_left", time_left_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);
|
Specification specification(test_setup, cases);
|
||||||
|
|
|
@ -45,7 +45,10 @@ static void pass_func(void *eh)
|
||||||
|
|
||||||
static void simple_func(void *p)
|
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)
|
static void sloth_func(void *p)
|
||||||
|
@ -995,3 +998,62 @@ TEST_F(TestEqueue, test_equeue_sibling)
|
||||||
equeue_cancel(&q, id2);
|
equeue_cancel(&q, id2);
|
||||||
equeue_destroy(&q);
|
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
|
// Mutex operations
|
||||||
int equeue_mutex_create(equeue_mutex_t *m)
|
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)
|
void equeue_mutex_destroy(equeue_mutex_t *m)
|
||||||
|
|
|
@ -45,6 +45,8 @@ namespace events {
|
||||||
// Predeclared classes
|
// Predeclared classes
|
||||||
template <typename F>
|
template <typename F>
|
||||||
class Event;
|
class Event;
|
||||||
|
template <typename F, typename A>
|
||||||
|
class UserAllocatedEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \defgroup events_EventQueue EventQueue class
|
* \defgroup events_EventQueue EventQueue class
|
||||||
|
@ -60,10 +62,17 @@ public:
|
||||||
/** Create an EventQueue
|
/** Create an EventQueue
|
||||||
*
|
*
|
||||||
* Create an event queue. The event queue either allocates a buffer of
|
* 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
|
* @param size Size of buffer to use for events in bytes
|
||||||
* (default to EVENTS_QUEUE_SIZE)
|
* (default to EVENTS_QUEUE_SIZE)
|
||||||
|
* If 0 provided then 1B dummy buffer is used
|
||||||
* @param buffer Pointer to buffer to use for events
|
* @param buffer Pointer to buffer to use for events
|
||||||
* (default to NULL)
|
* (default to NULL)
|
||||||
*/
|
*/
|
||||||
|
@ -139,6 +148,34 @@ public:
|
||||||
*/
|
*/
|
||||||
bool cancel(int id);
|
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
|
/** Query how much time is left for delayed event
|
||||||
*
|
*
|
||||||
* If the event is delayed, this function can be used to query how much time
|
* 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);
|
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
|
/** Background an event queue onto a single-shot timer-interrupt
|
||||||
*
|
*
|
||||||
* When updated, the event queue will call the provided update function
|
* 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>
|
template <typename R, typename ...BoundArgs, typename ...ContextArgs, typename ...Args>
|
||||||
Event<void(Args...)> event(mbed::Callback<R(BoundArgs..., Args...)> cb, ContextArgs ...context_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
|
#else
|
||||||
|
|
||||||
/** Calls an event on the queue
|
/** 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>
|
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);
|
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
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
#if !defined(DOXYGEN_ONLY)
|
#if !defined(DOXYGEN_ONLY)
|
||||||
template <typename F>
|
template <typename F>
|
||||||
friend class Event;
|
friend class Event;
|
||||||
|
template <typename F, typename A>
|
||||||
|
friend class UserAllocatedEvent;
|
||||||
struct equeue _equeue;
|
struct equeue _equeue;
|
||||||
mbed::Callback<void(int)> _update;
|
mbed::Callback<void(int)> _update;
|
||||||
|
|
||||||
|
@ -1098,7 +1247,7 @@ protected:
|
||||||
struct context<F> {
|
struct context<F> {
|
||||||
F f;
|
F f;
|
||||||
|
|
||||||
context(F f)
|
constexpr context(F f)
|
||||||
: f(f) {}
|
: f(f) {}
|
||||||
|
|
||||||
template <typename... ArgTs>
|
template <typename... ArgTs>
|
||||||
|
@ -1113,7 +1262,7 @@ protected:
|
||||||
F f;
|
F f;
|
||||||
C0 c0;
|
C0 c0;
|
||||||
|
|
||||||
context(F f, C0 c0)
|
constexpr context(F f, C0 c0)
|
||||||
: f(f), c0(c0) {}
|
: f(f), c0(c0) {}
|
||||||
|
|
||||||
template <typename... ArgTs>
|
template <typename... ArgTs>
|
||||||
|
@ -1129,7 +1278,7 @@ protected:
|
||||||
C0 c0;
|
C0 c0;
|
||||||
C1 c1;
|
C1 c1;
|
||||||
|
|
||||||
context(F f, C0 c0, C1 c1)
|
constexpr context(F f, C0 c0, C1 c1)
|
||||||
: f(f), c0(c0), c1(c1) {}
|
: f(f), c0(c0), c1(c1) {}
|
||||||
|
|
||||||
template <typename... ArgTs>
|
template <typename... ArgTs>
|
||||||
|
@ -1146,7 +1295,7 @@ protected:
|
||||||
C1 c1;
|
C1 c1;
|
||||||
C2 c2;
|
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) {}
|
: f(f), c0(c0), c1(c1), c2(c2) {}
|
||||||
|
|
||||||
template <typename... ArgTs>
|
template <typename... ArgTs>
|
||||||
|
@ -1164,7 +1313,7 @@ protected:
|
||||||
C2 c2;
|
C2 c2;
|
||||||
C3 c3;
|
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) {}
|
: f(f), c0(c0), c1(c1), c2(c2), c3(c3) {}
|
||||||
|
|
||||||
template <typename... ArgTs>
|
template <typename... ArgTs>
|
||||||
|
@ -1183,7 +1332,7 @@ protected:
|
||||||
C3 c3;
|
C3 c3;
|
||||||
C4 c4;
|
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) {}
|
: f(f), c0(c0), c1(c1), c2(c2), c3(c3), c4(c4) {}
|
||||||
|
|
||||||
template <typename... ArgTs>
|
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.
|
// be passed to equeue_cancel.
|
||||||
int equeue_post(equeue_t *queue, void (*cb)(void *), void *event);
|
int equeue_post(equeue_t *queue, void (*cb)(void *), void *event);
|
||||||
|
|
||||||
|
// Post an user allocated event onto the event queue
|
||||||
|
//
|
||||||
|
// The equeue_post_user_allocated function takes a callback and a pointer
|
||||||
|
// to an event allocated by user. The specified callback will be executed
|
||||||
|
// in the context of the event queue's dispatch loop with the allocated
|
||||||
|
// event as its argument.
|
||||||
|
//
|
||||||
|
// The equeue_post_user_allocated function is irq safe and can act as
|
||||||
|
// a mechanism for moving events out of irq contexts.
|
||||||
|
void equeue_post_user_allocated(equeue_t *queue, void (*cb)(void *), void *event);
|
||||||
|
|
||||||
// Cancel an in-flight event
|
// Cancel an in-flight event
|
||||||
//
|
//
|
||||||
// Attempts to cancel an event referenced by the unique id returned from
|
// Attempts to cancel an event referenced by the unique id returned from
|
||||||
|
@ -191,6 +202,20 @@ int equeue_post(equeue_t *queue, void (*cb)(void *), void *event);
|
||||||
// Returning false if invalid id or already started executing.
|
// Returning false if invalid id or already started executing.
|
||||||
bool equeue_cancel(equeue_t *queue, int id);
|
bool equeue_cancel(equeue_t *queue, int id);
|
||||||
|
|
||||||
|
// Cancel an in-flight user allocated event
|
||||||
|
//
|
||||||
|
// Attempts to cancel an event referenced by its address.
|
||||||
|
// It is safe to call equeue_cancel_user_allocated after an event
|
||||||
|
// has already been dispatched.
|
||||||
|
//
|
||||||
|
// The equeue_cancel_user_allocated function is irq safe.
|
||||||
|
//
|
||||||
|
// If called while the event queue's dispatch loop is active,
|
||||||
|
// equeue_cancel_user_allocated does not guarantee that the event
|
||||||
|
// will not not execute after it returns as the event may have
|
||||||
|
// already begun executing.
|
||||||
|
bool equeue_cancel_user_allocated(equeue_t *queue, void *event);
|
||||||
|
|
||||||
// Query how much time is left for delayed event
|
// Query how much time is left for delayed event
|
||||||
//
|
//
|
||||||
// If event is delayed, this function can be used to query how much time
|
// If event is delayed, this function can be used to query how much time
|
||||||
|
@ -200,6 +225,15 @@ bool equeue_cancel(equeue_t *queue, int id);
|
||||||
//
|
//
|
||||||
int equeue_timeleft(equeue_t *q, int id);
|
int equeue_timeleft(equeue_t *q, int id);
|
||||||
|
|
||||||
|
// Query how much time is left for delayed user allocated event
|
||||||
|
//
|
||||||
|
// If event is delayed, this function can be used to query how much time
|
||||||
|
// is left until the event is due to be dispatched.
|
||||||
|
//
|
||||||
|
// This function is irq safe.
|
||||||
|
//
|
||||||
|
int equeue_timeleft_user_allocated(equeue_t *q, void *event);
|
||||||
|
|
||||||
// Background an event queue onto a single-shot timer
|
// Background an event queue onto a single-shot timer
|
||||||
//
|
//
|
||||||
// The provided update function will be called to indicate when the queue
|
// The provided update function will be called to indicate when the queue
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
#include "events/EventQueue.h"
|
#include "events/EventQueue.h"
|
||||||
#include "events/Event.h"
|
#include "events/Event.h"
|
||||||
|
#include "events/UserAllocatedEvent.h"
|
||||||
|
|
||||||
#include "events/mbed_shared_queues.h"
|
#include "events/mbed_shared_queues.h"
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* events
|
/* events
|
||||||
* Copyright (c) 2016 ARM Limited
|
* Copyright (c) 2016-2019 ARM Limited
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* 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)
|
EventQueue::EventQueue(unsigned event_size, unsigned char *event_pointer)
|
||||||
{
|
{
|
||||||
if (!event_pointer) {
|
if (event_size == 0) {
|
||||||
equeue_create(&_equeue, event_size);
|
// 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 {
|
} 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 <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
// check if the event is allocaded by user - event address is outside queues internal buffer address range
|
||||||
|
#define EQUEUE_IS_USER_ALLOCATED_EVENT(e) ((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
|
// calculate the relative-difference between absolute times while
|
||||||
// correctly handling overflow conditions
|
// correctly handling overflow conditions
|
||||||
static inline int equeue_tickdiff(unsigned a, unsigned b)
|
static inline int equeue_tickdiff(unsigned a, unsigned b)
|
||||||
|
@ -64,9 +67,15 @@ int equeue_create_inplace(equeue_t *q, size_t size, void *buffer)
|
||||||
{
|
{
|
||||||
// setup queue around provided buffer
|
// setup queue around provided buffer
|
||||||
// ensure buffer and size are aligned
|
// ensure buffer and size are aligned
|
||||||
q->buffer = (void *)(((uintptr_t) buffer + sizeof(void *) -1) & ~(sizeof(void *) -1));
|
if (size >= sizeof(void *)) {
|
||||||
size -= (char *) q->buffer - (char *) buffer;
|
q->buffer = (void *)(((uintptr_t) buffer + sizeof(void *) -1) & ~(sizeof(void *) -1));
|
||||||
size &= ~(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;
|
q->allocated = 0;
|
||||||
|
|
||||||
|
@ -220,15 +229,13 @@ void equeue_dealloc(equeue_t *q, void *p)
|
||||||
e->dtor(e + 1);
|
e->dtor(e + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
equeue_mem_dealloc(q, e);
|
if (!EQUEUE_IS_USER_ALLOCATED_EVENT(e)) {
|
||||||
|
equeue_mem_dealloc(q, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void equeue_enqueue(equeue_t *q, struct equeue_event *e, unsigned tick)
|
||||||
// equeue scheduling functions
|
|
||||||
static int equeue_enqueue(equeue_t *q, struct equeue_event *e, unsigned tick)
|
|
||||||
{
|
{
|
||||||
// setup event and hash local id with buffer offset for unique id
|
|
||||||
int id = (e->id << q->npw2) | ((unsigned char *)e - q->buffer);
|
|
||||||
e->target = tick + equeue_clampdiff(e->target, tick);
|
e->target = tick + equeue_clampdiff(e->target, tick);
|
||||||
e->generation = q->generation;
|
e->generation = q->generation;
|
||||||
|
|
||||||
|
@ -254,7 +261,6 @@ static int equeue_enqueue(equeue_t *q, struct equeue_event *e, unsigned tick)
|
||||||
if (e->next) {
|
if (e->next) {
|
||||||
e->next->ref = &e->next;
|
e->next->ref = &e->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
e->sibling = 0;
|
e->sibling = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,24 +273,19 @@ static int equeue_enqueue(equeue_t *q, struct equeue_event *e, unsigned tick)
|
||||||
q->background.update(q->background.timer,
|
q->background.update(q->background.timer,
|
||||||
equeue_clampdiff(e->target, tick));
|
equeue_clampdiff(e->target, tick));
|
||||||
}
|
}
|
||||||
|
|
||||||
equeue_mutex_unlock(&q->queuelock);
|
equeue_mutex_unlock(&q->queuelock);
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct equeue_event *equeue_unqueue(equeue_t *q, int id)
|
// equeue scheduling functions
|
||||||
|
static int equeue_event_id(equeue_t *q, struct equeue_event *e)
|
||||||
{
|
{
|
||||||
// decode event from unique id and check that the local id matches
|
// setup event and hash local id with buffer offset for unique id
|
||||||
struct equeue_event *e = (struct equeue_event *)
|
return ((e->id << q->npw2) | ((unsigned char *)e - q->buffer));
|
||||||
&q->buffer[id & ((1 << q->npw2) - 1)];
|
}
|
||||||
|
|
||||||
|
static struct equeue_event *equeue_unqueue_by_address(equeue_t *q, struct equeue_event *e)
|
||||||
|
{
|
||||||
equeue_mutex_lock(&q->queuelock);
|
equeue_mutex_lock(&q->queuelock);
|
||||||
if (e->id != id >> q->npw2) {
|
|
||||||
equeue_mutex_unlock(&q->queuelock);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear the event and check if already in-flight
|
// clear the event and check if already in-flight
|
||||||
e->cb = 0;
|
e->cb = 0;
|
||||||
e->period = -1;
|
e->period = -1;
|
||||||
|
@ -310,6 +311,26 @@ static struct equeue_event *equeue_unqueue(equeue_t *q, int id)
|
||||||
e->next->ref = e->ref;
|
e->next->ref = e->ref;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
equeue_mutex_unlock(&q->queuelock);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct equeue_event *equeue_unqueue_by_id(equeue_t *q, int id)
|
||||||
|
{
|
||||||
|
// decode event from unique id and check that the local id matches
|
||||||
|
struct equeue_event *e = (struct equeue_event *)
|
||||||
|
&q->buffer[id & ((1 << q->npw2) - 1)];
|
||||||
|
|
||||||
|
equeue_mutex_lock(&q->queuelock);
|
||||||
|
if (e->id != id >> q->npw2) {
|
||||||
|
equeue_mutex_unlock(&q->queuelock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 == equeue_unqueue_by_address(q, e)) {
|
||||||
|
equeue_mutex_unlock(&q->queuelock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
equeue_incid(q, e);
|
equeue_incid(q, e);
|
||||||
equeue_mutex_unlock(&q->queuelock);
|
equeue_mutex_unlock(&q->queuelock);
|
||||||
|
@ -369,18 +390,30 @@ int equeue_post(equeue_t *q, void (*cb)(void *), void *p)
|
||||||
e->cb = cb;
|
e->cb = cb;
|
||||||
e->target = tick + e->target;
|
e->target = tick + e->target;
|
||||||
|
|
||||||
int id = equeue_enqueue(q, e, tick);
|
equeue_enqueue(q, e, tick);
|
||||||
|
int id = equeue_event_id(q, e);
|
||||||
equeue_sema_signal(&q->eventsema);
|
equeue_sema_signal(&q->eventsema);
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void equeue_post_user_allocated(equeue_t *q, void (*cb)(void *), void *p)
|
||||||
|
{
|
||||||
|
struct equeue_event *e = (struct equeue_event *)p;
|
||||||
|
unsigned tick = equeue_tick();
|
||||||
|
e->cb = cb;
|
||||||
|
e->target = tick + e->target;
|
||||||
|
|
||||||
|
equeue_enqueue(q, e, tick);
|
||||||
|
equeue_sema_signal(&q->eventsema);
|
||||||
|
}
|
||||||
|
|
||||||
bool equeue_cancel(equeue_t *q, int id)
|
bool equeue_cancel(equeue_t *q, int id)
|
||||||
{
|
{
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct equeue_event *e = equeue_unqueue(q, id);
|
struct equeue_event *e = equeue_unqueue_by_id(q, id);
|
||||||
if (e) {
|
if (e) {
|
||||||
equeue_dealloc(q, e + 1);
|
equeue_dealloc(q, e + 1);
|
||||||
return true;
|
return true;
|
||||||
|
@ -389,6 +422,21 @@ bool equeue_cancel(equeue_t *q, int id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool equeue_cancel_user_allocated(equeue_t *q, void *e)
|
||||||
|
{
|
||||||
|
if (!e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct equeue_event *_e = equeue_unqueue_by_address(q, e);
|
||||||
|
if (_e) {
|
||||||
|
equeue_dealloc(q, _e + 1);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int equeue_timeleft(equeue_t *q, int id)
|
int equeue_timeleft(equeue_t *q, int id)
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
|
@ -409,6 +457,21 @@ int equeue_timeleft(equeue_t *q, int id)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int equeue_timeleft_user_allocated(equeue_t *q, void *e)
|
||||||
|
{
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
if (!e) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct equeue_event *_e = (struct equeue_event *)e;
|
||||||
|
equeue_mutex_lock(&q->queuelock);
|
||||||
|
ret = equeue_clampdiff(_e->target, equeue_tick());
|
||||||
|
equeue_mutex_unlock(&q->queuelock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
void equeue_break(equeue_t *q)
|
void equeue_break(equeue_t *q)
|
||||||
{
|
{
|
||||||
equeue_mutex_lock(&q->queuelock);
|
equeue_mutex_lock(&q->queuelock);
|
||||||
|
|
|
@ -40,7 +40,10 @@ unsigned equeue_tick(void)
|
||||||
// Mutex operations
|
// Mutex operations
|
||||||
int equeue_mutex_create(equeue_mutex_t *m)
|
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)
|
void equeue_mutex_destroy(equeue_mutex_t *m)
|
||||||
|
|
|
@ -802,6 +802,56 @@ void sibling_test(void)
|
||||||
equeue_destroy(&q);
|
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()
|
int main()
|
||||||
{
|
{
|
||||||
printf("beginning tests...\n");
|
printf("beginning tests...\n");
|
||||||
|
@ -830,6 +880,7 @@ int main()
|
||||||
test_run(multithreaded_barrage_test, 20);
|
test_run(multithreaded_barrage_test, 20);
|
||||||
test_run(break_request_cleared_on_timeout);
|
test_run(break_request_cleared_on_timeout);
|
||||||
test_run(sibling_test);
|
test_run(sibling_test);
|
||||||
|
test_run(user_allocated_event_test);
|
||||||
printf("done!\n");
|
printf("done!\n");
|
||||||
return test_failure;
|
return test_failure;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue