Skip to content
GeMRTOS
GeMRTOS

The Generic eMbedded Multiprocessor RTOS

  • Home
  • Download Now!
  • GeMRTOS
    • License
    • Download
    • GeMRTOS Documentation
      • Documentation Browser
      • GeMRTOS Manuals
    • GeMRTOS repository
  • Log in
  • Contact us
Search
GeMRTOS
GeMRTOS

The Generic eMbedded Multiprocessor RTOS

  • Download Now!!!
  • GeMRTOS
    • License
    • Download now!
    • GeMRTOS documentation
    • GeMRTOS repository
  • Login
  • Contact us
  • FPGA Laboratory ACCESS
  • Cursos de FPGA (en español)
  • Challenges
GeMRTOS
GeMRTOS

The Generic eMbedded Multiprocessor RTOS

  • Home
  • Download Now!
  • GeMRTOS
    • License
    • Download
    • GeMRTOS Documentation
      • Documentation Browser
      • GeMRTOS Manuals
    • GeMRTOS repository
  • Log in
  • Contact us
Search
GeMRTOS
GeMRTOS

The Generic eMbedded Multiprocessor RTOS

  • Download Now!!!
  • GeMRTOS
    • License
    • Download now!
    • GeMRTOS documentation
    • GeMRTOS repository
  • Login
  • Contact us
  • FPGA Laboratory ACCESS
  • Cursos de FPGA (en español)
  • Challenges
  • Home
  • GeMRTOS KnowledgeBase
  • GeMRTOS
  • Digital design
  • GeMRTOS Mutex and Critical Sections in Multiprocessor RTOS

GeMRTOS Mutex and Critical Sections in Multiprocessor RTOS

In GeMRTOS multiprocessor RTOS applications running on Altera FPGA platforms with Nios V processors, synchronization is one of the most critical design considerations. When multiple processors execute tasks concurrently, shared resources must be carefully protected to prevent race conditions and data corruption. GeMRTOS provides four mechanisms for this purpose: semaphores, critical sections, a hardware mutex, and scheduling list exclusion. This guide explains how each mechanism works, where it applies, and how to choose the right tool for your multiprocessor RTOS application.

Race condition example in multiprocessor RTOS showing shared variable access by concurrent tasks

Why Synchronization Matters in Multiprocessor RTOS #

In real-time operating systems (RTOS), synchronization mechanisms, critical sections, and mutexes play a crucial role in managing shared resources and ensuring task safety. Consider a system where two or more tasks execute the following code to count executions, resetting the counter to 0 when it reaches 1000:

if (a == 1000)
	a = 0;
else
	a = a + 1;

The variable a is shared among multiple tasks and must have synchronized access. Without synchronization, a critical race condition can occur: tasks 1 and 2 both check that a == 999 and both increment it, potentially pushing it to 1001 — beyond its intended range. All future invocations will then operate on an out-of-range value.

NOTE: Depending on how the code is compiled and the volatility of variable a, it may end up with the value 1000 or 1001. Semaphores are proposed to synchronize access to shared variables, along with critical sections in uniprocessor systems.

Semaphores: Task-Level Synchronization #

A semaphore is a synchronization primitive used to control access to a shared resource by multiple tasks. It maintains a count to track the number of available resources. GeMRTOS provides gu_SemaphoreTake() and gu_SemaphoreGive() to acquire and release resources respectively — see the companion Semaphores API article for the full creation/take/give reference.

Access to variable a from the previous example can be synchronized using a semaphore as follows:

gu_SemaphoreTake(sem_a, G_LATEST_TIME);
if (a == 1000)
	a = 0;
else
	a = a + 1;
gu_SemaphoreGive(sem_a);

The operation <strong>gu_SemaphoreTake(sem_a, G_LATEST_TIME)</strong> ensures subsequent operations execute only after the task is granted semaphore sem_a. The <strong>G_LATEST_TIME</strong> argument means “wait indefinitely” — a blocking wait. Access is released at the end using <strong>gu_SemaphoreGive(sem_a)</strong>.

NOTE 1: A semaphore ensures no other task accesses the shared resource while it is granted, but it does not prevent the holding task from being preempted by a higher-priority task mid-execution.

NOTE 2: In uniprocessor systems, critical sections are used as synchronization mechanisms, but this approach is not valid in multiprocessor systems.

Critical Sections: Uniprocessor Systems Only #

A critical section is a portion of code that must execute atomically to prevent race conditions and data corruption. In uniprocessor RTOSs, critical sections are implemented by disabling processor interrupts. With only one processor, disabling interrupts forces atomic execution of the code section:

ENTER_CRITICAL_SECTION; // disable processor interrupts
if (a == 1000)
	a = 0;
else
	a = a + 1;
EXIT_CRITICAL_SECTION;  // enable processor interrupts

In a uniprocessor system this mechanism provides both atomicity and task exclusion — no other task can run until the critical section exits and interrupts are re-enabled.

In a multiprocessor system, disabling interrupts achieves atomicity on a single processor but does not prevent race conditions across processors. Consider this code intended to let only the processor identified by GRTOS_CMD_PRC_ID execute the subsequent block:

while (a != GRTOS_CMD_PRC_ID) {
	if (a == 0) a = GRTOS_CMD_PRC_ID;
}
// Next code is executed by processor GRTOS_CMD_PRC_ID
…
a = 0;  // end code execution

If two processors execute this concurrently, both may find a == 0 simultaneously and both write their IDs — causing the subsequent block to execute on two processors at once. Adding a critical section alone does not prevent this.

Using a semaphore addresses the race, but introduces a new edge case:

while (a != GRTOS_CMD_PRC_ID) {
 gu_SemaphoreTake(sem_a, G_LATEST_TIME);
	if (a == 0) a = GRTOS_CMD_PRC_ID;
 else {
   gu_SemaphoreGive(sem_a);
 }
}
// Next code is executed by processor GRTOS_CMD_PRC_ID
// granting semaphore sem_a
…
a = 0;
gu_SemaphoreGive(sem_a); // end code execution

The semaphore synchronizes the comparison and assignment, but does not guarantee atomic execution. The following sequence can still occur:

  1. A task executing on processor 1 acquires semaphore sem_a.
  2. It finds a == 0 and assigns a = 1 (processor 1’s ID).
  3. The task is preempted by a higher-priority task before releasing the semaphore.
  4. Execution resumes, but now on processor 2.
  5. Processor 2 releases the semaphore but enters an infinite loop because a == 1 does not match its own ID (processor 2).

Combining Semaphores and Critical Sections #

Disabling processor interrupts in a multiprocessor system does not guarantee exclusion and can disrupt OS services, potentially causing priority inversion or blocking the kernel. It is therefore essential to exit the critical section if the resource cannot be obtained. The following code combines semaphores and critical sections to prevent race conditions while achieving atomic execution without excessively blocking the system:

ENTER_CRITICAL_SECTION; // disable processor interrupts
while (a != GRTOS_CMD_PRC_ID) {
 gu_SemaphoreTake(sem_a, G_LATEST_TIME);
	if (a == 0) a = GRTOS_CMD_PRC_ID;
 else {
   gu_SemaphoreGive(sem_a);
   EXIT_CRITICAL_SECTION;  // enable processor interrupts
 }
 ENTER_CRITICAL_SECTION; // disable processor interrupts
}
// Next code is executed by processor GRTOS_CMD_PRC_ID
…
a = 0;
gu_SemaphoreGive(sem_a); // end code execution
EXIT_CRITICAL_SECTION;  // enable processor interrupts

This pattern distinguishes between the different mechanisms required in multiprocessor systems and highlights practices from uniprocessor systems that are not safe to carry over directly.

Mutex: Hardware-Enforced Mutual Exclusion #

