Blocking and non-blocking RTOS APIs

The primary purpose of a real-time operating system (RTOS) is to manage CPU time, distribute it between a number of tasks, and provide services to enable management of other resources such as peripherals. These functions are achieved in a variety of ways, but in most cases there is an opportunity for tasks to give a “hint” that they are able to relinquish the CPU for a while. Blocking system calls are one way to accomplish this. This article reviews how such calls work and when and how they are used.

API calls
At the heart of an RTOS is the kernel, which comprises the task scheduler and a number of services available to be called by application programs. Control of the scheduler and access to these services is by means of the kernel’s application program interface (API). APIs differ from one RTOS to another, although there are some standards, like POSIX, but some characteristics are common to many RTOSes. One of those similarities is the function of blocking and non-blocking calls.

Non-blocking calls
A program may make an API call to request a specific resource or service. Such a call may normally return with the required result and/or a pointer to the requested resources. There may also be the possibility for an error return. But what if the call is valid but the resource or service cannot be provided at this time? An example illustrates the possibilities.

Using Nucleus RTOS, a semaphore may be obtained by making a call of this form:

   STATUS NU_Obtain_Semaphore(NU_SEMAPHORE *semaphore, UNSIGNED suspend)

Like this:
   NU_SEMAPHORE my_semaphore;
   STATUS my_status;
   my_status = NU_Obtain_Semaphore(&my_semaphore, NU_NO_SUSPEND);

The return value, my_status , may have a variety of possible values, many of which indicate error conditions, but the likely ones are NU_SUCCESS , which means that the semaphore was successfully obtained, or NU_UNAVAILABLE , which means that the semaphore could not be obtained. Typically, a semaphore is used to lock a resource and a task needs to wait until the resource is available; i.e. the semaphore can be obtained. This could be achieved thus:

   do
   {
      my_status = NU_Obtain_Semaphore(&my_semaphore, NU_NO_SUSPEND);
   } while (my_status == NU_UNAVAILABLE);

So the code just sits in a loop, repeatedly trying to obtain the semaphore. (This code ignores the other possible error responses.) This is not good practice. It makes much more sense for the code to relinquish the CPU for a while after each try, thus:

   do
   {
     my_status = NU_Obtain_Semaphore(&my_semaphore, NU_NO_SUSPEND);
     if (my_status == NU_UNAVAILABLE)
       NU_Sleep(20);
   } while (my_status == NU_UNAVAILABLE);

or like this:

   while (TRUE)
   {
     my_status = NU_Obtain_Semaphore(&my_semaphore, NU_NO_SUSPEND);
     if (my_status == NU_SUCCESS)
       break;
     NU_Sleep(20);
   }

Here the task waits for 20 timer ticks before trying again. Both ways of writing it are likely to result in the same compiled code.

Blocking calls
Instead of a task needing to manage the waitingprocess, when an API call returns an “unavailable” response, an RTOStypically can handle everything. Again, using the example of a Nucleussemaphore:

   my_status = NU_Obtain_Semaphore(&my_semaphore, NU_SUSPEND);

In this case, as the suspend mode is set to NU_SUSPEND ,the API call will not return until the semaphore can be obtained. Inthe meantime, the task is suspended or “blocked”. When the circumstanceschange and the resource is available, the task is woken up; i.e. it ismade ready to be scheduled according to its priority, which could beimmediately.

Timeout
Some RTOSes, like Nucleus, offer greater flexibility with a further suspend option. Instead of choosing NU_SUSPEND or NU_NO_SUSPEND , a timeout value may be specified, thus:

   my_status = NU_Obtain_Semaphore(&my_semaphore, 20);

ThisAPI call will return when the semaphore is obtained or after 20 clockticks, whichever occurs first. If the return occurs after the timeout, my_status will have the value NU_TIMEOUT .

Multiple blocked tasks
Whatif several tasks are blocked pending the availability of a resource,which then becomes available? Which task is woken up to have its requestsatisfied? This depends upon the specific RTOS. Typically tasks areeither woken in priority order (i.e. a higher priority task will bewoken first) or they are woken in the order in which they suspended(“first in, first out”- FIFO). This may be a kernel configuration choiceor may be selectable on a per-object basis.

With Nucleus, RTOSobjects are created dynamically and the blocking behavior of each objectis determined by a parameter in the creation call, which takes thisform:

   STATUS NU_Create_Semaphore(NU_SEMAPHORE *semaphore, CHAR *name,   UNSIGNED initial_count, OPTION suspend_type);

The last parameter determines how blocked tasks are woken up; the options are priority (NU_PRIORITY ) or FIFO (NU_FIFO ) order.

To block or not to block
Theoption to make blocking API calls simplifies the application code, asthe RTOS takes the full responsibility for managing the CPU timeutilization, which is, after all, its primary raison d’être . In the caseof Nucleus RTOS, the API is orthogonal. Calls appertaining to numeroustypes of objects – for example, semaphores, mailboxes, memoryallocation, queues – all have blocking and non-blocking options.

Colin Walls has over thirty years of experience in the electronics industry,largely dedicated to embedded software. A frequent presenter atconferences and seminars, he is the author of numerous technicalarticles and the book Embedded Software, Second Edition: The Works. Colin is an embedded software technologist with Mentor Embedded [theMentor Graphics Embedded Software Division], and is based in the UK. Hisregular blog is located at blogs.mentor.com/colinwalls. He may be reached by email at .

1 thought on “Blocking and non-blocking RTOS APIs

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.