Mae Milano
Mae Milano

Reputation: 754

is static thread_local memory async signal safet in c++14?

Let's assume that I have this function:

int my_thread_id(){
  static int counter {0};
  thread_local int tid{++counter};
  return tid;
}

Is this function (my_thread_id) async-signal-safe, even on the first call?

Upvotes: 3

Views: 1229

Answers (2)

Sebastian Marsching
Sebastian Marsching

Reputation: 656

This answer can be considered an addendum to the answer given by ildjarn. When strictly speaking about the C++ 14 standard, using data with thread_local storage from a signal handler results in undefined behavior.

On certain platforms, however, such use might be permissible. For example, most POSIX systems implement thread-local storage by using a special data segment that is allocated per-thread (like the stack). Please refer to this document for detailed explanations. In this case, access to thread-local data is async signal safe because it does not involve any locks.

However, the data read or written by the signal handler might still be inconsistent unless only atomics are accessed or access is fenced by using std::atomic_signal_fence. The reason for this is that the compiler has no idea when a signal handler might interrupt execution and might thus reorder read and write instructions. std::atomic_signal_fence prohibits this reordering and reordering by the CPU is not a problem because the execution happens within the same thread and the CPU is only allowed to reorder instructions when the result (within the thread) is the same as if the instructions had been executed in order.

In addition to std::atomic_signal_fence, using variables of type std::atomic is safe, as long they are lock free (as indicated by std::is_lock_free).

On Linux (and I believe most other POSIX platforms), the question whether a signal is dispatched to a specific thread depends on how this signal is generated and the exact type of the signal. For example, SIGSEGV and SIGBUS are always dispatched to the thread that caused the error resulting in the signal. In this case, using thread-local storage can be a convenient way to recover from such errors. However, there is no way to do this while keeping the code portable to all platforms supporting the C++ standard.

Upvotes: 5

ildjarn
ildjarn

Reputation: 62975

No.

Signal handlers have no notion of which thread they're executing on, so thread_local has no valid semantics there. [intro.multithread]p2:

A signal handler that is executed as a result of a call to the raise function belongs to the same thread of execution as the call to the raise function. Otherwise it is unspecified which thread of execution contains a signal handler invocation.

Also relevant is p23:

Two actions are potentially concurrent if

  • they are performed by different threads, or
  • they are unsequenced, and at least one is performed by a signal handler.

The execution of a program contains a data race if it contains two potentially concurrent conflicting actions, at least one of which is not atomic, and neither happens before the other, except for the special case for signal handlers described below. Any such data race results in undefined behavior.

(The special case for signal handlers being referred to is only regarding the type volatile sig_atomic_t and does not apply here.)

The second bullet pertains because of [intro.execution]p6:

If a signal handler is executed as a result of a call to the raise function, then the execution of the handler is sequenced after the invocation of the raise function and before its return. [ Note: When a signal is received for another reason, the execution of the signal handler is usually unsequenced with respect to the rest of the program. —end note ]

Upvotes: 3

Related Questions