As long as the signal thread explicitly blocks the signals it will eventually wait on with sigwait, then it will be safe.
For example, the following two flow examples are both safe:
main:
create_signal_thread() // Inherits mask from main, so signals are not blocked.
block_signals()
create_more_threads() // Inherit mask from main, so signals are blocked.
signal_thread:
block_signals() // Must block signals, as this thread will wait on them.
sigwait() // Behavior is defined.
and
main:
block_signals()
create_signal_thread() // Inherits mask from main, so signals are blocked.
create_more_threads() // Inherit mask from main, so signals are blocked.
signal_thread:
sigwait() // Behavior is defined.
However, this example would not be safe:
main:
create_signal_thread() // Inherits mask from main, so signals are not blocked.
block_signals()
create_more_threads() // Inherit mask from main, so signals are blocked.
signal_thread:
sigwait() // Behavior is undefined.
Here is the relevant excerpt from sigwait:
The signals defined by set will been blocked at the time of the call to sigwait(); otherwise the behaviour is undefined. The effect of sigwait() on the signal actions for the signals in set is unspecified.