mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-29 06:13:20 +00:00
Introduce make_pthread
This allows creating a pthread directly, which can be joined. iothread_spawn wraps this.
This commit is contained in:
parent
78bbcef356
commit
6e0dd06f43
3 changed files with 72 additions and 15 deletions
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue