Main Content

Define Atomic Operations in Multitasking Code

In code with multiple threads, you can use Polyspace® Bug Finder™ to detect data races or Polyspace Code Prover™ to list potentially unprotected shared variables.

To determine if a variable shared between multiple threads is protected against concurrent access, Polyspace checks if the operations on the variable are atomic.

Nonatomic Operations

If an operation is nonatomic, Polyspace considers that the operation involves multiple steps. These steps do not need to occur together and can be interrupted by operations in other threads.

For instance, consider these two operations in two different threads:

  • Thread 1: var++;

    This operation is nonatomic because it takes place in three steps: reading var, incrementing var, and writing back var.

  • Thread 2: var = 0;

    This operation is atomic if the size of var is less than the word size on the target. See details below for how Polyspace determines the word size.

If the two operations are not protected (by using, for instance, critical sections), the operation in the second thread can interrupt the operation in the first thread. If the interruption happens after var is incremented in the first thread but before the incremented value is written back, you can see unexpected results.

What Polyspace Considers as Nonatomic

Code Prover considers all operations as nonatomic unless you protect them, for instance, by using critical sections. See Define Specific Operations as Atomic.

Bug Finder considers an operation as nonatomic if it can translate into more than one machine instruction. For instance:

  • The operation can involve both a read and write operation. For example, var++ involves reading the value of var, increasing the value by one and writing the increased value back to var.

  • The operation can involve a 64-bit variable on a 32-bit target. For example, the operation

    long long var1, var2;
    var1=var2;
    involves two steps in copying the content of var2 to var1 on certain targets.

    Polyspace uses the Pointer size for your Target processor type as the threshold to compute atomicity. For instance, if you use i386 as your Target processor type, the Pointer size is 32 bits and Long long and Double sizes are both 64 bits. Therefore, Polyspace considers copying one long long or double variable to another as nonatomic.

    See also Target processor type (-target).

  • The operation can involve writing the return value of a function call to a shared variable. For example, the operation x=func() involves calling func and writing the return value of func to x.

To detect data races where at least one of the two interrupting operations is nonatomic, enable the Bug Finder checker Data race. To remove this constraint on the checker, use the option -detect-atomic-data-race.

Define Specific Operations as Atomic

You might want to define a group of operations as atomic. This group of operations cannot be interrupted by operations in another thread or task.

Use one of these techniques:

  • Critical sections

    Protect a group of operations with critical sections.

    A critical section begins and ends with calls to specific functions. You can use a predefined set of primitives to begin or end critical sections, or use your own functions.

    A group of operations in a critical section are atomic with respect to another group of operations that are in the same critical section (that is, having the same beginning and ending function).

    Specify critical sections using the option Critical section details (-critical-section-begin -critical-section-end).

  • Temporally exclusive tasks

    Protect a group of operations by specifying certain tasks as temporally exclusive.

    If a group of tasks are temporally exclusive, all operations in one task are atomic with respect to operations in the other tasks.

    Specify temporal exclusion using the option Temporally exclusive tasks (-temporal-exclusions-file).

  • Task priorities

    Protect a group of operations by specifying that certain tasks have higher priorities. For instance, interrupts have higher priorities over cyclic tasks.

    You can specify up to four different priorities with these options (with highest priority listed first):

    All operations in a task with higher priority are atomic with respect to operations in tasks with lower priorities. See also Define Task Priorities for Data Race Detection in Polyspace.

  • Routine disabling interrupts (Bug Finder only)

    Protect a group of operations by disabling all interrupts. Use the option Disabling all interrupts (-routine-disable-interrupts -routine-enable-interrupts).

    After you call a routine to disable interrupts, all subsequent operations are atomic until you call another routine to reenable interrupts. The operations are atomic with respect to operations in all other tasks.

For a tutorial, see Protections for Shared Variables in Multitasking Code.

See Also

| | |

Related Topics