Main Content

MISRA C:2023 Dir 5.1

There shall be no data races between threads

Since R2024b

Description

This checker is deactivated in a default Polyspace® as You Code analysis. See Checkers Deactivated in Polyspace as You Code Analysis (Polyspace Access).

Directive Definition

There shall be no data races between threads.

Rationale

A data race occurs when all of these conditions are true:

  • Two actions on different threads attempt to read or modify the same memory location.

  • At least one of the actions is nonatomic.

  • The order of these operations is not fixed.

In this code, the variable data is modified by the functions foo() and bar(), which are on separate threads. The order of execution for these threads is not specified and none of the actions is atomic. Both threads can attempt to modify data at the same time, resulting in a data race. Such a data race results in undefined behavior.

int64_t data = 0;

void foo() { /* thread 1*/
	while(1) {
		data = -1;
	}
}

void bar() { /* thread 2 */
	while(1) {
		data = 10;
	}
}
The value of data depends on the order of execution of the threads. Because data is a 64-bit integer, the write operations might be implemented as two different 32-bit load instructions by your compiler. In that case, it is possible that the writing of data by foo() is interrupted by bar() after the first 32-bit load operation. As a result, the two 32-bit halves of data can be written by different threads, causing an unexpected value for data.

Data races can cause unexpected and erratic behavior and memory corruption, resulting in bugs that are difficult to diagnose. To avoid data races, take steps to explicitly synchronize access to objects that are shared between threads. For instance:

  • Declare shared objects as atomic.

  • Use mutex objects to synchronize threads that share objects.

Polyspace Implementation

Polyspace reports violations of this directive when either of these conditions is met:

  • Multiple tasks perform unprotected operations on a shared variable. This directive is not violated if both tasks perform read-only or atomic operations.

  • Multiple tasks perform unprotected calls to certain standard library functions. Unprotected calls to functions such as strerror(), setlocale(), or getenv() can cause violations of this directive. For the list of functions that can cause a violation of this directive, see CON33-C: Avoid race conditions when using library functions.

Polyspace reports one violation for each shared variable with conflicting operations.

A task is a function that you specify as an entry point using the option Tasks (-entry-points). If you enable this directive without specifying tasks, Polyspace does not report any violations of this directive.

To resolve violations of this directive, synchronize the threads in your code. Polyspace automatically recognizes the C11 concurrency library. See Auto-Detection of Thread Creation and Critical Section in Polyspace.

Troubleshooting

If you expect a rule violation but do not see it, refer to Diagnose Why Coding Standard Violations Do Not Appear as Expected.

Examples

expand all

In this example, the global variable shared_data is modified by the threads t1 and t2 in an unpredictable sequence, leading to an unexpected value for the variable. Polyspace reports a violation.

#include <stdio.h>
#include <threads.h>

// Shared variable that will be modified by both threads
int shared_data = 0;  /*Noncompliant*/

// Worker function 1: Increments the shared variable
int worker1(void *arg) {
	for(int i = 0; i < 100000; ++i) {
		shared_data++; // Data race occurs here
	}
	return 0;
}

// Worker function 2: Decrements the shared variable
int worker2(void *arg) {
	for(int i = 0; i < 100000; ++i) {
		shared_data--; // Data race occurs here
	}
	return 0;
}

// Main function
int main() {
	thrd_t t1, t2;

	// Create worker threads
	if(thrd_create(&t1, worker1, NULL) != thrd_success) {
		// Failed to create thread 1
		return 1;
	}
	if(thrd_create(&t2, worker2, NULL) != thrd_success) {
		// Failed to create thread 2
		return 1;
	}

	// Wait for threads to finish
	thrd_join(t1, NULL);
	thrd_join(t2, NULL);

	// Print the final value of shared_data
	printf("Final value of shared_data: %d\n", shared_data);
	// The expected final value is 0, but due to the data race, the actual result may vary

	return 0;
}

To run the example, specify worker1() and worker2() as tasks or entry-point functions. In the Polyspace user interface, specify these options:

OptionSpecification
Configure multitasking manually On
Tasks (-entry-points)

worker1

worker2

Alternatively, use this command at the command line:

 polyspace-bug-finder -entry-points worker1,worker2
  
In the Results Details pane, you can review each pair of conflicting operations. For details about reviewing data race results, see Data race.

In this example, threads t1 and t2 both call the nonreentrant standard library function strerror() in an unpredictable sequence. Polyspace reports a violation.

#include <stdio.h>
#include <threads.h>
#include <errno.h>
#include <string.h>


// Worker function 1
int worker1(void *arg) {
	//...
    char *errmsg = strerror(errno); //Noncompliant
    //...
	return 0;
}

// Worker function 2
int worker2(void *arg) {
	//...
    char *errmsg = strerror(errno);
    //...
	return 0;
}

// Main function
int main() {
	thrd_t t1, t2;

	// Create worker threads
	if(thrd_create(&t1, worker1, NULL) != thrd_success) {
		// Failed to create thread 1
		return 1;
	}
	if(thrd_create(&t2, worker2, NULL) != thrd_success) {
		// Failed to create thread 2
		return 1;
	}

	// Wait for threads to finish
	thrd_join(t1, NULL);
	thrd_join(t2, NULL);

	return 0;
}

To run the example, specify worker1() and worker2() as tasks or entry-point functions. In the Polyspace user interface, specify these options:

OptionSpecification
Configure multitasking manually On
Tasks (-entry-points)

worker1

worker2

Alternatively, use this command at the command line:

 polyspace-bug-finder -entry-points worker1,worker2
  
In the Results Details pane, you can review each pair of conflicting function call. For details about reviewing data race results for standard library function calls, see Data race through standard library function call.

Check Information

Group: Concurrency Considerations
Category: Required
AGC Category: Required

Version History

Introduced in R2024b