最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c - Embedded C99: How to solve concurrency issues on Multicore CPU - Stack Overflow

programmeradmin3浏览0评论

With C11 chapter 5.1.2.4 (Multi-threaded executions and data races) was added to "ISO/IEC 9899 Programming languages — C", and <stdatomic.h> was added.

The C Standard, section 5.1.2.5, paragraph 35 [ISO/IEC 9899:2024], says:

The execution of a program contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other. Any such data race results in undefined behavior.

Consequently, if I have a consumer and a producer function and at least one could be called from a different thread of execution than the other that would result in undefined behavior:

Data data;
unsigned int ready = 0;  /* 0 not ready; !=0 ready */

void producer(void)  /* called by thread 1 */
{
    initializeData();
    ready = 1;
}

void consumer(void)  /* called by thread 2 */
{        
    while(ready == 0)  /* data race */
    { /* wait until ready */ }
    useData();
}

With C11 I could fix the undefined behavior by adding sequential consistency by changing the type of ready to atomic_uint as that introduces a total order (also to data):

Data data;
atomic_uint ready = 0;  /* 0 not ready; !=0 ready */

void producer(void)  /* called by thread 1 */
{
    initializeData();
    ready = 0;
}

void consumer(void)  /* called by thread 2 */
{
    while(ready != 0)
    { /* wait until ready */ }
    useData();
}

In the C99 version of the standard I couldn't find anything about this kind of data race or about what happens when I have different threads of execution.

I assume that a concurrent and conflicting access to a shared object (in my example data and ready) also causes a data race and undefined behavior when I use C99.

My questions:

  1. How can I fix this kind of data race on an embedded platform, when I only have C99. (I guess it is compiler and hardware specific as it is out of scope of the C99 abstract machine.)
  2. If I have an embedded system without threads but a multicore CPU and different "Tasks" are executed on different CPU cores and it is possible that concurrent access to a global variable happens (task 1 on core 1 calls producer, task 2 on core 2 calls consumer). Is that the same as concurrent access from different threads (threads of execution) and undefined behavior in C (C99, C11, ...)?
  3. Are there other and easier solutions/fixes possible for the multicore scenario?

With C11 chapter 5.1.2.4 (Multi-threaded executions and data races) was added to "ISO/IEC 9899 Programming languages — C", and <stdatomic.h> was added.

The C Standard, section 5.1.2.5, paragraph 35 [ISO/IEC 9899:2024], says:

The execution of a program contains a data race if it contains two conflicting actions in different threads, at least one of which is not atomic, and neither happens before the other. Any such data race results in undefined behavior.

Consequently, if I have a consumer and a producer function and at least one could be called from a different thread of execution than the other that would result in undefined behavior:

Data data;
unsigned int ready = 0;  /* 0 not ready; !=0 ready */

void producer(void)  /* called by thread 1 */
{
    initializeData();
    ready = 1;
}

void consumer(void)  /* called by thread 2 */
{        
    while(ready == 0)  /* data race */
    { /* wait until ready */ }
    useData();
}

With C11 I could fix the undefined behavior by adding sequential consistency by changing the type of ready to atomic_uint as that introduces a total order (also to data):

Data data;
atomic_uint ready = 0;  /* 0 not ready; !=0 ready */

void producer(void)  /* called by thread 1 */
{
    initializeData();
    ready = 0;
}

void consumer(void)  /* called by thread 2 */
{
    while(ready != 0)
    { /* wait until ready */ }
    useData();
}

In the C99 version of the standard I couldn't find anything about this kind of data race or about what happens when I have different threads of execution.

I assume that a concurrent and conflicting access to a shared object (in my example data and ready) also causes a data race and undefined behavior when I use C99.

My questions:

  1. How can I fix this kind of data race on an embedded platform, when I only have C99. (I guess it is compiler and hardware specific as it is out of scope of the C99 abstract machine.)
  2. If I have an embedded system without threads but a multicore CPU and different "Tasks" are executed on different CPU cores and it is possible that concurrent access to a global variable happens (task 1 on core 1 calls producer, task 2 on core 2 calls consumer). Is that the same as concurrent access from different threads (threads of execution) and undefined behavior in C (C99, C11, ...)?
  3. Are there other and easier solutions/fixes possible for the multicore scenario?
Share Improve this question edited Feb 7 at 11:25 MSalters 180k11 gold badges167 silver badges370 bronze badges asked Feb 7 at 9:58 Sonic78Sonic78 8086 silver badges20 bronze badges 13
  • 1 What multi-threading primitives does the platform have? Does it have mutexes? Does it have condition variables? – Some programmer dude Commented Feb 7 at 10:02
  • 6 If all you have is the plain standard "C99 abstract machine", you don't have threads. That makes then whole premise moot, and this question becomes meaningless. – Some programmer dude Commented Feb 7 at 10:59
  • 1 Please keep in mind that mainstream multicore CPUs only appeared in the years 2000~2005 so after C99. For example the Pentium 4 (released in ~2000) did have only 1 core. Not much people cared about programming multicore besides some scientists working on super-computers before the 2000. Locks/mutexes and condition variables was enough before for multi-process/multi-threaded applications working on 1 core. Threads was only meant to make things concurrent, not parallel. – Jérôme Richard Commented Feb 7 at 11:01
  • 1 A C99 compiler can be multiprocessor-aware. In that case, it can have intrinsic functions to do atomic accesses, and all its synchronization objects (mutexes, semaphores, ...) must be multiprocessor-aware (using spin-locks and RAM synchronizations but not interrupt masking to access conflicting data). And I think a C11 compiler must be multiprocessor-aware. – dalfaB Commented Feb 7 at 11:49
  • 3 Yes, @dalfaB, but C99 does not acknowledge the existence of multiple threads or say anything about how a program could have them or how it would behave if it did. The question cannot be answered in terms of C99 (alone). – John Bollinger Commented Feb 7 at 12:31
 |  Show 8 more comments

1 Answer 1

Reset to default 1
  1. How can I fix this kind of data race on an embedded platform, when I only have C99. (I guess it is compiler and hardware specific as it is out of scope of the C99 abstract machine.)

In an implementation-specific way (probably) or not at all. Given that your target platform features multiple execution units with shared memory, I very much expect that it has one or more mechanisms appropriate to managing conflicting accesses (in the C11+ sense) to the same objects.

  • Possibly there are situations where it just works. For instance, perhaps every aligned, word-sized access effectively has sequential consistency semantics.

  • Possibly threading and synchronization primitives are provided by an implementation-specific library.

  • Possibly you need to access ISA-specific features directly, by, say, writing your own assembly-language interfaces.

  • Possibly (though I think the chances are remote) you are expected simply to avoid conflicting accesses.

  1. If I have an embedded system without threads but a multicore CPU and different "Tasks" are executed on different CPU cores and it is possible that concurrent access to a global variable happens (task 1 on core 1 calls producer, task 2 on core 2 calls consumer). Is that the same as concurrent access from different threads (threads of execution) and undefined behavior in C (C99, C11, ...)?

Maybe, and maybe not. Consult the documentation for your platform.

  1. Are there other and easier solutions/fixes possible for the multicore scenario?

It depends on the capabilities of your C implementation and execution environment. There is a variety of techniques for sharing data among multiple, possibly-concurrent executions. Using shared memory is one of them, but there is also I/O via regular files, pipes, or sockets; message queues; to some extent, semaphores, and probably others. Some, all, or none of those may be available in any given execution environment. Among those that are available, which are suitable for your particular needs and how easy it is to apply them depends also on the details of your particular requirements.

发布评论

评论列表(0)

  1. 暂无评论