Merge pull request #11342 from maciejbocianski/event_queue_static_alloc

add queue static allocation support
pull/11394/head
Martin Kojtal 2019-08-30 20:14:02 +02:00 committed by GitHub
commit bdd6cb8dee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 1079 additions and 42 deletions

View File

@ -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)
}; };

View File

@ -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);

View File

@ -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);
}

View File

@ -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)

View File

@ -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>

460
events/UserAllocatedEvent.h Normal file
View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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);
}
} }
} }

View File

@ -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);

View File

@ -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)

View File

@ -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;
} }