1#include <assert.h>
2#include <cstring>
3#include <iostream>
4#include <mutex>
5#include <string.h>
6#include <sys/wait.h>
7#include <thread>
8#include <unistd.h>
9#include <vector>
10
11pid_t g_pid = 0;
12std::mutex g_child_pids_mutex;
13std::vector<pid_t> g_child_pids;
14
15const char *g_program = nullptr;
16bool g_use_vfork = true; // Use vfork by default.
17bool g_call_exec = false; // Does not call exec by default.
18
19int call_vfork(int index) {
20 pid_t child_pid = 0;
21 if (g_use_vfork) {
22 child_pid = vfork();
23 } else {
24 child_pid = fork();
25 }
26
27 if (child_pid == -1) {
28 // Error handling
29 perror(s: "vfork");
30 return 1;
31 } else if (child_pid == 0) {
32 // This code is executed by the child process
33 g_pid = getpid();
34 printf(format: "Child process: %d\n", g_pid);
35
36 if (g_call_exec) {
37 std::string child_exit_code = std::to_string(val: index + 10);
38 execl(path: g_program, arg: g_program, "--child", child_exit_code.c_str(), NULL);
39 } else {
40 _exit(status: index + 10);
41 }
42 } else {
43 // This code is executed by the parent process
44 printf(format: "[Parent] Forked process id: %d\n", child_pid);
45 }
46 return 0;
47}
48
49void wait_all_children_to_exit() {
50 std::lock_guard<std::mutex> Lock(g_child_pids_mutex);
51 for (pid_t child_pid : g_child_pids) {
52 int child_status = 0;
53 pid_t pid = waitpid(pid: child_pid, stat_loc: &child_status, options: 0);
54 if (child_status != 0) {
55 int exit_code = WEXITSTATUS(child_status);
56 if (exit_code > 15 || exit_code < 10) {
57 printf(format: "Error: child process exits with unexpected code %d\n",
58 exit_code);
59 _exit(status: 1); // This will let our program know that some child processes
60 // didn't exist with an expected exit status.
61 }
62 }
63 if (pid != child_pid)
64 _exit(status: 2); // This will let our program know it didn't succeed
65 }
66}
67
68void create_threads(int num_threads) {
69 std::vector<std::thread> threads;
70 for (int i = 0; i < num_threads; ++i) {
71 threads.emplace_back(args: std::thread(call_vfork, i));
72 }
73 printf(format: "Created %d threads, joining...\n",
74 num_threads); // end_of_create_threads
75 for (auto &thread : threads) {
76 thread.join();
77 }
78 wait_all_children_to_exit();
79}
80
81// Can be called in various ways:
82// 1. [program]: use vfork and not call exec
83// 2. [program] --fork: use fork and not call exec
84// 3. [program] --fork --exec: use fork and call exec
85// 4. [program] --exec: use vfork and call exec
86// 5. [program] --child [exit_code]: child process
87int main(int argc, char *argv[]) {
88 g_pid = getpid();
89 g_program = argv[0];
90
91 for (int i = 1; i < argc; ++i) {
92 if (strcmp(s1: argv[i], s2: "--child") == 0) {
93 assert(i + 1 < argc);
94 int child_exit_code = std::stoi(str: argv[i + 1]);
95 printf(format: "Child process: %d, exiting with code %d\n", g_pid,
96 child_exit_code);
97 _exit(status: child_exit_code);
98 } else if (strcmp(s1: argv[i], s2: "--fork") == 0)
99 g_use_vfork = false;
100 else if (strcmp(s1: argv[i], s2: "--exec") == 0)
101 g_call_exec = true;
102 }
103
104 int num_threads = 5; // break here
105 create_threads(num_threads);
106 return 0;
107}
108

source code of lldb/test/API/functionalities/fork/concurrent_vfork/main.cpp