Zth (libzth)
Loading...
Searching...
No Matches
worker.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2019-2026 Jochem Rutgers
3 *
4 * SPDX-License-Identifier: MPL-2.0
5 */
6
7#include <libzth/worker.h>
8
9#include <libzth/async.h>
10
11#include <csignal>
12#include <vector>
13
14#ifndef ZTH_OS_WINDOWS
15# include <sys/types.h>
16# include <sys/wait.h>
17# include <unistd.h>
18#endif
19
20#ifdef ZTH_OS_MAC
21# include <crt_externs.h>
22# define environ (*_NSGetEnviron())
23#endif
24
25namespace zth {
26
27#if !defined(ZTH_OS_WINDOWS) && !defined(ZTH_OS_BAREMETAL)
28static void sigchld_handler(int /*unused*/);
29#endif
30
32{
33 zth_dbg(banner, "%s", banner());
34#if !defined(ZTH_OS_WINDOWS) && !defined(ZTH_OS_BAREMETAL)
35# ifdef ZTH_USE_VALGRIND
36 // valgrind does not seem to like the sigaction below. Not sure why.
37 if(!RUNNING_ON_VALGRIND) // NOLINT(hicpp-no-assembler)
38# endif
39 {
40 struct sigaction sa = {};
41 sa.sa_handler = &sigchld_handler;
42 sigaction(SIGCHLD, &sa, nullptr);
43 }
44#endif
45}
46ZTH_INIT_CALL(worker_global_init)
47
48#ifdef ZTH_USE_PTHREAD
49static void* worker_main(void* fiber)
50{
51 Worker w;
52 w << (Fiber*)fiber;
53 w.run();
54 return nullptr;
55}
56#endif
57
63 UNUSED_PAR(void (*f)()), size_t UNUSED_PAR(stack), char const* UNUSED_PAR(name))
64{
65#ifdef ZTH_USE_PTHREAD
66 pthread_t t;
67 int res = 0;
68
69 zth_dbg(thread, "[%s] starting new Worker", thread_id_str().c_str());
70
71 Fiber* fiber = new Fiber((void (*)(void*))f, nullptr);
72 if(stack)
73 if((res = fiber->setStackSize(stack)))
74 return res;
75
76 if(name)
77 fiber->setName(name);
78
79 if((res = pthread_create(&t, nullptr, &worker_main, (void*)fiber)))
80 return res;
81
82 pthread_detach(t);
83 return 0;
84#else
85 return ENOSYS;
86#endif
87}
88
93int execlp(char const* file, char const* arg, ... /*, nullptr */)
94{
96 int res = 0;
97
98 try {
99 va_list args;
100 va_start(args, arg);
101 while(true) {
102 char const* a = va_arg(args, char const*);
103 if(!a) {
104 argv.push_back(nullptr);
105 break;
106 } else {
107 char* argcopy = strdup(a);
108 if(!argcopy) {
109 res = ENOMEM;
110 break;
111 }
112 argv.push_back(argcopy);
113 }
114 }
115 va_end(args);
116 } catch(std::bad_alloc const&) {
117 res = ENOMEM;
118 } catch(...) {
119 res = EAGAIN;
120 }
121
122 if(!res)
123 res = execvp(file, argv.data());
124
125 for(size_t i = 0; i < argv.size(); i++)
126 if(argv[i])
127 free(argv[i]); // NOLINT
128
129 return res;
130}
131
132__attribute__((unused)) static cow_string thread_id_str() noexcept
133{
134 Worker const* w = Worker::instance();
135 if(w)
136 return w->id_str();
137
138 return format(
139 "pid %u",
140#ifdef ZTH_OS_WINDOWS
141 (unsigned int)_getpid()
142#else
143 (unsigned int)getpid()
144#endif
145 );
146}
147
152// cppcheck-suppress constParameter
153int execvp(char const* UNUSED_PAR(file), char* const UNUSED_PAR(arg[]))
154{
155#if defined(ZTH_OS_WINDOWS) || defined(ZTH_OS_BAREMETAL)
156 return ENOSYS;
157#else
158 perf_syscall("execvp()");
159 zth_dbg(thread, "[%s] execvp %s", thread_id_str().c_str(), file);
160
161 pid_t pid = fork();
162 if(!pid) {
163 // In child.
164 execve(file, arg, environ);
165 // If we get here, we could not create the process.
166 _exit(127);
167 // Unreachable.
168 return EAGAIN;
169 } else if(pid == -1) {
170 int res = errno;
171 zth_dbg(thread, "[%s] Could not fork(); %s", thread_id_str().c_str(),
172 err(res).c_str());
173 return res;
174 } else
175 return 0;
176#endif
177}
178
179#if !defined(ZTH_OS_WINDOWS) && !defined(ZTH_OS_BAREMETAL)
180// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
181static volatile sig_atomic_t sigchld_cleanup = 0;
182
183static void sigchld_handler(int /*unused*/)
184{
185 sigchld_cleanup = 1;
186}
187#endif
188
190{
191#if !defined(ZTH_OS_WINDOWS) && !defined(ZTH_OS_BAREMETAL)
192# ifdef ZTH_USE_VALGRIND
193 // The sigaction was disabled, so sigchld_cleanup will always be 0.
194 if(!RUNNING_ON_VALGRIND) // NOLINT(hicpp-no-assembler)
195# endif
196 if(likely(sigchld_cleanup == 0))
197 return;
198
199 Worker* w = Worker::instance();
200 char const* id_str = w ? w->id_str() : "?";
201 (void)id_str;
202
203 while(true) {
204 sigchld_cleanup = 0;
205 int wstatus = 0;
206 pid_t pid = waitpid(-1, &wstatus, WNOHANG);
207
208 if(pid == 0) {
209 // No (other) child exited.
210 return;
211 } else if(pid == -1) {
212 zth_dbg(worker, "[%s] waitpid() failed; %s", id_str, err(errno).c_str());
213 return;
214 } else {
215 zth_dbg(worker, "[%s] Child process %u terminated with exit code %d",
216 id_str, (unsigned int)pid, wstatus);
217 }
218 }
219#endif
220}
221
222} // namespace zth
The fiber.
Definition fiber.h:49
virtual char const * id_str() const override
Definition util.h:809
The class that manages the fibers within this thread.
Definition worker.h:35
void run(TimeInterval const &duration=TimeInterval())
Definition worker.h:307
A simple std::vector, which can contain Prealloc without heap allocation.
Definition util.h:1079
void push_back(value_type const &v)
Append an element to the vector using the copy constructor.
Definition util.h:1262
value_type * data() noexcept
Access the data array.
Definition util.h:1178
size_t size() const noexcept
Return the number of elements stored in the vector.
Definition util.h:1202
int startWorkerThread(void(*f)(), size_t stack=0, char const *name=nullptr)
Start a new thread, create a Worker, with one fiber, which executes f.
Definition worker.cpp:62
int execlp(char const *file, char const *arg,...)
Start an external program.
Definition worker.cpp:93
int execvp(char const *file, char *const arg[])
Start an external program.
Definition worker.cpp:153
fiber_type< F >::factory fiber(F f, char const *name=nullptr)
Create a new fiber.
Definition async.h:754
#define zth_dbg(group, fmt, a...)
Debug printf()-like function.
Definition util.h:189
char const * banner() noexcept
Prints a banner line with version and configuration information.
Definition util.cpp:37
#define ZTH_INIT_CALL(f)
Definition init.h:48
#define UNUSED_PAR(name)
Definition macros.h:78
void worker_global_init()
Definition worker.cpp:31
void sigchld_check()
Definition worker.cpp:189
string err(int e)
Return a string like strerror() does, but as a zth::string.
Definition util.h:675
void perf_syscall(char const *syscall, Timestamp const &t=Timestamp())
Put a syscall into the perf output.
Definition perf.h:361
#define likely(expr)
Marks the given expression to likely be evaluated to true.
Definition util.h:40