skip to Main Content

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

2

Answers


  1. 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.

    Login or Signup to reply.
  2. 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search