xref: /llvm-project/lldb/test/API/functionalities/fork/concurrent_vfork/main.cpp (revision 36f866c6ec3f6671fd4178ed4e49fd632a335cc2)
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 
11 pid_t g_pid = 0;
12 std::mutex g_child_pids_mutex;
13 std::vector<pid_t> g_child_pids;
14 
15 const char *g_program = nullptr;
16 bool g_use_vfork = true;  // Use vfork by default.
17 bool g_call_exec = false; // Does not call exec by default.
18 
call_vfork(int index)19 int 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("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("Child process: %d\n", g_pid);
35 
36     if (g_call_exec) {
37       std::string child_exit_code = std::to_string(index + 10);
38       execl(g_program, g_program, "--child", child_exit_code.c_str(), NULL);
39     } else {
40       _exit(index + 10);
41     }
42   } else {
43     // This code is executed by the parent process
44     printf("[Parent] Forked process id: %d\n", child_pid);
45   }
46   return 0;
47 }
48 
wait_all_children_to_exit()49 void 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(child_pid, &child_status, 0);
54     if (child_status != 0) {
55       int exit_code = WEXITSTATUS(child_status);
56       if (exit_code > 15 || exit_code < 10) {
57         printf("Error: child process exits with unexpected code %d\n",
58                exit_code);
59         _exit(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(2); // This will let our program know it didn't succeed
65   }
66 }
67 
create_threads(int num_threads)68 void create_threads(int num_threads) {
69   std::vector<std::thread> threads;
70   for (int i = 0; i < num_threads; ++i) {
71     threads.emplace_back(std::thread(call_vfork, i));
72   }
73   printf("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
main(int argc,char * argv[])87 int 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(argv[i], "--child") == 0) {
93       assert(i + 1 < argc);
94       int child_exit_code = std::stoi(argv[i + 1]);
95       printf("Child process: %d, exiting with code %d\n", g_pid,
96              child_exit_code);
97       _exit(child_exit_code);
98     } else if (strcmp(argv[i], "--fork") == 0)
99       g_use_vfork = false;
100     else if (strcmp(argv[i], "--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