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<br /> Creating 4 threads<br /> Signals caught after 4x10 seconds by thread 0: 1000<br /> Signals caught after 4x10 seconds by thread 1: 1000<br /> Signals caught after 4x10 seconds by thread 2: 1000<br /> 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):
[sourcecode language=”cpp”]#include #include #include #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]);
   }
}
[/sourcecode]


© 2019 Christopher Schleiden. All rights reserved.

Powered by Hydejack v8.4.0