paulm
paulm

Reputation: 5882

Using std::atomic from C

I have a C library that defined a bunch of platform specific macros for atomic operations. How can I use std::atomic as the implementation for this?

For example the C code has:

#define mylib_atomic_int_add(_pi, _val) do_atomic_int_add(_pi, _val)
int number = 0;
mylib_atomic_int_add(&number, 7);

And the C++ platform abstraction layer (i.e a static library compiled as C++11 that is linked with the C code) has:

extern "C"
{
    int do_atomic_int_add(volatile int* i, volatile int v)
    {
        return *i + v;
    } 
}

Obviously this isn't safe because it isn't atomic, since the int is declared in the C code std::atomic can't be used there, but is there some way to use it within do_atomic_int_add()? like return std::atomic<int>::add(*i, v);?

Edit:

I don't know why no one understands what I'm trying to do. Its basically how do I atomically increment an int in C++ which has been passed over from a C function.

Upvotes: 3

Views: 2492

Answers (2)

Jeff Hammond
Jeff Hammond

Reputation: 5642

Why this question is invalid

C++11 atomics require you to use C++11 atomic types for atomic operations. You can step outside the standard by casting from non-atomic to atomic types, but this is not portable code, even if it often works. It's not even valid from an entirely C++ code, let alone a mixture of C and C++.

Because of this limitation, you will not be able to write a C++ atomics library with an interface to C, because you cannot declare C++11 atomic types in C code.

Hans Boehm, one of the foremost authorities on programming language concurrency features, has a proposal for C++0y titled N4013: Atomic operations on non-atomic data that explains some of the issues here.

A different way to solve the problem

You can solve this entirely within C, which is the safest and most portable solution.

The most common way to do atomics in C and C++ prior to the introduction of native language types in C11 and C++11 is with compiler intrinsics. There are two variants of these:

Both of these are supported by GCC, Clang and Intel compilers as well as other compilers (IBM for sure, Cray mostly but with at least one bug, and probably PGI). I don't use Windows but I know MSVC has some sort of compiler intrinsics for atomics.

In addition, there are at least two different open-source implementations of C atomics with permissive licenses:

Additionally, OpenMP 3+ gives you the ability to use atomics from C89 code, but I don't know if MSVC supports it sufficiently.

Finally, C11 atomics will solve your problem beautifully in an entirely ISO standard way. However, compiler support is rather limited right now. I have used these features with GCC 5+ and late-model Clang, and perhaps Intel 16, but I do not expect MSVC to ever support them.

Code

I removed volatile from the value, since it does nothing. The extern "C" is not required except to turn off C++ name-mangling, as none of the implementations use C++. Because you didn't specify a memory ordering requirement, I assume relaxed (unordered) atomics are sufficient.

__sync version

extern "C"
{
    int do_atomic_int_add(volatile int* i, int v)
    {
        //return (*i + v);
        return __sync_add_and_fetch(i,v);
    } 
}

__atomic version

extern "C"
{
    int do_atomic_int_add(volatile int* i, int v)
    {
        //return (*i + v);
        return __atomic_add_fetch(i,v, __ATOMIC_RELAXED);
    } 
}

OpenMP version

extern "C"
{
    int do_atomic_int_add(volatile int* i, int v)
    {
        int r;
        #pragma omp atomic update
        { r = (*i + v); }
        return r;
    } 
}

Upvotes: 4

MSalters
MSalters

Reputation: 179819

Formally, the problem is that std::atomic<int> is a type whose objects can be modified in such a way that .load() will only return values that are .store()d. C doesn't call .load so it can't use the int directly.

Of course, you can define extern "C" int atomic_load(struct atomic_int*, enum std_memory_order) with the obvious implementation. You have to essentially wrap std::atomic_int in a C-compatible struct and forward all methods.

Upvotes: 1

Related Questions