Skip to content

Instantly share code, notes, and snippets.

@graphitemaster
Last active July 16, 2017 02:11
Show Gist options
  • Select an option

  • Save graphitemaster/3666b721be00e8285efb942c823afc7e to your computer and use it in GitHub Desktop.

Select an option

Save graphitemaster/3666b721be00e8285efb942c823afc7e to your computer and use it in GitHub Desktop.
nvbug work around data races atfork handlers
/* The following is an implementation of pthread_atfork that
* does absolutely nothing when nvidia is being used for rendering.
*
* This is needed because nvidia adds atfork handlers which are not
* async-signal safe and contain minor data-races which can lead to
* unexpected crashes.
*
* For example from valgrind's helgrind
*
* ==17309== Possible data race during write of size 1 at 0x3EEEC5E0 by thread #27
* ==17309== Locks held: 2, at addresses 0x37F01248 0x4FAF8810
* ==17309== at 0x37C5494D: ??? (in /usr/lib/libGLX_nvidia.so.370.28)
* ==17309== by 0xD4A61F5: fork (in /usr/lib/libc-2.24.so)
* ==17309== by 0xD456CC0: _IO_proc_open@@GLIBC_2.2.5 (in /usr/lib/libc-2.24.so)
* ==17309== by 0xD456FB7: popen@@GLIBC_2.2.5 (in /usr/lib/libc-2.24.so)
*
* The general technique involved here is to check /proc/$pid/maps
* and see if there is an nvidia library loaded
*/
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <pthread.h>
#include <unistd.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_once_t g_init = PTHREAD_ONCE_INIT;
static int g_nvidia = 0;
static int (*pthread_atfork_p)(void (*)(void), void (*)(void), void (*)(void));
static int (*__register_atfork_p)(void (*)(void), void(*)(void), void(*)(void), void*);
static void init(void) {
/* The true pthread_atfork and the LSB one*/
*(void **)&pthread_atfork_p = dlsym(RTLD_NEXT, "pthread_atfork");
*(void **)&__register_atfork_p = dlsym(RTLD_NEXT, "__register_atfork");
/* Check if we're running under nvidia */
char path[PATH_MAX], line[LINE_MAX];
pid_t pid = getpid();
sprintf(path, "/proc/%d/maps", (int)pid);
int fd = open(path, O_RDONLY);
while (read(fd, line, sizeof line) > 0) {
static char nvidia[] = "nvidia";
if (!memmem(line, sizeof line, nvidia, sizeof nvidia)) continue;
pthread_mutex_lock(&g_mutex);
g_nvidia = 1;
pthread_mutex_unlock(&g_mutex);
break;
}
close(fd);
}
int __register_atfork(void (*prepare)(void), void (*parent)(void), void(*child)(void), void *dso) {
pthread_once(&g_init, init);
pthread_mutex_lock(&g_mutex);
int status = g_nvidia ? -1 : __register_atfork_p(prepare, parent, child, dso);
pthread_mutex_unlock(&g_mutex);
return status;
}
int register_atfork(void (*prepare)(void), void (*parent)(void), void(*child)(void), void *dso) {
return __register_atfork(prepare, parent, child, dso);
}
int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)) {
pthread_once(&g_init, init);
pthread_mutex_lock(&g_mutex);
int status = g_nvidia ? -1 : pthread_atfork_p(prepare, parent, child);
pthread_mutex_unlock(&g_mutex);
return status;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment