unified: rename 'events' to 'alerts'

Event is such an overloaded and generic term (event logger, *kernel*
event logger, "protocol" events in other subsystems, etc.), that it is
confusing for the name an object. Events are kinda like signals, but not
exactly, so we chose not to name them 'signals' to prevent further
confusion. "Alerts" felt like a good fit, since they are used to "alert"
an application that something of significance should be addressed and
because an "alert handler" can be proactively registered with an alert.

Change-Id: Ibfeb5eaf0e6e62702ac3fec281d17f8a63145fa1
Signed-off-by: Benjamin Walsh <benjamin.walsh@windriver.com>
This commit is contained in:
Benjamin Walsh 2016-10-25 13:28:35 -04:00
commit 31a3f6a70e
11 changed files with 356 additions and 356 deletions

View file

@ -100,11 +100,11 @@ The nanokernel semaphore and microkernel semaphore object types have been
merged into a single type. The new type can now be used as a binary semaphore,
as well as a counting semaphore.
The microkernel event object type is now presented as a relative to Unix-style
signalling. Due to improvements to the implementation of semaphores, events
are now less efficient to use for basic synchronization than semaphores;
consequently, events should now be reserved for scenarios requiring the use
of a callback function.
The microkernel event object type is renamed to "alert" and is now presented as
a relative to Unix-style signalling. Due to improvements to the implementation
of semaphores, alerts are now less efficient to use for basic synchronization
than semaphores; consequently, alerts should now be reserved for scenarios
requiring the use of a callback function.
Data Passing
************

View file

