Skip to content

Commit 5adc699

Browse files
author
Bogdan Marinescu
committed
Added EventAdapter
EventAdapter holds either a Callback<void()> or an Event<void()>. It is useful for attach()-like functions, so that their aguments can run either in user context or an interrupt context. See EventAdapter.h for more details.
1 parent 4de4cae commit 5adc699

File tree

2 files changed

+364
-0
lines changed

2 files changed

+364
-0
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/* mbed Microcontroller Library
2+
* Copyright (c) 2016 ARM Limited
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES Ovoid CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "mbed.h"
18+
#include "greentea-client/test_env.h"
19+
#include "unity/unity.h"
20+
#include "utest/utest.h"
21+
#include "EventQueue.h"
22+
#include <stdio.h>
23+
24+
using namespace utest::v1;
25+
using events::EventQueue;
26+
27+
static EventQueue queue(1024);
28+
static Thread queue_thread;
29+
static osThreadId main_thread_id;
30+
static osThreadId expected_thread_id;
31+
static volatile uint32_t call_count;
32+
33+
static void test_func() {
34+
if (expected_thread_id == NULL) {
35+
// We're testing the event queue, so save the queue thread ID for subsequent
36+
// tests (and make sure that we're not running in the main context).
37+
TEST_ASSERT_NOT_EQUAL((uint32_t)expected_thread_id, (uint32_t)main_thread_id);
38+
expected_thread_id = Thread::gettid();
39+
} else {
40+
TEST_ASSERT_EQUAL((uint32_t)expected_thread_id, (uint32_t)Thread::gettid());
41+
}
42+
call_count ++;
43+
}
44+
45+
static void test_callbacks() {
46+
expected_thread_id = main_thread_id;
47+
call_count = 0;
48+
// Create and call (they'll execute immediately in the context of the main thread)
49+
for (unsigned i = 0; i < 10; i ++) {
50+
EventAdapter(test_func).call();
51+
TEST_ASSERT_EQUAL_UINT32(call_count, i + 1);
52+
}
53+
}
54+
55+
static void test_events() {
56+
// We don't know the event thread ID. it'll be set by the first call to test_func
57+
expected_thread_id = NULL;
58+
call_count = 0;
59+
// Start event thread now
60+
queue_thread.start(callback(&queue, &EventQueue::dispatch_forever));
61+
// Create and call 10 events that will be queued
62+
for (unsigned i = 0; i < 10; i ++) {
63+
EventAdapter(queue.event(test_func)).call();
64+
}
65+
// Wait for them to finish
66+
while (call_count < 10) {
67+
wait_ms(10);
68+
}
69+
// Wait a bit more and check count again to check that no more events are executed
70+
wait_ms(200);
71+
TEST_ASSERT_EQUAL_UINT32(call_count, 10);
72+
}
73+
74+
static Case cases[] = {
75+
Case("Test callbacks in EventAdapter", test_callbacks),
76+
Case("Test events in EventAdapter", test_events)
77+
};
78+
79+
static status_t greentea_test_setup(const size_t number_of_cases) {
80+
GREENTEA_SETUP(20, "default_auto");
81+
return greentea_test_setup_handler(number_of_cases);
82+
}
83+
84+
static Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);
85+
86+
int main() {
87+
main_thread_id = Thread::gettid();
88+
// Run tests
89+
Harness::run(specification);
90+
}

hal/api/EventAdapter.h

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
#ifndef MBED_EVENT_ADAPTER_H
2+
#define MBED_EVENT_ADAPTER_H
3+
4+
#include "Callback.h"
5+
#include "Event.h"
6+
#include <stdint.h>
7+
8+
namespace mbed {
9+
10+
/** An EventAdapter holds either a Callback<void()> or an Event<void()>. It can be used
11+
* for attach()-style functions to specify if the attached callback should run in user
12+
* context or in interrupt context. To run in user context, construct an EventAdapter
13+
* from an Event object, this will be enqueued in an EventQueue. To run in interrupt
14+
* context (without enqueuing), const an EventAdapter from a Callback object. Example:
15+
*
16+
* @code
17+
* InterruptIn sw(SW2);
18+
* EventQueue queue(128);
19+
*
20+
* void handler(void) {
21+
* }
22+
*
23+
* int main() {
24+
* sw.rise(queue.event(handler)); // run in user context
25+
* sw.rise(callback(handler)); // run in interrupt context
26+
* sw.rise(handler); // shortcut for the form above (run in interrupt context)
27+
* }
28+
*/
29+
class EventAdapter {
30+
31+
typedef Callback<void()> adapter_callback_t;
32+
typedef events::Event<void()> adapter_event_t;
33+
34+
public:
35+
/** Create an empty EventAdapter (it contains an empty Callback)
36+
*/
37+
EventAdapter() {
38+
construct_callback(adapter_callback_t());
39+
}
40+
41+
/** Create an EventAdapter that holds a callback
42+
* @param cb The callback
43+
*/
44+
EventAdapter(const adapter_callback_t& cb) {
45+
construct_callback(cb);
46+
}
47+
48+
/** Create an EventAdapter with a static function callback
49+
* @param func Static function to attach
50+
*/
51+
EventAdapter(void (*func)()) {
52+
construct_callback(callback(func));
53+
}
54+
55+
/** Create an EventAdapter with a member function callback
56+
* @param obj Pointer to object to invoke member function on
57+
* @param method Member function to attach
58+
*/
59+
template<typename T>
60+
EventAdapter(T *obj, void (T::*method)()) {
61+
construct_callback(callback(obj, method));
62+
}
63+
64+
/** Create an EventAdapter with a member function callback
65+
* @param obj Pointer to object to invoke member function on
66+
* @param method Member function to attach
67+
*/
68+
template<typename T>
69+
EventAdapter(const T *obj, void (T::*method)() const) {
70+
construct_callback(callback(obj, method));
71+
}
72+
73+
/** Create an EventAdapter with a member function callback
74+
* @param obj Pointer to object to invoke member function on
75+
* @param method Member function to attach
76+
*/
77+
template<typename T>
78+
EventAdapter(volatile T *obj, void (T::*method)() volatile) {
79+
construct_callback(callback(obj, method));
80+
}
81+
82+
/** Create an EventAdapter with a member function callback
83+
* @param obj Pointer to object to invoke member function on
84+
* @param method Member function to attach
85+
*/
86+
template<typename T>
87+
EventAdapter(const volatile T *obj, void (T::*method)() const volatile) {
88+
construct_callback(callback(obj, method));
89+
}
90+
91+
/** Create an EventAdapter with a static function and bound pointer callback
92+
* @param func Static function to attach
93+
* @param arg Pointer argument to function
94+
*/
95+
EventAdapter(void (*func)(void*), void *arg) {
96+
construct_callback(callback(func, arg));
97+
}
98+
99+
/** Create an EventAdapter with a static function and bound pointer callback
100+
* @param func Static function to attach
101+
* @param arg Pointer argument to function
102+
*/
103+
EventAdapter(void (*func)(const void*), const void *arg) {
104+
construct_callback(callback(func, arg));
105+
}
106+
107+
/** Create an EventAdapter with a static function and bound pointer callback
108+
* @param func Static function to attach
109+
* @param arg Pointer argument to function
110+
*/
111+
EventAdapter(void (*func)(volatile void*), volatile void *arg) {
112+
construct_callback(callback(func, arg));
113+
}
114+
115+
/** Create an EventAdapter with a static function and bound pointer callback
116+
* @param func Static function to attach
117+
* @param arg Pointer argument to function
118+
*/
119+
EventAdapter(void (*func)(const volatile void*), const volatile void *arg) {
120+
construct_callback(callback(func, arg));
121+
}
122+
123+
/** Create an EventAdapter with a static function and bound pointer callback
124+
* @param func Static function to attach
125+
* @param arg Pointer argument to function
126+
*/
127+
template<typename T>
128+
EventAdapter(void (*func)(T*), T *arg) {
129+
construct_callback(callback(func, arg));
130+
}
131+
132+
/** Create an EventAdapter with a static function and bound pointer callback
133+
* @param func Static function to attach
134+
* @param arg Pointer argument to function
135+
*/
136+
template<typename T>
137+
EventAdapter(void (*func)(const T*), const T *arg) {
138+
construct_callback(callback(func, arg));
139+
}
140+
141+
/** Create an EventAdapter with a static function and bound pointer callback
142+
* @param func Static function to attach
143+
* @param arg Pointer argument to function
144+
*/
145+
template<typename T>
146+
EventAdapter(void (*func)(volatile T*), volatile T *arg) {
147+
construct_callback(callback(func, arg));
148+
}
149+
150+
/** Create an EventAdapter with a static function and bound pointer callback
151+
* @param func Static function to attach
152+
* @param arg Pointer argument to function
153+
*/
154+
template<typename T>
155+
EventAdapter(void (*func)(const volatile T*), const volatile T *arg) {
156+
construct_callback(callback(func, arg));
157+
}
158+
159+
/** Create an EventAdapter that holds an Event
160+
* @param evt The event
161+
*/
162+
EventAdapter(const adapter_event_t& evt) {
163+
construct_event(evt);
164+
}
165+
166+
EventAdapter(const EventAdapter& adapter) {
167+
_attach(adapter);
168+
}
169+
170+
/** Test if a function is attached to this adapter
171+
*/
172+
operator bool() const {
173+
if (_is_cb) {
174+
return get_callback()->operator bool();
175+
} else {
176+
return true;
177+
}
178+
}
179+
180+
EventAdapter& operator =(const EventAdapter& rhs) {
181+
if (this != &rhs) {
182+
destroy();
183+
_attach(rhs);
184+
}
185+
return *this;
186+
}
187+
188+
/** Attach this adapter to the callback or event in a different adapter
189+
* @param adapter The EventAdapter to attach.
190+
*/
191+
void attach(const EventAdapter& adapter) {
192+
destroy();
193+
_attach(adapter);
194+
}
195+
196+
/** Call the callback or event in this adapter
197+
*/
198+
void call() {
199+
if (_is_cb) {
200+
get_callback()->call();
201+
} else {
202+
get_event()->call();
203+
}
204+
}
205+
206+
/** Call the callback or event in this adapter
207+
*/
208+
void operator ()() {
209+
call();
210+
}
211+
212+
~EventAdapter() {
213+
destroy();
214+
}
215+
216+
private:
217+
// No unions for non-POD data types in C++03. Need to use placement new to simulate the
218+
// union between a Callback<void()> and an Event<void()>
219+
union {
220+
// Ensure alignment by declaring the storage uint32_t instead of uint8_t
221+
uint32_t _cb_data[(sizeof(adapter_callback_t) + sizeof(uint32_t) - 1) / sizeof(uint32_t)];
222+
uint32_t _evt_data[(sizeof(adapter_event_t) + sizeof(uint32_t) - 1) / sizeof(uint32_t)];
223+
};
224+
bool _is_cb;
225+
226+
void destroy() {
227+
if (_is_cb) {
228+
get_callback()->~adapter_callback_t();
229+
} else {
230+
get_event()->~adapter_event_t();
231+
}
232+
}
233+
234+
void construct_callback(const adapter_callback_t& cb) {
235+
_is_cb = true;
236+
adapter_callback_t *p_cb = reinterpret_cast<adapter_callback_t*>(&_cb_data);
237+
new (p_cb) adapter_callback_t();
238+
p_cb->attach(cb);
239+
}
240+
241+
void construct_event(const adapter_event_t&evt) {
242+
_is_cb = false;
243+
adapter_event_t *p_evt = reinterpret_cast<adapter_event_t*>(&_evt_data);
244+
new(p_evt) adapter_event_t(evt);
245+
}
246+
247+
const adapter_event_t *get_event() const {
248+
return reinterpret_cast<const adapter_event_t*>(&_evt_data);
249+
}
250+
251+
adapter_event_t *get_event() {
252+
return reinterpret_cast<adapter_event_t*>(&_evt_data);
253+
}
254+
255+
const adapter_callback_t *get_callback() const {
256+
return reinterpret_cast<const adapter_callback_t*>(&_cb_data);
257+
}
258+
259+
adapter_callback_t *get_callback() {
260+
return reinterpret_cast<adapter_callback_t*>(&_cb_data);
261+
}
262+
263+
void _attach(const EventAdapter& adapter) {
264+
if (adapter._is_cb) {
265+
construct_callback(*adapter.get_callback());
266+
} else {
267+
construct_event(*adapter.get_event());
268+
}
269+
}
270+
};
271+
272+
} // namespace mbed
273+
274+
#endif

0 commit comments

Comments
 (0)