Events¶
Overview¶
MiraOS is depending on an event-driven execution model. Each process typically executes small chunks of instructions before telling the scheduler that they are waiting for an event, and thereby handing control back to the scheduler.
Events can be for instance expiration of an event timer, an incoming network packet, or a user defined event.
Waiting for events¶
It's only possible to wait for events in the part of a process definition that is between the
PROCESS_BEGIN()
and PROCESS_END()
statements. Waiting for an event is done by calling the
PROCESS_WAIT_EVENT()
or PROCESS_WAIT_EVENT_UNTIL()
system calls. Doing so yields the control back
to the scheduler, and execution is resumed only after an event has been sent to the process.
PROCESS_WAIT_EVENT_UNTIL()
also takes a statement as an argument, if this statement is evaluated to
FALSE
the process immediately yields back to the scheduler again waiting for the next event.
PROCESS_THREAD(test_hello, ev, data)
{
/* An event-timer variable. Note that this variable must be static
in order to preserve the value across yielding or waiting. */
static struct etimer et;
PROCESS_BEGIN();
while (1) {
etimer_set(&et, CLOCK_SECOND); /* Set the timer to 1 second. */
PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et));
printf("Hello, world!\n");
}
PROCESS_END();
}
System events¶
The following events are defined by the kernel.
Event | Description |
---|---|
PROCESS_EVENT_INIT | Delivered to a process when it is started. |
PROCESS_EVENT_POLL | Delivered to a process being polled. |
PROCESS_EVENT_EXIT | Delivered to an exiting a process. |
PROCESS_EVENT_CONTINUE | Delivered to a paused process when resuming execution. |
PROCESS_EVENT_EXITED | Delivered to all processes about an exited process. |
PROCESS_EVENT_TIMER | Delivered to a process when one of its timers expired. |
User-defined events¶
The application developer may also create new events that are not covered by the system-defined events.
The event variable must have an appropriate scope, so that all code using the event can access it.
static process_event_t my_event;
my_event = process_alloc_event();
Note that there is no support for deallocating events.
Asynchronous events¶
Posting an asynchronous event places the event on MiraOS kernel's event queue. Asynchronous events are delivered to the receiving process some time after they have been posted.
The events on the event queue are delivered to the receiving process by the kernel. The kernel loops through the event queue and delivers the events to the processes on the queue by invoking the processes.
The receiver of an asynchronous event can either be a specific process, or all running processes. When the receiver is a specific process, the kernel invokes this process to deliver the event. When an event is sent to all processes in the system, the kernel devlivers the event sequentially to all processes, one after another.
Asynchronous events are posted with the process_post()
call.
It first checks the size of the current event queue to determine if there is room for the event on the queue.
If not, the function returns an error. If there is room for the event on the queue, the function inserts the event at
the end of the event queue and returns.
Note
process_post()
isn't callable from interrupt service routines, instead use process_poll()
to wake up
a process.
Example¶
process_event_t my_event;
PROCESS(receiving_proc, "Receiving process");
PROCESS(sending_proc, "Sending process");
PROCESS_THREAD(receiving_proc, ev, data)
{
PROCESS_BEGIN();
while (1) {
PROCESS_WAIT_EVENT_UNTIL(ev == my_event);
printf("Got event\n");
}
PROCESS_END();
}
PROCESS_THREAD(sending_proc, ev, data)
{
static struct etimer timer;
PROCESS_BEGIN();
while (1) {
/* Sleep for three seconds */
etimer_set(&timer, CLOCK_SECOND * 3);
PROCESS_WAIT_UNTIL(etimer_expired(&timer));
/* Post event */
printf("Sending event\n");
process_post(&receiving_proc, my_event, NULL);
printf("Event sent\n");
}
PROCESS_END();
}
void start_sender_receiver(void)
{
my_event = process_alloc_event();
process_start(&sending_proc, NULL);
process_start(&receiving_proc, NULL);
}
Synchronous Events¶
Synchronous events are posted using the process_post_synch()
system call.
Unlike asynchronous events, synchronous events are delivered directly when they are posted. Synchronous events
can only be posted to a specific process.
Because synchronous events are delivered immediately, posting a synchronous event is functionally equivalent to a function call: the process to which the event is delivered is directly invoked, and the process that posted the event is blocked until the receiving process has finished processing the event.
The receiving process is, however, not informed whether the event was posted synchronously or asynchronously.
Note
process_post_synch()
isn't callable from interrupt service routines.
Use another way to inform the process about the event and wake it up with process_poll()
.
Polling processes¶
process_post()
is not allowed from interrupts. Therefore, to wake up
processes another method is needed.
To wake up a process from an interrupt process_poll()
is used. The process
will then wake.
However, using process_poll()
, no specific event or data can be passed to
the process. It is up to the process to check, or poll, what is updated to
decide what action the process should take.
Usually, a global variable is set in the interrupt that can be checked from the process.
Example¶
static volatile int got_irq_a = 0;
static volatile int got_irq_b = 0;
static process_event_t an_event; /* Should be set from process_alloc_event() */
PROCESS(receiving_proc, "Receiving process");
PROCESS_THREAD(receiving_proc, ev, data)
{
PROCESS_BEGIN();
while (1) {
/* Sleep until next event, regardless of reason */
PROCESS_YIELD();
if(got_irq_a) {
got_irq_a = 0;
printf("Got IRQ A\n");
}
if(got_irq_b) {
got_irq_b = 0;
printf("Got IRQ B\n");
}
if(ev == an_event) {
printf("Got an event\n");
}
}
PROCESS_END();
}
void isr_a(void)
{
got_irq_a = 1;
process_poll(&receiving_proc);
}
void isr_b(void)
{
got_irq_b = 1;
process_poll(&receiving_proc);
}