@ -0,0 +1,236 @@
.. _alerts_v2:
Alerts
######
An :dfn:`alert` is a kernel object that allows an application to perform
asynchronous signalling when a condition of interest occurs.
.. contents::
:local:
:depth: 2
Concepts
********
Any number of alerts can be defined. Each alert is referenced by
its memory address.
An alert has the following key properties:
* An **alert handler**, which specifies the action to be taken
when the alert is signalled. The action may instruct the system workqueue
to execute a function to process the alert, mark the alert as pending
so it can be processed later by a thread, or ignore the alert.
* An **alert pending flag**, which is set if the alert is signalled
and an alert handler function has not processed the signal.
An alert must be initialized before it can be used. This establishes
its alert handler and clears the alert pending flag.
Alert Lifecycle
===============
An ISR or a thread signals an alert by **sending** the alert
when a condition of interest occurs that cannot be handled by the detector.
Each time an alert is sent, the kernel examines its alert handler
to determine what action to take.
* :c:macro:`K_ALERT_IGNORE` causes the alert to be ignored.
* :c:macro:`K_ALERT_DEFAULT` causes the alert pending flag to be set.
* Any other value is assumed to be the address of an alert handler function,
and is invoked by the system workqueue thread. If the function returns
zero, the signal is deemed to have been consumed; otherwise, the alert
pending flag is set.
The kernel ensures that the alert handler function is executed once
for each time an alert is sent, even if the alert is sent multiple times
in rapid succession.
An alert whose alert pending flag becomes set remains pending until
the alert is accepted by a thread. This clears the alert pending flag.
A thread accepts a pending alert by **receiving** the alert.
If the alert's pending flag is currently clear, the thread may choose
to wait for the alert to become pending.
Any number of threads may wait for a pending alert simultaneously;
when the alert is pended it is accepted by the highest priority thread
that has waited longest.
.. note::
A thread that accepts an alert cannot directly determine how many times
the alert pending flag was set since the alert was last accepted.
Comparison to Unix-style Signals
================================
Zephyr alerts are somewhat akin to Unix-style signals, but have a number of
significant differences. The most notable of these are:
* A Zephyr alert cannot be blocked; it is always delivered to its alert
handler immediately.
* A Zephyr alert pends *after* it has been delivered to its alert handler,
and only if an alert handler function does not consume the alert.
* Zephyr has no pre-defined alerts or actions. All alerts are application
defined, and all have a default action that pends the alert.
Implementation
**************
Defining an Alert
=================
An alert is defined using a variable of type :c:type:`struct k_alert`.
It must then be initialized by calling :cpp:func:`k_alert_init()`.
The following code defines and initializes an alert.
.. code-block:: c
extern int my_alert_handler(struct k_alert *alert);
struct k_alert my_alert;
k_alert_init(&my_alert, my_alert_handler);
Alternatively, an alert can be defined and initialized at compile time
by calling :c:macro:`K_ALERT_DEFINE()`.
The following code has the same effect as the code segment above.
.. code-block:: c
extern int my_alert_handler(struct k_alert *alert);
K_ALERT_DEFINE(my_alert, my_alert_handler);
Signaling an Alert
==================
An alert is signalled by calling :cpp:func:`k_alert_send()`.
The following code illustrates how an ISR can signal an alert
to indicate that a key press has occurred.
.. code-block:: c
extern int my_alert_handler(struct k_alert *alert);
K_ALERT_DEFINE(my_alert, my_alert_handler);
void keypress_interrupt_handler(void *arg)
{
...
k_alert_send(&my_alert);
...
}
Handling an Alert
=================
An alert handler function is used when a signalled alert should not be ignored
or immediately pended. It has the following form:
.. code-block:: c
int <function_name>(struct k_alert *alert)
{
/* catch the alert signal; return zero if the signal is consumed, */
/* or non-zero to let the alert pend */
...
}
The following code illustrates an alert handler function that processes
key presses detected by an ISR (as shown in the previous section).
.. code-block:: c
int my_alert_handler(struct k_alert *alert_id_is_unused)
{
/* determine what key was pressed */
char c = get_keypress();
/* do complex processing of the keystroke */
...
/* signalled alert has been consumed */
return 0;
}
Accepting an Alert
==================
A pending alert is accepted by a thread by calling :cpp:func:`k_alert_recv()`.
The following code is an alternative to the code in the previous section.
It uses a dedicated thread to do very complex processing
of key presses that would otherwise monopolize the system workqueue.
The alert handler function is now used only to filter out unwanted key press
alerts, allowing the dedicated thread to wake up and process key press alerts
only when a numeric key is pressed.
.. code-block:: c
int my_alert_handler(struct k_alert *alert_id_is_unused)
{
/* determine what key was pressed */
char c = get_keypress();
/* signal thread only if key pressed was a digit */
if ((c >= '0') && (c <= '9')) {
/* save key press information */
...
/* signalled alert should be pended */
return 1;
} else {
/* signalled alert has been consumed */
return 0;
}
}
void keypress_thread(void *unused1, void *unused2, void *unused3)
{
/* consume numeric key presses */
while (1) {
/* wait for a key press alert to pend */
k_alert_recv(&my_alert, K_FOREVER);
/* process saved key press, which must be a digit */
...
}
}
Suggested Uses
**************
Use an alert to minimize ISR processing by deferring interrupt-related
work to a thread to reduce the amount of time interrupts are locked.
Use an alert to allow the kernel's system workqueue to handle an alert,
rather than defining an application thread to handle it.
Use an alert to allow the kernel's system workqueue to pre-process an alert,
prior to letting an application thread handle it.
Configuration Options
*********************
Related configuration options:
* None.
APIs
****
The following alert APIs are provided by :file:`kernel.h`:
* :cpp:func:`k_alert_handler_set()`
* :cpp:func:`k_alert_send()`
* :cpp:func:`k_alert_recv()`

View file

