cschleiden
Published on

SIGPROF signal handler and pthreads

Authors

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 not 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&#8230; \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]);
   }
}