/* * 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 * @{ */ template 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 class UserAllocatedEvent { public: typedef EventQueue::context 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) { MBED_ASSERT(!_post_ref); 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) { MBED_ASSERT(!_post_ref); 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, &_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, &_e); return true; } static void event_dtor(void *p) { UserAllocatedEvent *instance = (UserAllocatedEvent *)(((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 constexpr UserAllocatedEvent(T *obj, R(T::*method)(ArgTs...), ArgTs... args) : UserAllocatedEvent(mbed::callback(obj, method), args...) { } /** Create an event * @see UserAllocatedEvent::UserAllocatedEvent */ template 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 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 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 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 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 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 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 UserAllocatedEvent EventQueue::make_user_allocated_event(F f, ArgTs... args) { return UserAllocatedEvent(this, f, args...); } template UserAllocatedEvent, void(ArgTs...)> EventQueue::make_user_allocated_event(T *obj, R(T::*method)(ArgTs... args), ArgTs... args) { return UserAllocatedEvent, void(ArgTs...)>(this, mbed::callback(obj, method), args...); } template UserAllocatedEvent, void(ArgTs...)> EventQueue::make_user_allocated_event(const T *obj, R(T::*method)(ArgTs... args) const, ArgTs... args) { return UserAllocatedEvent, void(ArgTs...)>(this, mbed::callback(obj, method), args...); } template UserAllocatedEvent, void(ArgTs...)> EventQueue::make_user_allocated_event(volatile T *obj, R(T::*method)(ArgTs... args) volatile, ArgTs... args) { return UserAllocatedEvent, void(ArgTs...)>(this, mbed::callback(obj, method), args...); } template UserAllocatedEvent, void(ArgTs...)> EventQueue::make_user_allocated_event(const volatile T *obj, R(T::*method)(ArgTs... args) const volatile, ArgTs... args) { return UserAllocatedEvent, 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 constexpr UserAllocatedEvent make_user_allocated_event(F f, ArgTs... args) { return UserAllocatedEvent(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 constexpr UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(T *obj, R(T::*method)(ArgTs... args), ArgTs... args) { return UserAllocatedEvent, 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 constexpr UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(const T *obj, R(T::*method)(ArgTs... args) const, ArgTs... args) { return UserAllocatedEvent, 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 constexpr UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(volatile T *obj, R(T::*method)(ArgTs... args) volatile, ArgTs... args) { return UserAllocatedEvent, 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 constexpr UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(const volatile T *obj, R(T::*method)(ArgTs... args) const volatile, ArgTs... args) { return UserAllocatedEvent, void(ArgTs...)>(mbed::callback(obj, method), args...); } /** @}*/ /** @}*/ } #endif