IBM Support

mutex - Bad In Signal Context

White Papers


Abstract

On the lines of thread safe function another concept was introduced called signal safe functions. This was introduced to avoid the situations arising from functions getting called from signal handler which has the potential to interrupt the operation being performed. For example, suppose a program is in the middle of a call to printf and a signal occurs whose handler itself calls printf. In this case, the output of the two printf statements would be intertwined. To avoid this, the handler should not call printf itself when printf might be interrupted by a signal.

Content

On the lines of thread safe function another concept was introduced called signal safe functions. This was introduced to avoid the situations arising from functions getting called from signal handler which has the potential to interrupt the operation being performed. For example, suppose a program is in the middle of a call to printf and a signal occurs whose handler itself calls printf. In this case, the output of the two printf statements would be intertwined. To avoid this, the handler should not call printf itself when printf might be interrupted by a signal.

This problem cannot be solved by using synchronization primitives because any attempted synchronization between the signal handler and the operation being synchronized would produce immediate deadlock. Consider following scenario which demonstrates this behavior.

void *thread(void arg)
{

      pthread_mutex_lock( &mutex1 );
    global_var++
      pthread_mutex_unlock( &mutex1 );
}
void signal_handler( int sig )
{
      pthread_mutex_lock( &mutex1 );
    global_var++
      pthread_mutex_unlock( &mutex1 );
}

In the preceding code there could be 2 possibilities:
  • In normal running situation suppose thread acquires the lock first. Therefore the global_var would be incremented and then lock is released. After this happens an signal comes in which ends up in calling signal handler which would then acquire the lock , update the global_var and release the lock and after which normal operation is restored.

  • Consider the case when thread has just acquired the lock and signal comes in .Since signal handler gets the highest priority, signal_handler gets called. Now it will try to acquire the lock.Since the lock is already acquired by the thread , signal_handler will keep waiting for the same .Since signal handler won’t return until it has handled the signal , the application in whole will go into a dead lock kind of state.

Following is the sample example to demonstrate the case:

#include <signal.h>
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

long count=0;
pthread_mutex_t mutex_lock = PTHREAD_MUTEX_INITIALIZER;
void* thread(void *arg);
void signalInfoHandler(int sig,siginfo_t *,void *sc);

struct sigaction sa,oldsa;

void signalInfoHandler(int sig,siginfo_t *info,void *sc)
{
   pthread_mutex_lock(&mutex_lock);
        count++;
    pthread_mutex_unlock(&mutex_lock);
 
   fprintf(stderr,"signalhandler :count=%ld\n",count);

}

void* thread(void *arg)
{
   pthread_mutex_lock(&mutex_lock);
   fprintf(stderr,"thread:acquired lock");
   sleep(2);
        count++;
    pthread_mutex_unlock(&mutex_lock);
   printf(stderr,"thread:released");
   fprintf(stderr,"thread:count=%ld\n",count);  
}
int main()
{
  int i=0;  pthread_t pt[50];
 
  sa.sa_flags = SA_SIGINFO;
  sa.sa_sigaction=&signalInfoHandler;

  sigaction(SIGUSR2,&sa,&oldsa);
  sigaction(SIGINT,&sa,&oldsa);
  sigaction(SIGQUIT,&sa,&oldsa);

 while(i<50)
 {
   sleep(2);
   pthread_create(&pt[i],NULL,thread,NULL);
 }
 while(i<50)
pthread_join(pt[0],NULL);

 fprintf(stderr,"main:count=%ld",count);
}

Try pressing the CTRL+C at the time when the control is in thread call. After few iterations you could see that the CTRL+C would not respond and application as such would go into deadlock situation.

Hence it becomes imperative that such situation does not arise. Lot of programmers avoid such situations by defining there critical sections. Once critical section has been defined, they mask all the signals during this operation hence making signals ineffective during this period. Thus avoiding the drawbacks of unnecessary interruptions by signals.

Another set of feature that is said not to be used in signal handler are system heap related calls like malloc , free The main reason for these calls to be avoided is because of these being non-reentrant in nature. This includes even those functions that allocate space to store results. There is a possibility of memory corruption happening in situation where while the main thread was in malloc call and signal occurs which invokes signal handler within which a call to malloc is made again.

However consider the case where malloc is made reentrant by means of synchronization. Even this could at some time lead to hang situations. However one possibility could be to make these calls reentrant by masking all the signals but then that would lead to bad performance.

POSIX lists number of functions that could be called from signal handler which are termed as async-signal safe functions. The lists could be got from the manual pages or specification but I am listing here for quick reference:

_exit fstatreadsysconf
alarmgeteuidrmdirtcflow
cfgetispeedgetgidsetgidtcflush
cfgetospeedgetgroupssetpgidtcgetattr
cfsetispeedgetpgrpsetsidtcgetpgrp
cfsetospeedgetpidsetuidtcsendbreak
chdirgetppidsigactiontcsetattr
chmod getuidsigaddsettcsetpgrp
chown Killsigdelsettime
closelinksigemptysettimes
creatlseeksigfillsetumask
dupmkdirsigismemberuname
dup2mkfifosigpendingunlink
execleopensigprocmaskutime
execvepathconfsigsuspendwait
fcntlpausesleepwaitpid
forkpipestatWrite

Thread-Specific Data and Signal Handling in Multi-Threaded Applications

Original Publication Date

17 May 2006

[{"Product":{"code":"SSNVBF","label":"Runtimes for Java Technology"},"Business Unit":{"code":"BU059","label":"IBM Software w\/o TPS"},"Component":"General","Platform":[{"code":"PF016","label":"Linux"}],"Version":"5.0;1.4.2;1.4.1;1.3.1","Edition":"","Line of Business":{"code":"LOB36","label":"IBM Automation"}}]

Document Information

Modified date:
17 June 2018

UID

swg27007649