Introduce make_pthread

This allows creating a pthread directly, which can be joined.
iothread_spawn wraps this.
This commit is contained in:
ridiculousfish 2019-02-01 01:04:14 -08:00
parent 78bbcef356
commit 6e0dd06f43
3 changed files with 72 additions and 15 deletions

View file

@ -678,6 +678,20 @@ static void test_iothread() {
max_achieved_thread_count); max_achieved_thread_count);
} }
static void test_pthread() {
say(L"Testing pthreads");
pthread_t result = {};
int val = 3;
bool made = make_pthread(&result, [&val](){
val += 2;
});
do_test(made);
void *ignore = nullptr;
int ret = pthread_join(result, &ignore);
do_test(ret == 0);
do_test(val == 5);
}
static parser_test_error_bits_t detect_argument_errors(const wcstring &src) { static parser_test_error_bits_t detect_argument_errors(const wcstring &src) {
parse_node_tree_t tree; parse_node_tree_t tree;
if (!parse_tree_from_string(src, parse_flag_none, &tree, NULL, symbol_argument_list)) { if (!parse_tree_from_string(src, parse_flag_none, &tree, NULL, symbol_argument_list)) {
@ -5083,6 +5097,7 @@ int main(int argc, char **argv) {
if (should_test_function("convert_nulls")) test_convert_nulls(); if (should_test_function("convert_nulls")) test_convert_nulls();
if (should_test_function("tok")) test_tokenizer(); if (should_test_function("tok")) test_tokenizer();
if (should_test_function("iothread")) test_iothread(); if (should_test_function("iothread")) test_iothread();
if (should_test_function("pthread")) test_pthread();
if (should_test_function("parser")) test_parser(); if (should_test_function("parser")) test_parser();
if (should_test_function("cancellation")) test_cancellation(); if (should_test_function("cancellation")) test_cancellation();
if (should_test_function("indents")) test_indents(); if (should_test_function("indents")) test_indents();

View file

@ -3,6 +3,7 @@
#include <limits.h> #include <limits.h>
#include <pthread.h> #include <pthread.h>
#include <signal.h> #include <signal.h>
#include <stdio.h>
#include <sys/select.h> #include <sys/select.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/types.h> #include <sys/types.h>
@ -149,24 +150,14 @@ static void *iothread_worker(void *unused) {
/// Spawn another thread. No lock is held when this is called. /// Spawn another thread. No lock is held when this is called.
static void iothread_spawn() { static void iothread_spawn() {
// The spawned thread inherits our signal mask. We don't want the thread to ever receive signals
// on the spawned thread, so temporarily block all signals, spawn the thread, and then restore
// it.
sigset_t new_set, saved_set;
sigfillset(&new_set);
DIE_ON_FAILURE(pthread_sigmask(SIG_BLOCK, &new_set, &saved_set));
// Spawn a thread. If this fails, it means there's already a bunch of threads; it is very // Spawn a thread. If this fails, it means there's already a bunch of threads; it is very
// unlikely that they are all on the verge of exiting, so one is likely to be ready to handle // unlikely that they are all on the verge of exiting, so one is likely to be ready to handle
// extant requests. So we can ignore failure with some confidence. // extant requests. So we can ignore failure with some confidence.
pthread_t thread = 0; pthread_t thread = 0;
pthread_create(&thread, NULL, iothread_worker, NULL); if (make_pthread(&thread, iothread_worker, nullptr)) {
// We will never join this thread. // We will never join this thread.
DIE_ON_FAILURE(pthread_detach(thread)); DIE_ON_FAILURE(pthread_detach(thread));
debug(5, "pthread %p spawned", (void *)(intptr_t)thread); }
// Restore our sigmask.
DIE_ON_FAILURE(pthread_sigmask(SIG_SETMASK, &saved_set, NULL));
} }
int iothread_perform_impl(void_function_t &&func, void_function_t &&completion) { int iothread_perform_impl(void_function_t &&func, void_function_t &&completion) {
@ -342,3 +333,48 @@ void iothread_perform_on_main(void_function_t &&func) {
// Ok, the request must now be done. // Ok, the request must now be done.
assert(req.done); assert(req.done);
} }
bool make_pthread(pthread_t *result, void *(*func)(void *), void *param) {
// The spawned thread inherits our signal mask. We don't want the thread to ever receive signals
// on the spawned thread, so temporarily block all signals, spawn the thread, and then restore
// it.
sigset_t new_set, saved_set;
sigfillset(&new_set);
DIE_ON_FAILURE(pthread_sigmask(SIG_BLOCK, &new_set, &saved_set));
// Spawn a thread. If this fails, it means there's already a bunch of threads; it is very
// unlikely that they are all on the verge of exiting, so one is likely to be ready to handle
// extant requests. So we can ignore failure with some confidence.
pthread_t thread = 0;
int err = pthread_create(&thread, NULL, func, param);
if (err == 0) {
// Success, return the thread.
debug(5, "pthread %p spawned", (void *)(intptr_t)thread);
*result = thread;
} else {
perror("pthread_create");
}
// Restore our sigmask.
DIE_ON_FAILURE(pthread_sigmask(SIG_SETMASK, &saved_set, NULL));
return err == 0;
}
using void_func_t = std::function<void(void)>;
static void *func_invoker(void *param) {
void_func_t *vf = static_cast<void_func_t *>(param);
(*vf)();
delete vf;
return nullptr;
}
bool make_pthread(pthread_t *result, void_func_t &&func) {
// Copy the function into a heap allocation.
void_func_t *vf = new void_func_t(std::move(func));
if (make_pthread(result, func_invoker, vf)) {
return true;
}
// Thread spawning failed, clean up our heap allocation.
delete vf;
return false;
}

View file

@ -69,10 +69,16 @@ int iothread_perform(const HANDLER &handler, const COMPLETION &completion) {
// variant of iothread_perform without a completion handler // variant of iothread_perform without a completion handler
inline int iothread_perform(std::function<void(void)> &&func) { inline int iothread_perform(std::function<void(void)> &&func) {
return iothread_perform_impl(std::move(func), std::function<void(void)>()); return iothread_perform_impl(std::move(func), {});
} }
/// Performs a function on the main thread, blocking until it completes. /// Performs a function on the main thread, blocking until it completes.
void iothread_perform_on_main(std::function<void(void)> &&func); void iothread_perform_on_main(std::function<void(void)> &&func);
/// Creates a pthread, manipulating the signal mask so that the thread receives no signals.
/// The pthread runs \p func.
/// \returns true on success, false on failure.
bool make_pthread(pthread_t *result, void *(*func)(void *), void *param);
bool make_pthread(pthread_t *result, std::function<void(void)> &&func);
#endif #endif