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

In C++17, is it undefined to protect data passed from a signal handler using lock-free atomics?

本文关键字:程序 信号处理 保护 数据 原子学 C++17 是否 未定义      更新时间:2023-10-16

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

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

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);
}
}

根据评论回答我自己的问题 - 这似乎是未定义的行为,一如既往。

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

在大多数情况下,处理信号的正确做法是在信号处理程序中设置一个标志,或者"接受信号"(sigwait(( et. al(。就我而言,这不起作用,因为我需要获取堆栈跟踪(SIGPROF处理程序(并将其交付给应用程序。