18

I want to use setitimer() (or less probable, the alarm()) in multithreaded process in linux 2.6+ with NPTL-enabled libc. Which thread will receive sigalarm (SIGALRM) from kernel?

Thanks.

2014-04 update: How should I set the setitimer() in multithreaded program, if I want to write a profiling utility like gperftools's cpuprofile; but in my tool I want to support both dynamically linked programs (so it is possible to inject my own library to init profiling) and statically linked programs (without the possibility of doing ^^^^^^).

My current profiling tool works with setting setitimer just after fork() and before exec(), and it also uses ptrace to get control over the target program and to hijack SIGPROF/SIGVPROF/SIGALRM generated by the setitimer. I have no exact idea how it works with multithreaded programs.

osgx
  • 90,338
  • 53
  • 357
  • 513

3 Answers3

15

From signal(7) man page:

A process-directed signal may be delivered to any one of the threads that does not currently have the signal blocked. If more than one of the threads has the signal unblocked, then the kernel chooses an arbitrary thread to which to deliver the signal.

Now, alarm(2) man page says that:

alarm() arranges for a SIGALRM signal to be delivered to the process in seconds seconds.

So, the signal is delivered to a process (a signal might be directed at certain thread too) and thus you do not know which of the threads will receive it.

The same with setitimer(2):

When any timer expires, a signal is sent to the process, and the timer (potentially) restarts.

You could block SIGALARM in all your threads except one, then you could be certain that it will be delivered to that only thread. Assuming you are using pthreads, you can block signals with pthread_sigmask().

pajton
  • 15,828
  • 8
  • 54
  • 65
6

There was interesting topic in LKML in 2010 https://lkml.org/lkml/2010/4/11/81: "setitimer vs. threads: SIGALRM returned to which thread? (process master or individual child)" by Frantisek Rysanek (cz). Author says that setitimer used per-thread signals at least in times before Fedora 5:

... setitimer() had per-thread granularity. It used to deliver a SIGALRM from the timer to the particular thread that called setitimer().

But in more recent Fedoras the behavior was changed ("man pthreads", ..."Threads do not share interval timers (fixed in kernel 2.6.12).")

In the topic, Andi Kleen (Intel) recommends to switch to "POSIX timers (timer_create)"; and in ML thread Davide Libenzi suggests use of timerfd (timerfd_create, timerfd_settime) on non-ancient Linuxes.

osgx
  • 90,338
  • 53
  • 357
  • 513
  • Googled with "`setitimer` thread process". Other interesting pages: [Solaris 9](http://www.shrubbery.net/solaris9ab/SUNWdev/MTP/p34.html) "When the interval timer expired, either SIGVTALRM or SIGPROF, as appropriate, was sent to the LWP that owned the interval timer."; [Redhat bug 142790 "setitimer CPU timers should be per-process, not per thread", 2004](https://bugzilla.redhat.com/show_bug.cgi?id=142790) "setitimer operates on individual threads, even under NPTL. POSIX says it should be process-wide." Send hate mails to POSIX and Roland McGrath ("My changes are in Linus's tree now...2.6.12") – osgx Apr 14 '14 at 01:05
  • Google-perftools (gperftools) with cpuprofile should have same problems with `setitimer`: https://code.google.com/p/gperftools/ "*OS X issue: in multithreaded programs, it seems that OS X often delivers the profiling signal (from sigitimer()) to the main thread, even when it's sleeping, rather than spawned threads that are doing actual work*"; https://chromium.googlesource.com/external/gperftools/+/8e188310f7d8732d81b7b04f193f89964b7af6c5/src/profiler.cc - "*TODO: Detect whether or not setitimer() applies to all threads in the process.*" – osgx Apr 14 '14 at 01:07
  • HPUX has additional interval timers with well-defined behavior for threads: http://www.polarhome.com/service/man/generic.php?qf=setitimer&type=2&of=HP-UX&sf=2 *"In addition... HP-UX provides the following three per-thread interval timers for threads, ... signal is delivered to the thread..."* – osgx Apr 14 '14 at 01:17
  • timer_create on Linux allows specifying the thread id to receive the signal. I wonder if there's a POSIX conformant way to do it, though I really only care about having a timer-thread local, not about installing a timer on one thread to signal another. – Petr Skocik Jun 04 '18 at 21:37
0

I've read many posts but I haven't found how the signals work exactly. I usually use pause() to wait for a signal. But that doesn't work because SIGALARM can't be excluded and pause() doesn't return a signal. That's why it only works with sigwait. After a few tries I came up with this solution.

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>//"-lpthread" linker option needed

pthread_t thr1;

//wait for all, except SIGALRM
int sigwaitexcludealarm()
{   sigset_t set;
    sigfillset(&set);
    sigdelset(&set,SIGALRM);
    int sig=0;
    sigwait(&set,&sig);
    return sig;
}

//simple one shot timer
void dotimer(int sig)
{   printf("ALARM!\n");
    struct itimerval timerval;
    timerval.it_value.tv_sec=0;
    timerval.it_value.tv_usec=500000;//0,5 sec
    timerval.it_interval.tv_sec=0;
    timerval.it_interval.tv_usec=0;
    setitimer(ITIMER_REAL, &timerval, NULL);
}

//timer thread worker
void* timer_run(void* p)
{   printf("thread start\n");
    signal(SIGALRM, dotimer);
    dotimer(0);
    sigwaitexcludealarm();
    printf("thread finished\n");
}

//do shutdown
void doeshutdown(int sig)
{   pthread_kill(thr1,sig);
    pthread_join(thr1,NULL);
    printf("shutdown done\n");
}

//signal handler, needed but never called
void donop(int sig)
{   return;
}


int main(int argc, char** argv)
{   printf("main start\n");
    pthread_create(&thr1,NULL,timer_run,NULL);
    //connecting signals to this process
    signal(SIGINT, donop);
    signal(SIGTERM, donop);
    signal(SIGKILL, donop);
    doeshutdown(sigwaitexcludealarm());
    printf("main exit\n");
    return 0;
}
Erik
  • 1