예제 #1
0
파일: mutex.c 프로젝트: Alexander--/samba
_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;
}
예제 #2
-1
파일: mutex.c 프로젝트: Distrotech/samba
_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;
}