CTL provides semaphores to use for synchronization and resource allocation.

A semaphore is a counter which tasks can wait for to be non-zero. When a semaphore is non-zero and a task waits on it, the semaphore value is decremented and the task continues executing. When a semaphore is zero and a task waits on it, the task will be suspended until the semaphore is signaled. When a semaphore is signaled and no tasks are waiting for it, the semaphore's value is incremented. When a semaphore is signaled and tasks are waiting, one of the tasks is made runnable.

You allocate a semaphore by declaring it as a C variable. For example:

CTL_SEMAPHORE_t s1;

A CTL_SEMAPHORE_t is a synonym for an unsigned type, so the maximum value of the counter is dependent upon the word size of the processor (16 or 32 bits).

A semaphore must be initialized done before any tasks can use it. To initialize a semaphore, use ctl_semaphore_init:

ctl_semaphore_init(&s1, 1);

To signal a semaphore, use ctl_semaphore_signal:

ctl_semaphore_signal(&s1);

The highest-priority task waiting on the semaphore pointed at by s1 will be made runnable by this call. If no tasks are waiting on the semaphore, the semaphore's value is incremented.

To wait for a semaphore with an optional timeout, use ctl_semaphore_wait:

ctl_semaphore_wait(&s1, CTL_TIMEOUT_NONE, 0);

This example will block the task if the semaphore is zero, otherwise it will decrement the semaphore and execution will continue.

if (ctl_semaphore_wait(&s1, CTL_TIMEOUT_ABSOLUTE, ctl_get_current_time()+1000) == 0)
  {
    // ...timeout occurred
  }

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

Task synchronization in an interrupt service routine

The following example illustrates synchronizing a task with a function called from an interrupt service routine.

CTL_SEMAPHORE_t s1;

void ISRfn()
{
  // Detected something, signal the waiting task.
  ctl_semaphore_signal(&s1);
}

void task1(void *p)
{
  for (;;)
    {
      // Wait for ISR to signal that an event happened.
      ctl_semaphore_wait(&s1, CTL_TIMEOUT_NONE, 0);
      // Deal with the event.
    }
}

Resource serialization with semaphore

The following example illustrates resource serialization of two tasks:

CTL_SEMAPHORE_t s1 = 1;

void task1(void)
{
  for (;;)
    {
      // Wait for resource.
      ctl_semaphore_wait(&s1, CTL_TIMEOUT_NONE, 0);
      // Resource has now been acquired, do something with it.
      
      ⁞

      // And now release it...
      ctl_semaphore_signal(&s1);
      // Resource is now released.
    }
}

void task2(void)
{
  for (;;)
    {
      ctl_semaphore_wait(&s1, CTL_TIMEOUT_NONE, 0);
      // Resource has now been acquired, do something with it.

      ⁞
      
      // And now release it...
      ctl_semaphore_signal(&s1);
      // Resource has now been released.
    }
}

int main(void)
{
  // Initialize semaphore.
  ctl_semaphore_init(&s1, 1);
}

Note that s1 is initialized to one; without this, neither task would acquire the resource.