Event sets are a versatile way to communicate between tasks, manage resource allocation, and synchronize tasks.

An event set is a means to synchronize tasks with other tasks and with interrupt service routines. An event set contains a set of events (one per bit), and tasks can wait for one or more of these bits to be set (i.e., to have the value 1). When a task waits on an event set, the events it is waiting for are matched against the current values—if they match, the task can still execute. If they don't match, the task is put on the task list with details about the event set and the events for which the task is waiting.

You allocate an event set by declaring it as C variable:

CTL_EVENT_SET_t e1;

A CTL_EVENT_SET_t is a synonym for an unsigned type. Thus, when an unsigned is 16 bits wide, an event set will contain 16 events; and when it consists of 32 bits, an event set will contain 32 events.

An event set must be initialized before any tasks can use it. To initialize an event set, use ctl_events_init:

ctl_events_init(&e1, 0);

You can set and clear events in an event set using the ctl_events_set_clear function.

ctl_events_set_clear(&e1, 1<<0, 1<<15);

This example will set the bit-zero event and clear the bit-15 event. If any tasks are waiting on this event set, the events they are waiting on will be matched against the new event set value, which could cause the task to become runnable.

You can wait for events to be set using ctl_events_wait. You can wait for any of the events in an event set to be set (CTL_EVENT_WAIT_ANY_EVENTS) or all of the events to be set (CTL_EVENT_WAIT_ALL_EVENTS). You can also specify that when events have been set and have been matched that they should be automatically reset (CTL_EVENT_WAIT_ANY_EVENTS_WITH_AUTO_CLEAR and CTL_EVENT_WAIT_ALL_EVENTS_WITH_AUTO_CLEAR). You can associate a timeout with a wait for an event set to stop your application blocking indefinitely.

ctl_events_wait(CTL_EVENT_WAIT_ANY_EVENTS,
                &e1, 1<<15,
                CTL_TIMEOUT_NONE, 0);

This example waits for bit 15 of the event set pointed to by e1 to become set.

if (ctl_events_wait(CTL_EVENT_WAIT_ANY_EVENTS,
                    &e1, 1<<15,
                    CTL_TIMEOUT_DELAY, 1000) == 0)
  {
    // ...timeout occurred
  }

This example uses a timeout and tests the return result to see if the timeout occurred.

You can use ctl_events_pulse to set and immediately clear events. A typical use for this would be to wake up multiple threads and reset the events atomically.

Synchronizing with an ISR

The following example illustrates synchronizing a task with a function called from an ISR.

CTL_EVENT_SET_t e1;
CTL_TASK_s t1;

void ISRfn()
{
  // ...do work, and then...
  ctl_events_set_clear(&e1, 1<<0, 0);
}

void task1(void *p)
{
  for (;;)
    {
      ctl_events_wait(CTL_EVENT_WAIT_ANY_EVENTS,
                      &e1, 1<<0,
                      CTL_TIMEOUT_NONE, 0);
      //
      // ...do whatever needs to be done...
      //
      ctl_events_set_clear(&e1, 0, 1<<0);
    }
}

Synchronizing with more than one ISR

The following example illustrates synchronizing a task with functions called from two interrupt service routines.

CTL_EVENT_SET_t e1;
CTL_TASK_s t1;

void ISRfn1(void)
{
  // do work, and then...
  ctl_events_set_clear(&e1, 1<<0, 0);
}

void ISRfn2(void)
{
  // do work, and then...
  ctl_events_set_clear(&e1, 1<<1, 0);
}

void task1(void *p)
{
  for (;;)
    {
      unsigned e;
      e = ctl_events_wait(CTL_EVENT_WAIT_ANY_EVENTS_WITH_AUTO_CLEAR,
                          &e1, (1<<0) | (1<<1),
                          CTL_TIMEOUT_NONE, 0);
      if (e & (1<<0))
        {
          // ISRfn1 completed
        }
      else if (e & (1<<1))
        {
          // ISRfn2 completed
        }
      else
        {
          // error
        }
    }
}

Resource serialization with an event set

The following example illustrates resource serialization of two tasks.

CTL_EVENT_SET_t e1;

void task1(void)
{
  for (;;)
    {
      // Acquire resource.
      ctl_events_wait(CTL_EVENT_WAIT_ANY_EVENTS_WITH_AUTO_CLEAR,
                      &e1, 1<<0,
                      CTL_TIMEOUT_NONE, 0);
      // Resource is now been acquired.

      ⁞

      // Release acquired resource.
      ctl_events_set_clear(&e1, 1<<0, 0);
      // Resource is now released.
    }
}

void task2(void)
{
  for (;;)
    {
      // Acquire resource.
      ctl_events_wait(CTL_EVENT_WAIT_ANY_EVENTS_WITH_AUTO_CLEAR,
                      &e1, 1<<0,
                      CTL_TIMEOUT_NONE, 0);
      // Resource is now acquired.

      ⁞
      
      // Release acquired resource.
      ctl_events_set_clear(&e1, 1<<0, 0);
      // Resource is now been released.
    }
}

void main(void)
{
  // Initialize event set.
  ctl_events_init(&e1, 1<<0);
  // Create tasks and let them run.
}

Note that e1 is initialized with the event set; without this, neither task would acquire the resource.