[cfe-dev] atomic intrinsics

Howard Hinnant hhinnant at apple.com
Mon Oct 18 11:21:35 PDT 2010


On Oct 18, 2010, at 1:27 PM, Howard Hinnant wrote:

> On Oct 18, 2010, at 1:19 PM, John McCall wrote:
> 
>>> One question about Design A that has yet to be discussed is the possible defaulting of memory orderings in the intrinsics.  This defaulting, if it exists, is described at the very bottom of:
>>> 
>>> http://libcxx.llvm.org/atomic_design_a.html
>>> 
>>> I have no strong feeling either way.  I can easily live with or without the memory order defaults.  But I should either remove the "If desired" from the description (mandating the defaults), or remove the description of the defaults altogether (banning them).  Please weigh in with your opinions.
>> 
>> The defaulting scheme for the two-parameter versions is very complicated and precludes implementing the type-checking for these builtins with a single C++ function declaration.  If we're going to allow defaulting, I think they should both default to 5.
> 
> This would make it very easy to drop into undefined behavior.  For example:
> 
> while (__atomic_compare_exchange_strong(&atomic_obj, &expected, desired, memory_order_acquire))
>    ...
> 
> In the above example, only mem_failure is defaulted (to 5 as you suggest), and this violates the requirement that mem_failure <= mem_success (mem_success is 2).

Here's a brief C++ simulation of the "default behavior" that the C++ <atomic> header is specified to have for compare_exchange_strong and compare_exchange_weak.  This functionality either goes into the lib, or into the intrinsic.  Which is best, I don't really have an opinion.  But if the intrinsics are to have defaults, I think they should match the behavior of the standard.

The demo below models undefined behavior with an assert:

#include <iostream>
#include <cassert>

typedef enum memory_order
{
    memory_order_relaxed, memory_order_consume, memory_order_acquire,
    memory_order_release, memory_order_acq_rel, memory_order_seq_cst
} memory_order;

int
translate_memory_order(int o)
{
    switch (o)
    {
    case 4:
        return 2;
    case 3:
        return 0;
    }
    return o;
}

void test(int mem_success = memory_order_seq_cst)
{
    int mem_failure = translate_memory_order(mem_success);
    std::cout << "mem_success = " << mem_success << '\n';
    std::cout << "mem_failure = " << mem_failure << '\n';
    assert(mem_success >= mem_failure);
    assert(mem_failure != memory_order_release);
    assert(mem_failure != memory_order_acq_rel);
    std::cout << "------------------------\n";
}

void test(int mem_success, int mem_failure)
{
    std::cout << "mem_success = " << mem_success << '\n';
    std::cout << "mem_failure = " << mem_failure << '\n';
    assert(mem_success >= mem_failure);
    assert(mem_failure != memory_order_release);
    assert(mem_failure != memory_order_acq_rel);
    std::cout << "------------------------\n";
}

int main()
{
    std::cout << "test()\n";
    test();
    std::cout << "test(memory_order_relaxed)\n";
    test(memory_order_relaxed);
    std::cout << "test(memory_order_consume)\n";
    test(memory_order_consume);
    std::cout << "test(memory_order_acquire)\n";
    test(memory_order_acquire);
    std::cout << "test(memory_order_release)\n";
    test(memory_order_release);
    std::cout << "test(memory_order_acq_rel)\n";
    test(memory_order_acq_rel);
    std::cout << "test(memory_order_seq_cst)\n";
    test(memory_order_seq_cst);
    std::cout << "test(memory_order_acquire, memory_order_consume)\n";
    test(memory_order_acquire, memory_order_consume);
    std::cout << "test(memory_order_acquire, memory_order_seq_cst)\n";
    test(memory_order_acquire, memory_order_seq_cst);
}

test()
mem_success = 5
mem_failure = 5
------------------------
test(memory_order_relaxed)
mem_success = 0
mem_failure = 0
------------------------
test(memory_order_consume)
mem_success = 1
mem_failure = 1
------------------------
test(memory_order_acquire)
mem_success = 2
mem_failure = 2
------------------------
test(memory_order_release)
mem_success = 3
mem_failure = 0
------------------------
test(memory_order_acq_rel)
mem_success = 4
mem_failure = 2
------------------------
test(memory_order_seq_cst)
mem_success = 5
mem_failure = 5
------------------------
test(memory_order_acquire, memory_order_consume)
mem_success = 2
mem_failure = 1
------------------------
test(memory_order_acquire, memory_order_seq_cst)
mem_success = 2
mem_failure = 5
Assertion failed: (mem_success >= mem_failure), function test, file test.cpp, line 38.
Abort trap

-Howard





More information about the cfe-dev mailing list