Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there an async-safe way to get the current thread ID in Linux?

Is there any way to get the current thread ID from a signal handler in Linux? The getpid() method does what I want, but it is not clear if it is async-safe. man 7 signal provides a list of POSIX methods which are async safe, but this tells us nothing about non-POSIX methods such as getpid(). Presumably some of the many non-POSIX methods Linux has added are async safe, but I can't find the list.

There is also this answer which claims that all direct (not multiplexed) syscalls are async safe, but provides no evidence.

The goal is to build some kind of async-safe thread local storage, since __thread is not safe in the general case.

It doesn't have to be the "Linux thread ID" - any consistent thread ID would be fine. For example pthread_self would be great, but there is nothing claiming that is async safe. If we examine the implementation of that method in glibc Linux, it defers to the THREAD_SELF macro, which looks like:

# define THREAD_SELF \
({ struct pthread *__self;                                                  \
  asm ("movl %%gs:%c1,%0" : "=r" (__self)                                  \
          : "i" (offsetof (struct pthread, header.self)));                    \
  __self;}) 

It seems to be that this should be async-safe, if the thread in question was created under a regime that populates the gs resgister (perhaps all threads in Linux are, I'm not sure). Still looking at that header makes me pretty scared...

like image 441
BeeOnRope Avatar asked Nov 10 '22 13:11

BeeOnRope


1 Answers

As mentioned in Async-signal-safe access to __thread variables from dlopen()ed libraries? you provided (emphasis is mine):

The __thread variables generally fit the bill (at least on Linux/x86), when the variable is in the main executable, or in a directly-linked DSO.

But when the DSO is dlopen()ed (and does not use initial-exec TLS model), the first access to TLS variable from a given thread triggers a call to malloc...

In other words, it only requires one access to that thread specific variable to bring it to life and make it available in signal handlers. E.g.:

  1. Block the signals you are interested in in the parent thread before creating a child thread.
  2. Unblock those interesting signals in the parent thread after the call to pthread_create.
  3. In the child thread initialize your __thread variable and unblock those interesting signals.

I normally store the write end of a unix pipe in that tread-specific variable and the signal handler writes the signal number into that pipe. The read end of the pipe is registered with select/epoll in that same thread, so that I can handle signals outside of the signal context. E.g.:

__thread int signal_pipe; // initialized at thread start

extern "C" void signal_handler(int signo, siginfo_t*, void*)
{
    unsigned char signo_byte = static_cast<unsigned>(signo); // truncate
    ::write(signal_pipe, &signo_byte, 1); // standard unix self pipe trick
}
like image 163
Maxim Egorushkin Avatar answered Nov 25 '22 04:11

Maxim Egorushkin