SIGPROF signal handler and pthreads

During work on my thesis the question popped up how signals generated by the SIGPROF timer were handled in multithreaded code. Signal handlers are process specific to it could have been that one random thread handled the sent signal. As I could not a find a suitable explanation in the intertubes I performed a small experiment.

My sample program installs a signal handler and starts a timer with a frequency of about 100hz. At first the number of captured signals in a ten second timespan are captured using only the main thread and then using four individual threads. The output is:
Signals caught after 10 seconds: 999
Creating 4 threads
Signals caught after 4x10 seconds by thread 0: 1000
Signals caught after 4x10 seconds by thread 1: 1000
Signals caught after 4x10 seconds by thread 2: 1000
Signals caught after 4x10 seconds by thread 3: 1000

So apparently each thread handles the SIGPROF signal, which is quite nice for my purpose.

The sourcode is here (I just assume that pthread_self is async-safe even though it’s specified by the standard. It appears, however, that assuming that is done by most people working on that kind of stuff):

#include <signal.h>
#include <stdio.h>
#include <pthread.h>
#include <sys/time.h>

#include "../../util/util_time_measurement.h"

const int thread_count = 4;

volatile sig_atomic_t signal_count[thread_count];

pthread_t threads[thread_count];

static void sigprof_handler(int sig_nr, siginfo_t* info, void *context)
{
   int t;
   for(t = 0; t < thread_count; ++t)
   {
      if(threads[t] == pthread_self())
      {
	signal_count[t]++;

	return;
      }
   }

   /* Probably no thread */
   signal_count[0]++;
}

void install_signal_handler()
{
   /* Install signal handler for SIGPROF event */
   struct sigaction sa;
   memset(&sa, 0, sizeof(sa));
   sa.sa_sigaction = sigprof_handler;
   sa.sa_flags = SA_RESTART | SA_SIGINFO;
   sigemptyset(&sa.sa_mask);

   sigaction(SIGPROF, &sa, NULL);
}

void idle_time(int seconds)
{
   timestamp_t start = util_get_timestamp();
   while(1)
   {
      if(util_get_timestamp() > start + seconds)
      {
	break;
      }
   }
}

void* thread_work(void* data)
{
   idle_time(10);

   return NULL;
}

int main(int argc, char** argv)
{
   install_signal_handler();
   
   static struct itimerval timer;

   timer.it_interval.tv_sec = 0;
   timer.it_interval.tv_usec = 1000000 / 100; /* 100hz */
   timer.it_value = timer.it_interval;

   /* Reset count */
   int t;
   for(t = 0; t < thread_count; ++t)
   {
      signal_count[t] = 0;
   }

   /* Install timer */
   if (setitimer(ITIMER_PROF, &timer, NULL) != 0)
   {
      printf("Timer could not be initialized \n");
   }
   
   /* Idle for 10 seconds */
   idle_time(10);


   printf("Signals caught after 10 seconds: %d \n", signal_count[0]);

   /* Reset count */
   for(t = 0; t < thread_count; ++t)
   {
      signal_count[t] = 0;
   }

   printf("Creating %d threads... \n", thread_count);

   for(t = 0; t < thread_count; ++t)
   {
      pthread_create(&threads[t], NULL, thread_work, NULL);
   }
   
   for(t = 0; t < thread_count; ++t)
   {
      pthread_join(threads[t], NULL);
   }

   for(t = 0; t < thread_count; ++t)
   {
      printf("Signals caught after %dx10 seconds by thread %d: %d \n", thread_count, t, signal_count[t]);
   }
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s