mirror of https://github.com/ARMmbed/mbed-os.git
462 lines
16 KiB
C++
462 lines
16 KiB
C++
/*
|
|
* 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 <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)
|
|
{
|
|
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<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
|