Signals and threads on Unix are always areas where interesting behaviour could be seen.
As an example consider the way how synchronous faults are handled.
In this test program I model behaviour of Hotspot VM in regard of safepoint synchronization,
without explicit memory barrier (-XX:-UseMembar, which is default with recent VMs).
Here we check that SEGV is always handled on the same stack for the same thread.
This program shows that on Solaris, Linux, FreeBSD synchronous faults are always handled
on the stack of thread which produced fault.
Update: thanks to Dave Holmes for explanations. This behavior is standardized by POSIX, and if signal is masked, it'll remain pending.
Update: thanks to Dave Holmes for explanations. This behavior is standardized by POSIX, and if signal is masked, it'll remain pending.
"Signals which are generated by some action attributable to a particular thread, such as a hardware fault, shall be generated for the thread that caused the signal to be generated."
"If the action associated with a blocked signal is anything other than to ignore the signal, and if that signal is generated for the thread, the signal shall remain pending until it is unblocked, it is accepted when it is selected and returned by a call to the sigwait() function, or the action associated with it is set to ignore the signal."
#define _GNU_SOURCE 1 #include <pthread.h> #include <signal.h> #include <stdio.h> #include <unistd.h> #include <ucontext.h> #include <sys/mman.h> #include <string.h> #define NT 10 volatile intptr_t sps[NT]; void handle_signal(int sig, siginfo_t* info, void* ucVoid) { ucontext_t* uc = (ucontext_t*) ucVoid; intptr_t sp = 0; int nt; #ifdef __linux__ #ifdef __i386__ #define MY_SP REG_ESP #endif #ifdef __amd64__ #define MY_SP REG_RSP #endif #endif #if defined(__sun__) #define MY_SP REG_SP #endif sp = uc->uc_mcontext.gregs[MY_SP]; nt = ((intptr_t)info->si_addr & 0xff); if (sps[nt] == 0) { sps[nt] = (intptr_t)&uc; } if (sps[nt] != (intptr_t)&uc) { fprintf(stderr, "fault at %p sp=%p mine=%p\n", info->si_addr, sp, &uc); } else { fprintf(stderr, "."); } } void setup_sig() { struct sigaction act; memset(&act, 0, sizeof act); act.sa_handler = NULL; act.sa_sigaction = handle_signal; act.sa_flags = SA_SIGINFO | SA_NODEFER; sigaction(SIGSEGV, &act, NULL); sigaction(SIGBUS, &act, NULL); } char* page; void* runner(void* arg) { int t = (int)(intptr_t)arg, i; for (i=0; i<1000000; i++) { page[t]++; } } int main() { pthread_t threads[NT]; int i, ps; memset((void*)sps, 0, sizeof sps); ps = getpagesize(); page = (char*)mmap(NULL, ps, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); setup_sig(); for (i=0; i<NT; i++) { pthread_create(threads+i, NULL, runner, (void*)(intptr_t)i); } for (i=0; i<1000000; i++) { mprotect(page, ps, PROT_NONE); mprotect(page, ps, PROT_READ | PROT_WRITE); } for (i=0; i<NT; i++) { pthread_join(threads[i], NULL); } return 0; }