mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-28 05:43:11 +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);
|
||||
}
|
||||
|
||||
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) {
|
||||
parse_node_tree_t tree;
|
||||
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("tok")) test_tokenizer();
|
||||
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("cancellation")) test_cancellation();
|
||||
if (should_test_function("indents")) test_indents();
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/time.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.
|
||||
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
|
||||
// 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;
|
||||
pthread_create(&thread, NULL, iothread_worker, NULL);
|
||||
|
||||
// We will never join this 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));
|
||||
if (make_pthread(&thread, iothread_worker, nullptr)) {
|
||||
// We will never join this thread.
|
||||
DIE_ON_FAILURE(pthread_detach(thread));
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
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
|
||||
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.
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue