首页 > 解决方案 > 在 C++17 中,是否未定义使用无锁原子保护从信号处理程序传递的数据?

问题描述

根据我对过去问题的阅读,该标准允许在信号处理程序中访问无锁原子,并且对“普通”对象的任何修改都变得未定义。如果是这样,下面的模式是不是原子变量是非原子变量的内存屏障,也是未定义的?

下面的模式是单生产者-信号-消费者环形缓冲区的模式。信号是生产者,线程是消费者。请忽略正在旋转的收集线程的低效率 - 不想使已经复杂的示例复杂化。

AFAICT,这种模式在信号处理程序中使用是安全的,因为原子变量可以防止任何竞争,并且我避免了信号处理程序内部的内存分配。但是,它确实修改了信号处理程序中的非原子对象。

vector<vector<int>> ring;
atomic<int> push_idx;
atomic<int> pop_idx;

void init()
{
    ring.resize(8);
    for (auto &v : ring)
    {
        v.resize(10);
    }
}

int advance_idx(int i)
{
    return (i + 1) % 8;
}

int buf[5];

void sig_handler()
{
    int a = push_idx.load(memory_order_relaxed);
    int p = pop_idx.load(memory_order_acquire);
    if (advance_idx(a) == p)
    {
        return; // ring overflow
    }

    // fill up static-linkage data
    for (int i = 0; i < 5; i++)
    {
        buf[i] = i;
    }

    // copy some static-linkage data into buffer
    for (int i = 0; i < 5; i++)
    {
        ring[a][i] = buf[i];
    }
    ring[a].resize(5);
    push_idx.store(advance_idx(a), memory_order_release)
}

void collecting_thread()
{
    for (;;)
    {
        int p = pop_idx.load(memory_order_relaxed);
        int a = push_idx.load(memory_order_acquire);
        if (p == a)
        {
            continue; // nothing to do
        }

        auto &vec_to_process = ring[p];
        process_vec(std::move(vec_to_process); //may move-from
        vec_to_process.clear();
        vec_to_process.resize(10);
        pop_idx.store(advance_idx(p), memory_order_release);
    }
}

标签: c++signalsc++17

解决方案


根据评论回答我自己的问题 - 它似乎是未定义的行为,因为它一直都是。

正如对sem_post、信号处理程序和未定义行为(关于 POSIX)的回答中所说,它未定义不是因为它与信号、线程、原子或 CPU 的工作方式相矛盾,而是因为指定正确的行为很困难,而且没有任何意义收益。

在大多数情况下,正确处理信号的方法是在信号处理程序中设置一个标志,或者“接受信号”(sigwait() 等)。在我的情况下,这不起作用,因为我需要获取堆栈跟踪(SIGPROF 处理程序)并将其传递给应用程序。


推荐阅读