A mutex (mutual exclusion) is a synchronization primitive that grants exclusive access to a shared resource to exactly one task at a time. Unlike counting semaphores, a mutex guarantees both synchronization and atomicity, and GeMRTOS builds it on the same semaphore resource type — created with gu_SemaphoreCreateMutex() or gu_SemaphoreCreateRecursiveMutex(), then locked/unlocked with the same gu_SemaphoreTake()/gu_SemaphoreGive() calls covered above. The two fundamental mutex operations are:

  1. Locking (gu_SemaphoreTake) — acquires exclusive access to the shared resource. If the mutex is already held, the requesting task blocks (up to the given timeout) until it becomes available.
  2. Unlocking (gu_SemaphoreGive) — releases the mutex, allowing other tasks to acquire it.

Separately from this per-resource mutex, GeMRTOS also implements a single, system-wide hardware mutex that protects all access to GeMRTOS’s own internal data structures and kernel code, through two macros:

  • gm_GeMRTOSCriticalSectionEnter — locks the GeMRTOS kernel mutex.
  • gm_GeMRTOSCriticalSectionExit — unlocks the GeMRTOS kernel mutex.

This kernel mutex can also be used when calling unsafe multiprocessor functions such as newlib functions.

NOTE: When the GeMRTOS kernel mutex is locked in a user task, all GeMRTOS kernel functions are suspended until the mutex is released. Avoid holding it while accessing slow I/O devices.

Scheduling List Exclusion #

GeMRTOS introduces a novel mechanism called scheduling list exclusion. Each scheduling list (LCB) includes an exclusion parameter, set with gu_SchedulingListExclusionSet(), that sets the maximum number of tasks from that list that may execute simultaneously across all processors.

Setting the exclusion parameter to 1 forces all tasks assigned to that list to be scheduled as if running on a uniprocessor system. Critical sections within those tasks then execute atomically, effectively preventing all race conditions among tasks sharing the same scheduling list — without requiring explicit semaphore or mutex calls in application code.

Key Takeaways #

  • In GeMRTOS on Nios V FPGA platforms, semaphores (gu_SemaphoreTake / gu_SemaphoreGive) provide task-level synchronization for shared resources but do not prevent task preemption mid-execution.
  • Critical sections (interrupt disable) enforce atomicity in uniprocessor RTOS only — in multiprocessor systems they do not prevent concurrent access by other processors.
  • When both atomicity and synchronization are needed in a multiprocessor RTOS, semaphores and critical sections must be combined carefully, with the critical section exited immediately if the resource is unavailable.
  • Mutexes (gu_SemaphoreCreateMutex / CreateRecursiveMutex, taken/given the same way as semaphores) provide exclusive single-task access built on the same resource type as semaphores — not a separate primitive.
  • The GeMRTOS kernel mutex (gm_GeMRTOSCriticalSectionEnter / Exit) protects the kernel’s own internal state and suspends all kernel functions while held — avoid using it with slow I/O.
  • Scheduling list exclusion (gu_SchedulingListExclusionSet, exclusion = 1) is the most elegant solution: it transparently serializes all tasks in a list as if they ran on a single processor, eliminating the need for explicit synchronization primitives.
GeMRTOS, Nios 2, Nios V
Share This Article :
  • Facebook
  • Twitter
  • LinkedIn
  • Pinterest
Still stuck? How can we help?

How can we help?

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Table of Contents
  • Why Synchronization Matters in Multiprocessor RTOS
  • Semaphores: Task-Level Synchronization
  • Critical Sections: Uniprocessor Systems Only
  • Combining Semaphores and Critical Sections
  • Mutex: Hardware-Enforced Mutual Exclusion
  • Scheduling List Exclusion
  • Key Takeaways

Copyright © 2026 - contact us - Dorrego 287 - B8000FLE - Bahía Blanca - Argentina - +542914311867  GeMRTOS

Nios II, Nios V, Quartus Prime, ModelsSim are property of their respective companies.
GeMRTOS is property of R. Cayssials.
**All contact forms on this site are protected by reCAPTCHA. Google Privacy

GeMRTOS joins Intel Partner Alliance as Gold member.