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 ZTH_SHARED_LIB
28// In case of a shared pointer, the static m_instance of the Singleton classes are not shared
29// between the library and application. By defining the instance() function here, we ensure that the
30// same instance is used for both.
31safe_ptr<Worker>::type Worker::instance() noexcept
32{
34}
35#endif // ZTH_SHARED_LIB
36
37#if !defined(ZTH_OS_WINDOWS) && !defined(ZTH_OS_BAREMETAL)
38static void sigchld_handler(int /*unused*/);
39#endif
40
42{
43 zth_dbg(banner, "%s", banner());
44#if !defined(ZTH_OS_WINDOWS) && !defined(ZTH_OS_BAREMETAL)
45# ifdef ZTH_USE_VALGRIND
46 // valgrind does not seem to like the sigaction below. Not sure why.
47 if(!RUNNING_ON_VALGRIND) // NOLINT(hicpp-no-assembler)
48# endif
49 {
50 struct sigaction sa = {};
51 sa.sa_handler = &sigchld_handler;
52 sigaction(SIGCHLD, &sa, nullptr);
53 }
54#endif
55}
56ZTH_INIT_CALL(worker_global_init)
57
58#ifdef ZTH_USE_PTHREAD
59static void* worker_main(void* fiber)
60{
61 Worker w;
62 w << (Fiber*)fiber;
63 w.run();
64 return nullptr;
65}
66#endif
67
73 UNUSED_PAR(void (*f)()), size_t UNUSED_PAR(stack), char const* UNUSED_PAR(name))
74{
75#ifdef ZTH_USE_PTHREAD
76 pthread_t t;
77 int res = 0;
78
79 zth_dbg(thread, "[%s] starting new Worker", thread_id_str().c_str());
80
81 Fiber* fiber = new Fiber((void (*)(void*))f, nullptr);
82 if(stack)
83 if((res = fiber->setStackSize(stack)))
84 return res;
85
86 if(name)
87 fiber->setName(name);
88
89 if((res = pthread_create(&t, nullptr, &worker_main, (void*)fiber)))
90 return res;
91
92 pthread_detach(t);
93 return 0;
94#else
95 return ENOSYS;
96#endif
97}
98
103int execlp(char const* file, char const* arg, ... /*, nullptr */)
104{
106 int res = 0;
107
108 try {
109 va_list args;
110 va_start(args, arg);
111 while(true) {
112 char const* a = va_arg(args, char const*);
113 if(!a) {
114 argv.push_back(nullptr);
115 break;
116 } else {
117 char* argcopy = strdup(a);
118 if(!argcopy) {
119 res = ENOMEM;
120 break;
121 }
122 argv.push_back(argcopy);
123 }
124 }
125 va_end(args);
126 } catch(std::bad_alloc const&) {
127 res = ENOMEM;
128 } catch(...) {
129 res = EAGAIN;
130 }
131
132 if(!res)
133 res = execvp(file, argv.data());
134
135 for(size_t i = 0; i < argv.size(); i++)
136 if(argv[i])
137 free(argv[i]); // NOLINT
138
139 return res;
140}
141
142__attribute__((unused)) static cow_string thread_id_str() noexcept
143{
144 Worker const* w = Worker::instance();
145 if(w)
146 return w->id_str();
147
148 return format(
149 "pid %u",
150#ifdef ZTH_OS_WINDOWS
151 (unsigned int)_getpid()
152#else
153 (unsigned int)getpid()
154#endif
155 );
156}
157
162// cppcheck-suppress constParameter
163int execvp(char const* UNUSED_PAR(file), char* const UNUSED_PAR(arg[]))
164{
165#if defined(ZTH_OS_WINDOWS) || defined(ZTH_OS_BAREMETAL)
166 return ENOSYS;
167#else
168 perf_syscall("execvp()");
169 zth_dbg(thread, "[%s] execvp %s", thread_id_str().c_str(), file);
170
171 pid_t pid = fork();
172 if(!pid) {
173 // In child.
174 execve(file, arg, environ);
175 // If we get here, we could not create the process.
176 _exit(127);
177 // Unreachable.
178 return EAGAIN;
179 } else if(pid == -1) {
180 int res = errno;
181 zth_dbg(thread, "[%s] Could not fork(); %s", thread_id_str().c_str(),
182 err(res).c_str());
183 return res;
184 } else
185 return 0;
186#endif
187}
188
189#if !defined(ZTH_OS_WINDOWS) && !defined(ZTH_OS_BAREMETAL)
190// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
191static volatile sig_atomic_t sigchld_cleanup = 0;
192
193static void sigchld_handler(int /*unused*/)
194{
195 sigchld_cleanup = 1;
196}
197#endif
198
200{
201#if !defined(ZTH_OS_WINDOWS) && !defined(ZTH_OS_BAREMETAL)
202# ifdef ZTH_USE_VALGRIND
203 // The sigaction was disabled, so sigchld_cleanup will always be 0.
204 if(!RUNNING_ON_VALGRIND) // NOLINT(hicpp-no-assembler)
205# endif
206 if(likely(sigchld_cleanup == 0))
207 return;
208
209 Worker* w = Worker::instance();
210 char const* id_str = w ? w->id_str() : "?";
211 (void)id_str;
212
213 while(true) {
214 sigchld_cleanup = 0;
215 int wstatus = 0;
216 pid_t pid = waitpid(-1, &wstatus, WNOHANG);
217
218 if(pid == 0) {
219 // No (other) child exited.
220 return;
221 } else if(pid == -1) {
222 zth_dbg(worker, "[%s] waitpid() failed; %s", id_str, err(errno).c_str());
223 return;
224 } else {
225 zth_dbg(worker, "[%s] Child process %u terminated with exit code %d",
226 id_str, (unsigned int)pid, wstatus);
227 }
228 }
229#endif
230}
231
232} // namespace zth
The fiber.
Definition fiber.h:62
virtual char const * id_str() const noexcept override
Definition util.h:787
static safe_ptr< singleton_type >::type instance() noexcept
Return the only instance of T within this thread.
Definition util.h:1196
The class that manages the fibers within this thread.
Definition worker.h:35
void run(TimeInterval const &duration=TimeInterval())
Definition worker.h:326
A simple std::vector, which can contain Prealloc without heap allocation.
Definition util.h:1228
void push_back(value_type const &v)
Append an element to the vector using the copy constructor.
Definition util.h:1411
value_type * data() noexcept
Access the data array.
Definition util.h:1327
size_t size() const noexcept
Return the number of elements stored in the vector.
Definition util.h:1351
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:72
int execlp(char const *file, char const *arg,...)
Start an external program.
Definition worker.cpp:103
fiber_type< F >::fiber fiber(F &&f, Args &&... args)
Create and start a new fiber.
Definition async.h:1221
int execvp(char const *file, char *const arg[])
Start an external program.
Definition worker.cpp:163
#define zth_dbg(group, fmt, a...)
Debug printf()-like function.
Definition util.h:194
char const * banner() noexcept
Returns a banner line with version and configuration information.
Definition util.cpp:38
#define ZTH_INIT_CALL(f)
Definition init.h:41
#define UNUSED_PAR(name)
Definition macros.h:79
void worker_global_init()
Definition worker.cpp:41
void sigchld_check()
Definition worker.cpp:199
string err(int e)
Return a string like strerror() does, but as a zth::string.
Definition util.h:701
void perf_syscall(char const *syscall, Timestamp const &t=Timestamp()) noexcept
Put a syscall into the perf output.
Definition perf.h:71
#define likely(expr)
Marks the given expression to likely be evaluated to true.
Definition util.h:45