调用pthread_sigmask在创建线程之前是否是线程安全的?

huangapple go评论72阅读模式
英文:

Is calling pthread_sigmask before creating a thread thread-safe

问题

I handle my thread spawning (C++17, CentOS) by blocking all signals in the parent thread beforehand, then starting the new thread (gets copy of signal mask) and then restoring the old signal mask in the parent. I do not want another thread than the main thread to process signals.

我通过在父线程之前阻塞所有信号,然后启动新线程(获取信号掩码的副本),然后在父线程中恢复旧的信号掩码来处理线程生成(C++17,CentOS)。我不希望除主线程之外的其他线程处理信号。

For this I have written a RAII-class and the whole thing seems to work fine. However, I am unsure if there can be a race condition here? Is the creation of a new thread synchronous or asynchronous? Is it possible that the signal mask in the parent thread was restored long before the new thread could create its copy of the mask? If this is a problem, how do I solve it once for pthreads and once for std::threads (using pthreads internally) in an elegant, generic way?

为此,我编写了一个RAII类,整个过程似乎运行良好。然而,我不确定这里是否可能存在竞态条件?创建新线程是同步还是异步的?有可能在新线程能够创建其掩码副本之前,父线程中的信号掩码已经被恢复吗?如果这是一个问题,如何以一种优雅而通用的方式解决pthread和std::thread(在内部使用pthread)的问题?

ScopedThreadSignalBlocker

ScopedThreadSignalBlocker

class ScopedThreadSignalBlocker
{
    sigset_t m_oldSignalSet;

    auto blockAllSignalsInThreadViaSignalMaskTS() -> sigset_t
    {
        sigset_t oldMask;
        pthread_sigmask( SIG_UNBLOCK, NULL, &oldMask );

        sigset_t setMask;
        sigfillset( &setMask );
        pthread_sigmask( SIG_SETMASK, &setMask, NULL );

        return oldMask;
    }

    auto setSignalMaskForThreadTS( const sigset_t &setMask ) -> void
    {
        pthread_sigmask( SIG_SETMASK, &setMask, NULL );
    }

public:
    explicit ScopedThreadSignalBlocker()
    {
        m_oldSignalSet = blockAllSignalsInThreadViaSignalMaskTS();
    }

    ~ScopedThreadSignalBlocker()
    {
        setSignalMaskForThreadTS( m_oldSignalSet );
    }
};

Usage:

用法:

//Starting pthread
{ 
    ScopedThreadSignalBlocker oSignalBlocker;
    pthread_create( &ptUpdateThread, NULL, fnUpdateThread, NULL);
} //<- Has pthread_create done the mask copy before the dtor will trigger?

//Starting std::thread
{
	ScopedThreadSignalBlocker oSignalBlocker;
    m_thread = std::thread{ std::move( func ) };
} //<-  Same question here
英文:

I handle my thread spawning (C++17, CentOS) by blocking all signals in the parent thread beforehand, then starting the new thread (gets copy of signal mask) and then restoring the old signal mask in the parent. I do not want another thread than the main thread to
process signals.

For this I have written a RAII-class and the whole thing seems to work fine. However, I am unsure if there can be a race condition here? Is the creation of a new thread synchronous or asynchronous? Is it possible that the signal mask in the parent thread was restored long before the new thread could create its copy of the mask? If this is a problem, how do I solve it once for pthreads and once for std::threads (using pthreads internally) in an elegant, generic way?

ScopedThreadSignalBlocker

class ScopedThreadSignalBlocker
{
    sigset_t m_oldSignalSet;

    auto blockAllSignalsInThreadViaSignalMaskTS() -> sigset_t
    {
        sigset_t oldMask;
        pthread_sigmask( SIG_UNBLOCK, NULL, &oldMask );

        sigset_t setMask;
        sigfillset( &setMask );
        pthread_sigmask( SIG_SETMASK, &setMask, NULL );

        return oldMask;
    }

    auto setSignalMaskForThreadTS( const sigset_t &setMask ) -> void
    {
        pthread_sigmask( SIG_SETMASK, &setMask, NULL );
    }

public:
    explicit ScopedThreadSignalBlocker()
    {
        m_oldSignalSet = blockAllSignalsInThreadViaSignalMaskTS();
    }

    ~ScopedThreadSignalBlocker()
    {
        setSignalMaskForThreadTS( m_oldSignalSet );
    }
};

Usage:

//Starting pthread
{ 
    ScopedThreadSignalBlocker oSignalBlocker;
    pthread_create( &ptUpdateThread, NULL, fnUpdateThread, NULL);
} //<- Has pthread_create done the mask copy before the dtor will trigger?

//Starting std::thread
{
	ScopedThreadSignalBlocker oSignalBlocker;
    m_thread = std::thread{ std::move( func ) };
} //<-  Same question here

答案1

得分: 1

I am unsure if there can be a race condition here? Is the creation of a new thread synchronous or asynchronous?

我不确定这里是否可能存在竞态条件?创建新线程是同步还是异步的?

