Zth (libzth)
worker.cpp
Go to the documentation of this file.
1 /*
2  * Zth (libzth), a cooperative userspace multitasking library.
3  * Copyright (C) 2019-2022 Jochem Rutgers
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
8  */
9 
10 #include <libzth/worker.h>
11 
12 #include <libzth/async.h>
13 
14 #include <csignal>
15 #include <vector>
16 
17 #ifndef ZTH_OS_WINDOWS
18 # include <sys/types.h>
19 # include <sys/wait.h>
20 # include <unistd.h>
21 #endif
22 
23 #ifdef ZTH_OS_MAC
24 # include <crt_externs.h>
25 # define environ (*_NSGetEnviron())
26 #endif
27 
28 namespace zth {
29 
30 #if !defined(ZTH_OS_WINDOWS) && !defined(ZTH_OS_BAREMETAL)
31 static void sigchld_handler(int /*unused*/);
32 #endif
33 
35 {
36  zth_dbg(banner, "%s", banner());
37 #if !defined(ZTH_OS_WINDOWS) && !defined(ZTH_OS_BAREMETAL)
38 # ifdef ZTH_USE_VALGRIND
39  // valgrind does not seem to like the sigaction below. Not sure why.
40  if(!RUNNING_ON_VALGRIND) // NOLINT(hicpp-no-assembler)
41 # endif
42  {
43  struct sigaction sa = {};
44  sa.sa_handler = &sigchld_handler;
45  sigaction(SIGCHLD, &sa, nullptr);
46  }
47 #endif
48 }
50 
51 #ifdef ZTH_USE_PTHREAD
52 static void* worker_main(void* fiber)
53 {
54  Worker w;
55  w << (Fiber*)fiber;
56  w.run();
57  return nullptr;
58 }
59 #endif
60 
66  UNUSED_PAR(void (*f)()), size_t UNUSED_PAR(stack), char const* UNUSED_PAR(name))
67 {
68 #ifdef ZTH_USE_PTHREAD
69  pthread_t t;
70  int res = 0;
71 
72  zth_dbg(thread, "[%s] starting new Worker", thread_id_str().c_str());
73 
74  Fiber* fiber = new Fiber((void (*)(void*))f, nullptr);
75  if(stack)
76  if((res = fiber->setStackSize(stack)))
77  return res;
78 
79  if(name)
80  fiber->setName(name);
81 
82  if((res = pthread_create(&t, nullptr, &worker_main, (void*)fiber)))
83  return res;
84 
85  pthread_detach(t);
86  return 0;
87 #else
88  return ENOSYS;
89 #endif
90 }
91 
96 int execlp(char const* file, char const* arg, ... /*, nullptr */)
97 {
99  int res = 0;
100 
101  try {
102  va_list args;
103  va_start(args, arg);
104  while(true) {
105  char const* a = va_arg(args, char const*);
106  if(!a) {
107  argv.push_back(nullptr);
108  break;
109  } else {
110  char* argcopy = strdup(a);
111  if(!argcopy) {
112  res = ENOMEM;
113  break;
114  }
115  argv.push_back(argcopy);
116  }
117  }
118  va_end(args);
119  } catch(std::bad_alloc const&) {
120  res = ENOMEM;
121  } catch(...) {
122  res = EAGAIN;
123  }
124 
125  if(!res)
126  res = execvp(file, argv.data());
127 
128  for(size_t i = 0; i < argv.size(); i++)
129  if(argv[i])
130  free(argv[i]);
131 
132  return res;
133 }
134 
135 __attribute__((unused)) static cow_string thread_id_str() noexcept
136 {
137  Worker* w = Worker::instance();
138  if(w)
139  return w->id_str();
140 
141  return format(
142  "pid %u",
143 #ifdef ZTH_OS_WINDOWS
144  (unsigned int)_getpid()
145 #else
146  (unsigned int)getpid()
147 #endif
148  );
149 }
150 
155 int execvp(char const* UNUSED_PAR(file), char* const UNUSED_PAR(arg[]))
156 {
157 #if defined(ZTH_OS_WINDOWS) || defined(ZTH_OS_BAREMETAL)
158  return ENOSYS;
159 #else
160  perf_syscall("execvp()");
161  zth_dbg(thread, "[%s] execvp %s", thread_id_str().c_str(), file);
162 
163  pid_t pid = 0;
164  if((pid = fork()) == 0) {
165  // In child.
166  execve(file, arg, environ);
167  // If we get here, we could not create the process.
168  _exit(127);
169  // Unreachable.
170  return EAGAIN;
171  } else if(pid == -1) {
172  int res = errno;
173  zth_dbg(thread, "[%s] Could not fork(); %s", thread_id_str().c_str(),
174  err(res).c_str());
175  return res;
176  } else
177  return 0;
178 #endif
179 }
180 
181 #if !defined(ZTH_OS_WINDOWS) && !defined(ZTH_OS_BAREMETAL)
182 static volatile sig_atomic_t sigchld_cleanup = 0;
183 
184 static void sigchld_handler(int /*unused*/)
185 {
186  sigchld_cleanup = 1;
187 }
188 #endif
189 
191 {
192 #if !defined(ZTH_OS_WINDOWS) && !defined(ZTH_OS_BAREMETAL)
193 # ifdef ZTH_USE_VALGRIND
194  // The sigaction was disabled, so sigchld_cleanup will always be 0.
195  if(!RUNNING_ON_VALGRIND) // NOLINT(hicpp-no-assembler)
196 # endif
197  if(likely(sigchld_cleanup == 0))
198  return;
199 
200  Worker* w = Worker::instance();
201  char const* id_str = w ? w->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:52
static safe_ptr< singleton_type >::type instance() noexcept
Return the only instance of T within this thread.
Definition: util.h:989
virtual char const * id_str() const override
Definition: util.h:751
The class that manages the fibers within this thread.
Definition: worker.h:38
void run(TimeInterval const &duration=TimeInterval())
Definition: worker.h:306
A simple std::vector, which can contain Prealloc without heap allocation.
Definition: util.h:1021
void push_back(value_type const &v)
Append an element to the vector using the copy constructor.
Definition: util.h:1203
size_t size() const noexcept
Return the number of elements stored in the vector.
Definition: util.h:1143
value_type * data() noexcept
Access the data array.
Definition: util.h:1119
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:65
int execlp(char const *file, char const *arg,...)
Start an external program.
Definition: worker.cpp:96
fiber_type< F >::factory fiber(F f, char const *name=nullptr)
Create a new fiber.
Definition: async.h:713
int execvp(char const *file, char *const arg[])
Start an external program.
Definition: worker.cpp:155
#define zth_dbg(group, fmt, a...)
Debug printf()-like function.
Definition: util.h:210
char const * banner() noexcept
Prints a banner line with version and configuration information.
Definition: util.cpp:34
#define ZTH_INIT_CALL(f)
Definition: init.h:51
#define UNUSED_PAR(name)
Definition: macros.h:79
Definition: allocator.h:23
void worker_global_init()
Definition: worker.cpp:34
void sigchld_check()
Definition: worker.cpp:190
string err(int e)
Return a string like strerror() does, but as a zth::string.
Definition: util.h:617
string format(char const *fmt,...)
Format like sprintf(), but save the result in an zth::string.
Definition: util.h:503
void perf_syscall(char const *syscall, Timestamp const &t=Timestamp())
Put a syscall into the perf output.
Definition: perf.h:359
#define likely(expr)
Marks the given expression to likely be evaluated to true.
Definition: util.h:42