Search code examples
c++multithreadingperformancec++11atomic

How to correctly use std::atomic_signal_fence()?


cppreference.com documents this function as "fence between a thread and a signal handler executed in the same thread". But I found no example on the Internet.

I wonder whether or not the following psuedo-code correctly illustrates the function of std::atomic_signal_fence():

int n = 0;
SignalObject s;

void thread_1()
{
    s.wait();
    std::atomic_signal_fence(std::memory_order_acquire);
    assert(1 == n); // never fires ???
}

void thread_2()
{
    n = 1;
    s.signal();
}

int main()
{
    std::thread t1(thread_1);
    std::thread t2(thread_2);

    t1.join(); t2.join();
}

Solution

  • No, your code does not demonstrate correct usage of atomic_signal_fence. As you quote cppreference.com, atomic_signal_fence only perform synchronization between a signal handler and other code running on the same thread. That means that it does not perform synchronization between two different threads. Your example code shows two different threads.

    The C++ spec contains the following notes about this function:

    Note: compiler optimizations and reorderings of loads and stores are inhibited in the same way as with atomic_thread_fence, but the hardware fence instructions that atomic_thread_fence would have inserted are not emitted.

    Note: atomic_signal_fence can be used to specify the order in which actions performed by the thread become visible to the signal handler.

    Here's an example of correct, if not motivating, usage:

    static_assert(2 == ATOMIC_INT_LOCK_FREE, "this implementation does not guarantee that std::atomic<int> is always lock free.");
    
    std::atomic<int> a = 0;
    std::atomic<int> b = 0;
    
    extern "C" void handler(int) {
        if (1 == a.load(std::memory_order_relaxed)) {
            std::atomic_signal_fence(std::memory_order_acquire);
            assert(1 == b.load(std::memory_order_relaxed));
        }
    
        std::exit(0);
    }
    
    int main() {
        std::signal(SIGTERM, &handler);
    
        b.store(1, std::memory_order_relaxed);
        std::atomic_signal_fence(std::memory_order_release);
        a.store(1, std::memory_order_relaxed);
    }
    

    The assertion, if encountered, is guaranteed to hold true.