You should rely on functions to do what their documentation says they will do, and not to return until that work is complete. That is the general contract for function calls. Some functions offer a mechanism to request that a piece of work be done at an unspecified future time. We then say that the requested work is to be performed aynchronously with respect to the requesting thread, but the work of the function call itself -- to register a piece of work for future execution -- is performed just as synchronously as any other function's.

你应该依赖函数执行其文档所述的操作,并且不要返回,直到工作完成。这是函数调用的一般契约。一些函数提供一种机制,用于请求在未来的某个未指定的时间执行一项工作。我们随后称所请求的工作将在与请求线程异步执行,但函数调用本身的工作——注册未来执行的工作——与任何其他函数一样同步执行。

pthread_create() is documented to create a new thread. If it returns successfully, you can rely on such a thread to already have been created. That is, pthread_create() is synchronous, just like all other functions. The work of the new thread is performed asynchronously with respect to the subsequent work of its parent thread, but that's a separate matter. Furthermore, you can see in the rationale section of the POSIX specifications for this function that POSIX does not distinguish between creating and starting a thread, so when pthread_create() returns successfully, the new thread is actually running (though it might or might not have received any CPU time yet).

pthread_create()有文档说明创建一个新线程。如果它成功返回,你可以依赖这样的线程已经创建。也就是说,pthread_create()是同步的,就像所有其他函数一样。新线程的工作是相对于其父线程的后续工作异步执行的,但这是一个单独的问题。此外,你可以在此函数的POSIX规范的解释部分中看到,POSIX不区分创建和启动线程,因此当pthread_create()成功返回时,新线程实际上已经在运行(尽管它可能还没有接收到任何CPU时间)。

Is it possible that the signal mask in the parent thread was restored long before the new thread could create its copy of the mask?

父线程的信号掩码在新线程创建其掩码副本之前是否可能被恢复?

No. At least, not by action of the parent thread.

不可能。至少不是由父线程的操作而来。

Moreover, the question belies an incorrect view of the behavior. The new thread is not responsible for copying the mask. It is the responsibility of the system and / or parent to see that that is done, as part of preparing the new thread to run. The copy of the parent's signal mask is the new thread's initial signal mask. And the parent does not return from pthread_create() until the new thread has actually been created / started.

此外,这个问题反映了一个不正确的行为观点。新线程不负责复制掩码。这是系统和/或父线程的责任,作为准备新线程运行的一部分。父线程的信号掩码的副本是新线程的初始信号掩码。父线程不会在新线程实际创建/启动之前从pthread_create()返回。

英文:

> I am unsure if there can be a race condition here? Is the creation of a new thread synchronous or asynchronous?

You should rely on functions to do what their documentation says they will do, and not to return until that work is complete. That is the general contract for function calls. Some functions offer a mechanism to request that a piece of work be done at an unspecified future time. We then say that the requested work is to be performed aynchronously with respect to the requesting thread, but the work of the function call itself -- to register a piece of work for future execution -- is performed just as synchronously as any other function's.

pthread_create() is documented to create a new thread. If it returns successfully, you can rely on such a thread to already have been created. That is, pthread_create() is synchronous, just like all other functions. The work of the new thread is performed asynchronously with respect to the subsequent work of its parent thread, but that's a separate matter. Furthermore, you can see in the rationale section of the POSIX specifications for this function that POSIX does not distinguish between creating and starting a thread, so when pthread_create() returns successfully, the new thread is actually running (though it might or might not have received any CPU time yet).

> Is it possible that the signal mask in the parent thread was restored long before the new thread could create its copy of the mask?

No. At least, not by action of the parent thread.

Moreover, the question belies an incorrect view of the behavior. The new thread is not responsible for copying the mask. It is the responsibility of the system and / or parent to see that that is done, as part of preparing the new thread to run. The copy of the parent's signal mask is the new thread's initial signal mask. And the parent does not return from pthread_create() until the new thread has actually been created / started.

答案2

得分: -1

在你的代码中,创建新线程并随后调用 pthread_createstd::thread 发生在同一个代码块内,按顺序执行。所以我会说在这个例子中不应该有竞态条件。

对于 pthread_create,创建新线程是异步的。该函数在新线程成功创建后返回。然而,可能新线程在 ScopedThreadSignalBlocker 析构函数被调用之前尚未有机会从父线程复制信号掩码,这会导致恢复旧的信号掩码。

对于使用 pthread_create 内部的 std::thread,行为类似。新线程的创建也是异步的,也可能在 ScopedThreadSignalBlocker 析构函数被调用之前,新线程尚未复制信号掩码。

英文:

So, in your code, the creating of the news thread and the subsequent call to pthread_create or std::thread happen within the same block and are executed sequentially. So i would say there shouldn't be any race conditions (again in this example).

In the case of pthread_create, the creation of a new thread is asynchronous. The function returns once the new thread has been successfully created. However, it is possible that the new thread has not yet had a chance to copy the signal mask from the parent thread before the destructor of ScopedThreadSignalBlocker is called, which would restore the old signal mask.

In the case of std::thread, which internally uses pthread_create, the behavior is similar. The creation of the new thread is asynchronous, and it is also possible that the new thread has not copied the signal mask before the destructor of ScopedThreadSignalBlocker is invoked.

huangapple
  • 本文由 发表于 2023年5月22日 20:15:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/76306100.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定