mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-17 10:06:36 +00:00

Commit efd254b6362 ("tsan: fix deadlock in pthread_atfork callbacks") fixed another deadlock related to atfork handling. But builders with DCHECKs enabled reported failures of pthread_atfork_deadlock2.c and pthread_atfork_deadlock3.c tests related to the fact that we hold runtime locks on interceptor exit: https://lab.llvm.org/buildbot/#/builders/70/builds/6727 This issue is somewhat inherent to the current approach, we indeed execute user code (atfork callbacks) with runtime lock held. Refactor fork handling to not run user code (atfork callbacks) with runtime locks held. This change does this by installing own atfork callbacks during runtime initialization. Atfork callbacks run in LIFO order, so the expectation is that our callbacks run last, right before the actual fork. This way we lock runtime mutexes around fork, but not around user callbacks. Extend tests to also install after fork callbacks just to cover more scenarios. Some tests also started reporting real races that we previously suppressed. Also extend tests to cover fork syscall support. Reviewed By: vitalybuka Differential Revision: https://reviews.llvm.org/D101517
54 lines
1.2 KiB
C
54 lines
1.2 KiB
C
// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
|
|
// Regression test for
|
|
// https://groups.google.com/d/msg/thread-sanitizer/e_zB9gYqFHM/DmAiTsrLAwAJ
|
|
// pthread_atfork() callback triggers a data race and we deadlocked
|
|
// on the report_mtx as we lock it around fork.
|
|
#include "test.h"
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <errno.h>
|
|
|
|
int glob = 0;
|
|
|
|
void *worker(void *unused) {
|
|
glob++;
|
|
barrier_wait(&barrier);
|
|
return NULL;
|
|
}
|
|
|
|
void atfork() {
|
|
glob++;
|
|
}
|
|
|
|
int main() {
|
|
barrier_init(&barrier, 2);
|
|
pthread_atfork(atfork, atfork, atfork);
|
|
pthread_t t;
|
|
pthread_create(&t, NULL, worker, NULL);
|
|
barrier_wait(&barrier);
|
|
pid_t pid = fork();
|
|
if (pid < 0) {
|
|
fprintf(stderr, "fork failed: %d\n", errno);
|
|
return 1;
|
|
}
|
|
if (pid == 0) {
|
|
fprintf(stderr, "CHILD\n");
|
|
return 0;
|
|
}
|
|
if (pid != waitpid(pid, NULL, 0)) {
|
|
fprintf(stderr, "waitpid failed: %d\n", errno);
|
|
return 1;
|
|
}
|
|
pthread_join(t, NULL);
|
|
fprintf(stderr, "PARENT\n");
|
|
return 0;
|
|
}
|
|
|
|
// CHECK: ThreadSanitizer: data race
|
|
// CHECK: Write of size 4
|
|
// CHECK: #0 atfork
|
|
// CHECK: Previous write of size 4
|
|
// CHECK: #0 worker
|
|
// CHECK: CHILD
|
|
// CHECK: PARENT
|