mbed-os/events/UserAllocatedEvent.h

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