@ -1,236 +0,0 @@
.. _events_v2:
Events
######
An :dfn:`event` is a kernel object that allows an application to perform
asynchronous signalling when a condition of interest occurs.
.. contents::
:local:
:depth: 2
Concepts
********
Any number of events can be defined. Each event is referenced by
its memory address.
An event has the following key properties:
* An **event handler**, which specifies the action to be taken
when the event is signalled. The action may instruct the system workqueue
to execute a function to process the event, mark the event as pending
so it can be later processed by a thread, or ignore the event.
* An **event pending flag**, which is set if the event is signalled
and an event handler function does not consume the signal.
An event must be initialized before it can be used. This establishes
its event handler and clears the event pending flag.
Event Lifecycle
===============
An ISR or a thread signals an event by **sending** the event
when a condition of interest occurs that cannot be handled by the detector.
Each time an event is sent, the kernel examines its event handler
to determine what action to take.
* :c:macro:`K_EVT_IGNORE` causes the event to be ignored.
* :c:macro:`K_EVT_DEFAULT` causes the event pending flag to be set.
* Any other value is assumed to be the address of an event handler function,
and is invoked by the system workqueue thread. If the function returns
zero, the signal is deemed to have been consumed; otherwise, the event
pending flag is set.
The kernel ensures that the event handler function is executed once
for each time an event is sent, even if the event is sent multiple times
in rapid succession.
An event whose event pending flag becomes set remains pending until
the event is accepted by a thread. This clears the event pending flag.
A thread accepts a pending event by **receiving** the event.
If the event's pending flag is currently clear, the thread may choose
to wait for the event to become pending.
Any number of threads may wait for a pending event simultaneously;
when the event is pended it is accepted by the highest priority thread
that has waited longest.
.. note::
A thread that accepts an event cannot directly determine how many times
the event pending flag was set since the event was last accepted.
Comparison to Unix-style Signals
================================
Zephyr events are somewhat akin to Unix-style signals, but have a number of
significant differences. The most notable of these are listed below:
* A Zephyr event cannot be blocked --- it is always delivered to its event
handler immediately.
* A Zephyr event pends *after* it has been delivered to its event handler,
and only if an event handler function does not consume the event.
* Zephyr has no pre-defined events or actions. All events are application
defined, and all have a default action that pends the event.
Implementation
**************
Defining an Event
=================
An event is defined using a variable of type :c:type:`struct k_event`.
It must then be initialized by calling :cpp:func:`k_event_init()`.
The following code defines and initializes an event.
.. code-block:: c
extern int my_event_handler(struct k_event *event);
struct k_event my_event;
k_event_init(&my_event, my_event_handler);
Alternatively, an event can be defined and initialized at compile time
by calling :c:macro:`K_EVENT_DEFINE()`.
The following code has the same effect as the code segment above.
.. code-block:: c
extern int my_event_handler(struct k_event *event);
K_EVENT_DEFINE(my_event, my_event_handler);
Signaling an Event
==================
An event is signalled by calling :cpp:func:`k_event_send()`.
The following code illustrates how an ISR can signal an event
to indicate that a key press has occurred.
.. code-block:: c
extern int my_event_handler(struct k_event *event);
K_EVENT_DEFINE(my_event, my_event_handler);
void keypress_interrupt_handler(void *arg)
{
...
k_event_send(&my_event);
...
}
Handling an Event
=================
An event handler function is used when a signalled event should not be ignored
or immediately pended. It has the following form:
.. code-block:: c
int <function_name>(struct k_event *event)
{
/* catch the event signal; return zero if the signal is consumed, */
/* or non-zero to let the event pend */
...
}
The following code illustrates an event handler function that processes
key presses detected by an ISR (as shown in the previous section).
.. code-block:: c
int my_event_handler(struct k_event *event_id_is_unused)
{
/* determine what key was pressed */
char c = get_keypress();
/* do complex processing of the keystroke */
...
/* signalled event has been consumed */
return 0;
}
Accepting an Event
==================
A pending event is accepted by a thread by calling :cpp:func:`k_event_recv()`.
The following code is an alternative to the code in the previous section.
It uses a dedicated thread to do very complex processing
of key presses that would otherwise monopolize the system workqueue.
The event handler function is now used only to filter out unwanted key press
events, allowing the dedicated thread to wake up and process key press events
only when a numeric key is pressed.
.. code-block:: c
int my_event_handler(struct k_event *event_id_is_unused)
{
/* determine what key was pressed */
char c = get_keypress();
/* signal thread only if key pressed was a digit */
if ((c >= '0') && (c <= '9')) {
/* save key press information */
...
/* signalled event should be pended */
return 1;
} else {
/* signalled event has been consumed */
return 0;
}
}
void keypress_thread(void *unused1, void *unused2, void *unused3)
{
/* consume numeric key presses */
while (1) {
/* wait for a key press event to pend */
k_event_recv(&my_event, K_FOREVER);
/* process saved key press, which must be a digit */
...
}
}
Suggested Uses
**************
Use an event to minimize ISR processing by deferring interrupt-related
work to a thread to reduce the amount of time interrupts are locked.
Use an event to allow the kernel's system workqueue to handle an event,
rather than defining an application thread to handle it.
Use an event to allow the kernel's system workqueue to pre-process an event,
prior to letting an application thread handle it.
Configuration Options
*********************
Related configuration options:
* None.
APIs
****
The following event APIs are provided by :file:`kernel.h`:
* :cpp:func:`k_event_handler_set()`
* :cpp:func:`k_event_send()`
* :cpp:func:`k_event_recv()`

