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

时间:2018-12-03 22:58:01

标签: c++ atomic errno

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

这会导致未定义的行为或实现定义的行为吗?

1 个答案:

答案 0 :(得分:3)

尽管1988年的旧POSIX标准曾经要求errno为全局对象,但在以后的版本中不再是这种情况。至少POSIX.1-2001要求errno是线程本地的。我怀疑指定POSIX线程的POSIX.1c-1995已经要求这样做,但是我无权访问该文档,因此无法验证。

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

也就是说,尽管C标准不需要errno作为全局对象(至少C99不需要),但它也不保证errno的线程局部性。因此,在非POSIX系统或不提供线程局部性的旧POSIX系统上,可能需要锁定errno。并且,如果由于某种原因该平台确实支持C ++ 11,则C ++ 11原子可能是实现锁的理想选择-也可能不是。至少在理论上。

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