From eb80c2539871112f6dd8a6cb2e084a1a86a8728c Mon Sep 17 00:00:00 2001 From: Kevin Bracey Date: Tue, 30 May 2017 16:12:51 +0300 Subject: [PATCH] Add ability to request a shared event queue To allow components with a simple need to schedule a few events to not have to create their own threads, with all the associated memory overhead, add 2 central calls to get shared normal and an interrupt-deferral event queues, each dispatched on their own shared threads. For non-RTOS systems, just the normal event queue is provided, and the application would have to dispatch this itself. This application-dispatch is also available via a config option, to potentially save memory by reusing the main thread. Possible future improvement: the ability for separate components to request a minimum stack size, and have the JSON combine these requests. (Analogous tooling has already been mooted for mbed TLS config options like key size). --- events/mbed_events.h | 2 + events/mbed_lib.json | 22 +++++++++- events/mbed_shared_queues.cpp | 72 +++++++++++++++++++++++++++++++ events/mbed_shared_queues.h | 81 +++++++++++++++++++++++++++++++++++ 4 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 events/mbed_shared_queues.cpp create mode 100644 events/mbed_shared_queues.h diff --git a/events/mbed_events.h b/events/mbed_events.h index d25caf0bd6..b313cd2d54 100644 --- a/events/mbed_events.h +++ b/events/mbed_events.h @@ -28,6 +28,8 @@ #include "events/EventQueue.h" #include "events/Event.h" +#include "events/mbed_shared_queues.h" + using namespace events; #endif diff --git a/events/mbed_lib.json b/events/mbed_lib.json index 7c6edcce89..0f786f6637 100644 --- a/events/mbed_lib.json +++ b/events/mbed_lib.json @@ -1,6 +1,26 @@ { "name": "events", "config": { - "present": 1 + "present": 1, + "shared-stacksize": { + "help": "Stack size (bytes) for shared event queue thread", + "value": 1024 + }, + "shared-eventsize": { + "help": "Event buffer size (bytes) for shared event queue", + "value": 256 + }, + "shared-dispatch-from-application": { + "help": "No thread created for shared event queue - application will call dispatch from another thread (eg dispatch_forever at end of main)", + "value": false + }, + "shared-highprio-stacksize": { + "help": "Stack size (bytes) for shared high-priority event queue thread", + "value": 1024 + }, + "shared-highprio-eventsize": { + "help": "Event buffer size (bytes) for shared high-priority event queue", + "value": 256 + } } } diff --git a/events/mbed_shared_queues.cpp b/events/mbed_shared_queues.cpp new file mode 100644 index 0000000000..b227d8d850 --- /dev/null +++ b/events/mbed_shared_queues.cpp @@ -0,0 +1,72 @@ +/* events + * Copyright (c) 2017 ARM Limited + * + * 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. + */ + +#include "events/mbed_shared_queues.h" +#include "mbed.h" + +using namespace events; + +namespace mbed { + +#ifdef MBED_CONF_RTOS_PRESENT +/* Create an event queue, and start the thread that dispatches it. Static + * variables mean this happens once the first time each template instantiation + * is called. This is currently instantiated no more than twice. + */ +template + +EventQueue *do_shared_event_queue_with_thread() +{ + static uint64_t queue_buffer[QueueSize / sizeof(uint64_t)]; + static EventQueue queue(sizeof queue_buffer, (unsigned char *) queue_buffer); + + static uint64_t stack[StackSize / sizeof(uint64_t)]; + static Thread thread(Priority, StackSize, (unsigned char *) stack); + + Thread::State state = thread.get_state(); + if (state == Thread::Inactive || state == Thread::Deleted) { + osStatus status = thread.start(callback(&queue, &EventQueue::dispatch_forever)); + MBED_ASSERT(status == osOK); + if (status != osOK) { + return NULL; + } + } + + return &queue; +} +#endif + +EventQueue *mbed_event_queue() +{ +#if MBED_CONF_EVENTS_SHARED_DISPATCH_FROM_APPLICATION || !defined MBED_CONF_RTOS_PRESENT + /* Only create the EventQueue, but no dispatching thread */ + static unsigned char queue_buffer[MBED_CONF_EVENTS_SHARED_EVENTSIZE]; + static EventQueue queue(sizeof queue_buffer, queue_buffer); + + return &queue; +#else + return do_shared_event_queue_with_thread(); +#endif +} + +#ifdef MBED_CONF_RTOS_PRESENT +EventQueue *mbed_highprio_event_queue() +{ + return do_shared_event_queue_with_thread(); +} +#endif + +} diff --git a/events/mbed_shared_queues.h b/events/mbed_shared_queues.h new file mode 100644 index 0000000000..e56f6bfbc3 --- /dev/null +++ b/events/mbed_shared_queues.h @@ -0,0 +1,81 @@ + +/** \addtogroup events */ +/** @{*/ +/* events + * Copyright (c) 2017 ARM Limited + * + * 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 MBED_SHARED_QUEUES_H +#define MBED_SHARED_QUEUES_H + +#include "events/EventQueue.h" + +namespace mbed { + +/** + * Return a pointer to an EventQueue, on which normal tasks can be queued. + * + * All calls to this return the same EventQueue - it and its dispatch thread + * are created on the first call to this function. The dispatch thread + * runs at default priority (currently osPriorityNormal). + * + * The EventQueue returned may be used to call() Events, or to chain() other + * EventQueues so that they are run in the same context. + * + * Events (or chained EventQueues) executing on the normal event queue should + * normally take less than 10ms to execute, to avoid starving other users. As + * such, users can expect that event latency will typically be 10ms or less, + * but could occasionally be significantly higher if many events are queued. + * + * If an RTOS is not present or the configuration option + * `events.shared-dispatch-from-application` is set to true, then this + * does not create a dedicated dispatch thread - instead the application is + * expected to run the EventQueue's dispatch, eg from main. This is necessary + * for the event loop to work without an RTOS, or an RTOS system can can save + * memory by reusing the main stack. + * + * @return pointer to event queue + */ +events::EventQueue *mbed_event_queue(); + +#ifdef MBED_CONF_RTOS_PRESENT +/** + * Return a pointer to an EventQueue, on which small high-priority tasks can + * be queues, such as simple deferrals from interrupt. + * + * All calls to this return the same EventQueue - it and its thread are + * created on the first call to this function. The dispatch thread + * runs at a high priority (currently osPriorityHigh). + * + * The EventQueue returned may be used to call() Events, or to chain() other + * EventQueues so that they are run in the same context. + * + * Events (or chained EventQueues) executing on the high-priority event queue + * should normally take less than 100us to execute, to avoid starving other + * users. As such, users can expect that event latency will typically be 100us + * or less, but could occasionally be significantly higher if many events are + * queued. + * + * @return pointer to high-priority event queue + */ + +events::EventQueue *mbed_highprio_event_queue(); + +#endif // MBED_CONF_RTOS_PRESENT + +}; + +#endif + +/** @}*/