View file

@ -12,4 +12,4 @@ of different threads, or the operation of an ISR and a thread.
semaphores.rst
semaphore_groups.rst
mutexes.rst
events.rst
alerts.rst

View file

@ -125,23 +125,23 @@ Using a Timer Expiry Function
The following code uses a timer to perform a non-trivial action on a periodic
basis. Since the required work cannot be done at interrupt level,
the timer's expiry function uses a :ref:`kernel event object <events_v2>`
the timer's expiry function uses a :ref:`kernel alert object <alerts_v2>`
to do the work in the context of the system workqueue.
.. code-block:: c
int my_event_handler(struct k_event *dummy)
int my_alert_handler(struct k_alert *dummy)
{
/* do the processing that needs to be done periodically */
...
return 0;
}
K_EVENT_DEFINE(my_event, my_event_handler);
K_ALERT_DEFINE(my_alert, my_alert_handler);
void my_timer_handler(struct k_timer *dummy)
{
k_event_send(&my_event);
k_alert_send(&my_alert);
}
K_TIMER_DEFINE(my_timer, my_timer_handler, NULL);

View file

@ -81,7 +81,7 @@ typedef sys_dlist_t _wait_q_t;
struct tcs;
struct k_mutex;
struct k_sem;
struct k_event;
struct k_alert;
struct k_msgq;
struct k_mbox;
struct k_pipe;
@ -923,41 +923,41 @@ extern void k_sem_group_reset(struct k_sem *sem_array[]);
struct k_sem name = \
K_SEM_INITIALIZER(name, initial_count, count_limit)
/* events */
/* alerts */
#define K_EVT_DEFAULT NULL
#define K_EVT_IGNORE ((void *)(-1))
#define K_ALERT_DEFAULT NULL
#define K_ALERT_IGNORE ((void *)(-1))
typedef int (*k_event_handler_t)(struct k_event *);
typedef int (*k_alert_handler_t)(struct k_alert *);
struct k_event {
k_event_handler_t handler;
struct k_alert {
k_alert_handler_t handler;
atomic_t send_count;
struct k_work work_item;
struct k_sem sem;
_DEBUG_TRACING_KERNEL_OBJECTS_NEXT_PTR(k_event);
_DEBUG_TRACING_KERNEL_OBJECTS_NEXT_PTR(k_alert);
};
extern void _k_event_deliver(struct k_work *work);
extern void _alert_deliver(struct k_work *work);
#define K_EVENT_INITIALIZER(obj, event_handler) \
#define K_ALERT_INITIALIZER(obj, alert_handler) \
{ \
.handler = (k_event_handler_t)event_handler, \
.handler = (k_alert_handler_t)alert_handler, \
.send_count = ATOMIC_INIT(0), \
.work_item = K_WORK_INITIALIZER(_k_event_deliver), \
.work_item = K_WORK_INITIALIZER(_alert_deliver), \
.sem = K_SEM_INITIALIZER(obj.sem, 0, 1), \
_DEBUG_TRACING_KERNEL_OBJECTS_INIT \
}
#define K_EVENT_DEFINE(name, event_handler) \
struct k_event name \
__in_section(_k_event_list, event, name) = \
K_EVENT_INITIALIZER(name, event_handler)
#define K_ALERT_DEFINE(name, alert_handler) \
struct k_alert name \
__in_section(_k_event_list, alert, name) = \
K_ALERT_INITIALIZER(name, alert_handler)
extern void k_event_init(struct k_event *event, k_event_handler_t handler);
extern int k_event_recv(struct k_event *event, int32_t timeout);
extern void k_event_send(struct k_event *event);
extern void k_alert_init(struct k_alert *alert, k_alert_handler_t handler);
extern int k_alert_recv(struct k_alert *alert, int32_t timeout);
extern void k_alert_send(struct k_alert *alert);
/**
* data transfers (complex)

View file

@ -360,7 +360,7 @@ static inline int nano_delayed_work_submit_to_queue(struct nano_workqueue *wq,
/* events */
#define kevent_t const struct k_event *
#define kevent_t const struct k_alert *
typedef int (*kevent_handler_t)(int event);
#define isr_event_send task_event_send
@ -369,32 +369,32 @@ typedef int (*kevent_handler_t)(int event);
static inline int task_event_handler_set(kevent_t legacy_event,
kevent_handler_t handler)
{
struct k_event *event = (struct k_event *)legacy_event;
struct k_alert *alert = (struct k_alert *)legacy_event;
if ((event->handler != NULL) && (handler != NULL)) {
if ((alert->handler != NULL) && (handler != NULL)) {
/* can't overwrite an existing event handler */
return RC_FAIL;
}
event->handler = (k_event_handler_t)handler;
alert->handler = (k_alert_handler_t)handler;
return RC_OK;
}
static inline int task_event_send(kevent_t legacy_event)
{
k_event_send((struct k_event *)legacy_event);
k_alert_send((struct k_alert *)legacy_event);
return RC_OK;
}
static inline int task_event_recv(kevent_t legacy_event, int32_t timeout)
{
return _error_to_rc(k_event_recv((struct k_event *)legacy_event,
return _error_to_rc(k_alert_recv((struct k_alert *)legacy_event,
_ticks_to_ms(timeout)));
}
#define DEFINE_EVENT(name, event_handler) \
K_EVENT_DEFINE(_k_event_obj_##name, event_handler); \
struct k_event * const name = &(_k_event_obj_##name)
K_ALERT_DEFINE(_k_event_obj_##name, event_handler); \
struct k_alert * const name = &(_k_event_obj_##name)
/* memory maps */

View file

@ -26,7 +26,7 @@ lib-y += $(strip \
msg_q.o \
mailbox.o \
mem_pool.o \
event.o \
alert.o \
pipes.o \
legacy_offload.o \
errno.o \

78
kernel/unified/alert.c Normal file
View file

@ -0,0 +1,78 @@
/*
* Copyright (c) 2016 Wind River Systems, Inc.
*
* 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.
*/
/**
* @file
* @brief kernel alerts.
*/
#include <kernel.h>
#include <nano_private.h>
#include <misc/debug/object_tracing_common.h>
#include <atomic.h>
#include <toolchain.h>
#include <sections.h>
void _alert_deliver(struct k_work *work)
{
struct k_alert *alert = CONTAINER_OF(work, struct k_alert, work_item);
while (1) {
if ((alert->handler)(alert) == 0) {
/* do nothing -- handler has processed the alert */
} else {
/* pend the alert */
k_sem_give(&alert->sem);
}
if (atomic_dec(&alert->send_count) == 1) {
/* have finished delivering alerts */
break;
}
}
}
void k_alert_init(struct k_alert *alert, k_alert_handler_t handler)
{
const struct k_work my_work_item = { NULL, _alert_deliver, { 1 } };
alert->handler = handler;
alert->send_count = ATOMIC_INIT(0);
alert->work_item = my_work_item;
k_sem_init(&alert->sem, 0, 1);
SYS_TRACING_OBJ_INIT(micro_event, alert);
}
void k_alert_send(struct k_alert *alert)
{
if (alert->handler == K_ALERT_IGNORE) {
/* ignore the alert */
} else if (alert->handler == K_ALERT_DEFAULT) {
/* pend the alert */
k_sem_give(&alert->sem);
} else {
/* deliver the alert */
if (atomic_inc(&alert->send_count) == 0) {
/* add alert's work item to system work queue */
k_work_submit_to_queue(&k_sys_work_q,
&alert->work_item);
}
}
}
int k_alert_recv(struct k_alert *alert, int32_t timeout)
{
return k_sem_take(&alert->sem, timeout);
}

View file

@ -1,78 +0,0 @@
/*
* Copyright (c) 2016 Wind River Systems, Inc.
*
* 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.
*/
/**
* @file
* @brief kernel events.
*/
#include <kernel.h>
#include <nano_private.h>
#include <misc/debug/object_tracing_common.h>
#include <atomic.h>
#include <toolchain.h>
#include <sections.h>
void _k_event_deliver(struct k_work *work)
{
struct k_event *event = CONTAINER_OF(work, struct k_event, work_item);
while (1) {
if ((event->handler)(event) == 0) {
/* do nothing -- handler has processed the event */
} else {
/* pend the event */
k_sem_give(&event->sem);
}
if (atomic_dec(&event->send_count) == 1) {
/* have finished delivering events */
break;
}
}
}
void k_event_init(struct k_event *event, k_event_handler_t handler)
{
const struct k_work my_work_item = { NULL, _k_event_deliver, { 1 } };
event->handler = handler;
event->send_count = ATOMIC_INIT(0);
event->work_item = my_work_item;
k_sem_init(&event->sem, 0, 1);
SYS_TRACING_OBJ_INIT(event, event);
}
void k_event_send(struct k_event *event)
{
if (event->handler == K_EVT_IGNORE) {
/* ignore the event */
} else if (event->handler == K_EVT_DEFAULT) {
/* pend the event */
k_sem_give(&event->sem);
} else {
/* deliver the event */
if (atomic_inc(&event->send_count) == 0) {
/* add event's work item to system work queue */
k_work_submit_to_queue(&k_sys_work_q,
&event->work_item);
}
}
}
int k_event_recv(struct k_event *event, int32_t timeout)
{
return k_sem_take(&event->sem, timeout);
}

View file

@ -583,7 +583,7 @@ def kernel_main_c_events():
if kernel_type == 'micro':
event_type = 'int'
else:
event_type = 'struct k_event *'
event_type = 'struct k_alert *'
# event descriptors
@ -608,7 +608,7 @@ def kernel_main_c_events():
if kernel_type == 'micro':
kernel_main_c_out("DEFINE_EVENT(%s, %s);\n" % (event[0], event[1]))
else:
kernel_main_c_out("K_EVENT_DEFINE(_k_event_obj_%s, %s);\n" %
kernel_main_c_out("K_ALERT_DEFINE(_k_event_obj_%s, %s);\n" %
(event[0], event[1]))
def kernel_main_c_mutexes():
@ -1132,7 +1132,7 @@ def generate_sysgen_h_obj_ids():
fifo_type = 'struct k_msgq *'
mbox_struct = 'k_mbox'
mbox_type = 'struct k_mbox *'
event_type = 'struct k_event *'
event_type = 'struct k_alert *'
mem_pool_type = 'struct k_mem_pool'
# add missing object types
@ -1221,7 +1221,7 @@ def generate_sysgen_h_obj_ids():
sysgen_h_data += "extern const %s %s;\n" % (event_type, name)
elif (kernel_type == 'unified'):
sysgen_h_data += \
"extern struct k_event _k_event_obj_%s;\n" % (name)
"extern struct k_alert _k_event_obj_%s;\n" % (name)
sysgen_h_data += \
"#define %s (&_k_event_obj_%s)\n\n" % (name, name)