_PUBLIC_ bool tdb_runtime_check_for_robust_mutexes(void) { void *ptr = NULL; pthread_mutex_t *m = NULL; pthread_mutexattr_t ma; int ret = 1; int pipe_down[2] = { -1, -1 }; int pipe_up[2] = { -1, -1 }; ssize_t nread; char c = 0; bool ok; static bool initialized; pid_t saved_child_pid = -1; bool cleanup_ma = false; if (initialized) { return tdb_mutex_locking_cached; } initialized = true; ok = tdb_mutex_locking_supported(); if (!ok) { return false; } tdb_mutex_locking_cached = false; ptr = mmap(NULL, sizeof(pthread_mutex_t), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1 /* fd */, 0); if (ptr == MAP_FAILED) { return false; } ret = pipe(pipe_down); if (ret != 0) { goto cleanup; } ret = pipe(pipe_up); if (ret != 0) { goto cleanup; } ret = pthread_mutexattr_init(&ma); if (ret != 0) { goto cleanup; } cleanup_ma = true; ret = pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_ERRORCHECK); if (ret != 0) { goto cleanup; } ret = pthread_mutexattr_setpshared(&ma, PTHREAD_PROCESS_SHARED); if (ret != 0) { goto cleanup; } ret = pthread_mutexattr_setrobust(&ma, PTHREAD_MUTEX_ROBUST); if (ret != 0) { goto cleanup; } ret = pthread_mutex_init(ptr, &ma); if (ret != 0) { goto cleanup; } m = (pthread_mutex_t *)ptr; if (tdb_robust_mutex_setup_sigchild(tdb_robust_mutex_handler, &tdb_robust_mutext_old_handler) == false) { goto cleanup; } tdb_robust_mutex_pid = fork(); saved_child_pid = tdb_robust_mutex_pid; if (tdb_robust_mutex_pid == 0) { size_t nwritten; close(pipe_down[1]); close(pipe_up[0]); ret = pthread_mutex_lock(m); nwritten = write(pipe_up[1], &ret, sizeof(ret)); if (nwritten != sizeof(ret)) { _exit(1); } if (ret != 0) { _exit(1); } nread = read(pipe_down[0], &c, 1); if (nread != 1) { _exit(1); } /* leave locked */ _exit(0); } if (tdb_robust_mutex_pid == -1) { goto cleanup; } close(pipe_down[0]); pipe_down[0] = -1; close(pipe_up[1]); pipe_up[1] = -1; nread = read(pipe_up[0], &ret, sizeof(ret)); if (nread != sizeof(ret)) { goto cleanup; } ret = pthread_mutex_trylock(m); if (ret != EBUSY) { if (ret == 0) { pthread_mutex_unlock(m); } goto cleanup; } if (write(pipe_down[1], &c, 1) != 1) { goto cleanup; } nread = read(pipe_up[0], &c, 1); if (nread != 0) { goto cleanup; } tdb_robust_mutex_wait_for_child(&saved_child_pid); ret = pthread_mutex_trylock(m); if (ret != EOWNERDEAD) { if (ret == 0) { pthread_mutex_unlock(m); } goto cleanup; } ret = pthread_mutex_consistent(m); if (ret != 0) { goto cleanup; } ret = pthread_mutex_trylock(m); if (ret != EDEADLK && ret != EBUSY) { pthread_mutex_unlock(m); goto cleanup; } ret = pthread_mutex_unlock(m); if (ret != 0) { goto cleanup; } tdb_mutex_locking_cached = true; cleanup: /* * Note that we don't reset the signal handler we just reset * tdb_robust_mutex_pid to -1. This is ok as this code path is only * called once per process. * * Leaving our signal handler avoids races with other threads potentialy * setting up their SIGCHLD handlers. * * The worst thing that can happen is that the other newer signal * handler will get the SIGCHLD signal for our child and/or reap the * child with a wait() function. tdb_robust_mutex_wait_for_child() * handles the case where waitpid returns ECHILD. */ tdb_robust_mutex_wait_for_child(&saved_child_pid); if (m != NULL) { pthread_mutex_destroy(m); } if (cleanup_ma) { pthread_mutexattr_destroy(&ma); } if (pipe_down[0] != -1) { close(pipe_down[0]); } if (pipe_down[1] != -1) { close(pipe_down[1]); } if (pipe_up[0] != -1) { close(pipe_up[0]); } if (pipe_up[1] != -1) { close(pipe_up[1]); } if (ptr != NULL) { munmap(ptr, sizeof(pthread_mutex_t)); } return tdb_mutex_locking_cached; }
_PUBLIC_ bool tdb_runtime_check_for_robust_mutexes(void) { void *ptr; pthread_mutex_t *m; pthread_mutexattr_t ma; int ret = 1; int pipe_down[2] = { -1, -1 }; int pipe_up[2] = { -1, -1 }; ssize_t nread; char c = 0; bool ok; int status; static bool initialized; if (initialized) { return tdb_mutex_locking_cached; } initialized = true; ok = tdb_mutex_locking_supported(); if (!ok) { return false; } tdb_mutex_locking_cached = false; ptr = mmap(NULL, sizeof(pthread_mutex_t), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1 /* fd */, 0); if (ptr == MAP_FAILED) { return false; } m = (pthread_mutex_t *)ptr; ret = pipe(pipe_down); if (ret != 0) { goto cleanup_mmap; } ret = pipe(pipe_up); if (ret != 0) { goto cleanup_pipe; } ret = pthread_mutexattr_init(&ma); if (ret != 0) { goto cleanup_pipe; } ret = pthread_mutexattr_settype(&ma, PTHREAD_MUTEX_ERRORCHECK); if (ret != 0) { goto cleanup_ma; } ret = pthread_mutexattr_setpshared(&ma, PTHREAD_PROCESS_SHARED); if (ret != 0) { goto cleanup_ma; } ret = pthread_mutexattr_setrobust(&ma, PTHREAD_MUTEX_ROBUST); if (ret != 0) { goto cleanup_ma; } ret = pthread_mutex_init(m, &ma); if (ret != 0) { goto cleanup_ma; } if (tdb_robust_mutex_setup_sigchild(tdb_robust_mutex_handler, &tdb_robust_mutext_old_handler) == false) { goto cleanup_ma; } tdb_robust_mutex_pid = fork(); if (tdb_robust_mutex_pid == 0) { size_t nwritten; close(pipe_down[1]); close(pipe_up[0]); ret = pthread_mutex_lock(m); nwritten = write(pipe_up[1], &ret, sizeof(ret)); if (nwritten != sizeof(ret)) { _exit(1); } if (ret != 0) { _exit(1); } nread = read(pipe_down[0], &c, 1); if (nread != 1) { _exit(1); } /* leave locked */ _exit(0); } if (tdb_robust_mutex_pid == -1) { goto cleanup_sig_child; } close(pipe_down[0]); pipe_down[0] = -1; close(pipe_up[1]); pipe_up[1] = -1; nread = read(pipe_up[0], &ret, sizeof(ret)); if (nread != sizeof(ret)) { goto cleanup_child; } ret = pthread_mutex_trylock(m); if (ret != EBUSY) { if (ret == 0) { pthread_mutex_unlock(m); } goto cleanup_child; } if (write(pipe_down[1], &c, 1) != 1) { goto cleanup_child; } nread = read(pipe_up[0], &c, 1); if (nread != 0) { goto cleanup_child; } while (tdb_robust_mutex_pid > 0) { pid_t pid; errno = 0; pid = waitpid(tdb_robust_mutex_pid, &status, 0); if (pid == tdb_robust_mutex_pid) { tdb_robust_mutex_pid = -1; break; } if (pid == -1 && errno != EINTR) { goto cleanup_child; } } tdb_robust_mutex_setup_sigchild(tdb_robust_mutext_old_handler, NULL); ret = pthread_mutex_trylock(m); if (ret != EOWNERDEAD) { if (ret == 0) { pthread_mutex_unlock(m); } goto cleanup_m; } ret = pthread_mutex_consistent(m); if (ret != 0) { goto cleanup_m; } ret = pthread_mutex_trylock(m); if (ret != EDEADLK) { pthread_mutex_unlock(m); goto cleanup_m; } ret = pthread_mutex_unlock(m); if (ret != 0) { goto cleanup_m; } tdb_mutex_locking_cached = true; goto cleanup_m; cleanup_child: while (tdb_robust_mutex_pid > 0) { pid_t pid; kill(tdb_robust_mutex_pid, SIGKILL); errno = 0; pid = waitpid(tdb_robust_mutex_pid, &status, 0); if (pid == tdb_robust_mutex_pid) { tdb_robust_mutex_pid = -1; break; } if (pid == -1 && errno != EINTR) { break; } } cleanup_sig_child: tdb_robust_mutex_setup_sigchild(tdb_robust_mutext_old_handler, NULL); cleanup_m: pthread_mutex_destroy(m); cleanup_ma: pthread_mutexattr_destroy(&ma); cleanup_pipe: if (pipe_down[0] != -1) { close(pipe_down[0]); } if (pipe_down[1] != -1) { close(pipe_down[1]); } if (pipe_up[0] != -1) { close(pipe_up[0]); } if (pipe_up[1] != -1) { close(pipe_up[1]); } cleanup_mmap: munmap(ptr, sizeof(pthread_mutex_t)); return tdb_mutex_locking_cached; }