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