原子和 errno:使用 C++11 原子读取受保护的 errno 值是否合理?

Atomics and `errno`: is it reasonable to use C++11 atomics to read a protected errno value?

处理 errno 是在多线程环境中使用 POSIX API 的难题之一。像下面这样使用 std::atomic 的锁是否合理?

class FastLock{
    std::atomic_int value;
public:
    FastLock()
    : value{0}{}

    void unlock()
    {
        value.store(0,std::memory_order_release);
    }

    bool try_lock()
    {
        int r = value.exchange(1,std::memory_order_acquire);
        return !r;
    }
};

上下文类似于:

template<typename function, typename ...args>
auto shield(function _fn){
    static FastLock* lk = new FastLock{};
    return [=](args... _v){
        while(lk->try_lock());
        auto ret = std::forward(_fn, _v...);
        auto errval = errno;
        lk->unlock();
        return std::make_pair(ret,errval);
    };
}

这会导致任何类型的未定义行为或实现定义的行为吗?

虽然 1988 年的旧 POSIX 标准曾经要求 errno 是全局对象,但在以后的修订版中不再如此。至少 POSIX.1-2001 要求 errno 是线程本地的。我怀疑 POSIX.1c-1995 已经要求它指定 POSIX 线程,但我无权访问该文档,所以我无法验证。

我不认为支持 C++11 的 POSIX 系统也不会支持 POSIX 2001,因此,使用 C++11 原子似乎不太可能必要的。

也就是说,虽然 C 标准不要求 errno 是一个全局对象(至少 C99 不需要),但它也不保证 errno 的线程局部性。因此,在非 POSIX 系统或不提供线程局部性的旧 POSIX 系统上可能需要锁定 errno。如果出于某种原因该平台确实支持 C++11,那么 C++11 原子可能是实现锁的理想选择——也可能不是。至少理论上是这样。

请注意,为了保证线程安全,您必须确保对可能设置 errno 的函数的所有调用都必须使用锁。如果您使用任何可能使用此类标准函数的库,您也必须锁定对此类函数的调用。