Is there a way to ensure atomicity while having a

2019-03-01 00:21发布

问题:

If I have a program like this (in pseudocode):

mutex_lock;
func() {
    lock(mutex_lock);
    // Some code (long enough to make a
    // race condition if no proper synchronisation
    // is available). We also going to call a signal,
    // say, SIGINT, through (ctrl-c), while we are in
    // the range of locking and unlocking the lock.
    unlock(mutex_lock);
}

sig_handler_func(sig) {
    // Say, we are handling SIGINT (ctrl-c) signal
    // And we need to call func from here too.
    if (sig == SIGINT) {
        func();
    }
}

main() {
    // Calling func from main
    func();
}

then there would be a deadlock when trying to acquire the lock in func, while it is already acquired from the 'main' caller. I was wondering if there is a way to suspend the signal handling in the C language, specifically, when, in this example, entering func and acquiring the lock, and resume the signal handling AND call the handlers upon exiting func.

回答1:

You want pthread_sigmask, the multithread version of sigprocmask

Here's some sample pseudo code:

int
main(void)
{
    sigset_t omask;
    sigset_t nmask;

    // add as many signals as you want to the mask ...
    sigemptyset(&nmask);
    sigaddset(&nmask,SIGINT);

    // [temporarily] block signals
    pthread_sigmask(SIG_BLOCK,&nmask,&omask);

    // call function safely
    func();

    // restore signal mask
    pthread_sigmask(SIG_SETMASK,&omask,NULL);

    // pending signals should occur now ...
}

I'm not totally sure, but, you may need to use pthread_sigmask to block signals in all but one thread and do the above from that thread only.

Also, I'd be remiss if I didn't say that I'd refactor your code. The number of things you can do in a signal handler [aside from this] is limited (e.g. no malloc, no printf, etc.)

Dedicating one thread for signal handling and having it do sigsetjmp and the signal handler does siglongjmp.

Or have the signal handler set a volatile global (e.g. signal_occurred) that is monitored at base level.

Thus, all the "heavy lifting" that you'd be doing in the signal handler can be done from base task level where you can do anything.



回答2:

You need two locks. The one used inside your func(), and one to protect the process's signal mask.

You have to make masking and unmasking the signal atomic also:

static  pthread_mutex_t mask_mutex = PTHREAD_MUTEX_INITIALIZER;
sigset_t old_set;
sigset_t new_set;

sigemptyset( &new_set );
sigaddset( &new_set, SIGINT );

pthread_mutex_lock( &mask_mutex );

pthread_sigmask( SIG_BLOCK, &new_mask, &old_mask );

func();

pthread_sigmask( SIG_SETMASK, &old_mask, NULL );

pthread_mutex_unlock( &mask_mutex );

With no lock around the pthread_sigmask(), threads are likely to corrupt the process sigmask as execution overlaps.



回答3:

You may use the test mutex functions (trylock) in this kind of undefined situations, to be safe. And with this, you don't necessarily need to block too.