xref: /llvm-project/lldb/test/API/functionalities/fork/concurrent_vfork/main.cpp (revision 36f866c6ec3f6671fd4178ed4e49fd632a335cc2)
18bdddcf0Sjeffreytan81 #include <assert.h>
2ec72909bSJordan Rupprecht #include <cstring>
38bdddcf0Sjeffreytan81 #include <iostream>
48bdddcf0Sjeffreytan81 #include <mutex>
5*36f866c6Sjeffreytan81 #include <string.h>
68bdddcf0Sjeffreytan81 #include <sys/wait.h>
78bdddcf0Sjeffreytan81 #include <thread>
88bdddcf0Sjeffreytan81 #include <unistd.h>
98bdddcf0Sjeffreytan81 #include <vector>
108bdddcf0Sjeffreytan81 
118bdddcf0Sjeffreytan81 pid_t g_pid = 0;
128bdddcf0Sjeffreytan81 std::mutex g_child_pids_mutex;
138bdddcf0Sjeffreytan81 std::vector<pid_t> g_child_pids;
148bdddcf0Sjeffreytan81 
158bdddcf0Sjeffreytan81 const char *g_program = nullptr;
168bdddcf0Sjeffreytan81 bool g_use_vfork = true;  // Use vfork by default.
178bdddcf0Sjeffreytan81 bool g_call_exec = false; // Does not call exec by default.
188bdddcf0Sjeffreytan81 
call_vfork(int index)198bdddcf0Sjeffreytan81 int call_vfork(int index) {
208bdddcf0Sjeffreytan81   pid_t child_pid = 0;
218bdddcf0Sjeffreytan81   if (g_use_vfork) {
228bdddcf0Sjeffreytan81     child_pid = vfork();
238bdddcf0Sjeffreytan81   } else {
248bdddcf0Sjeffreytan81     child_pid = fork();
258bdddcf0Sjeffreytan81   }
268bdddcf0Sjeffreytan81 
278bdddcf0Sjeffreytan81   if (child_pid == -1) {
288bdddcf0Sjeffreytan81     // Error handling
298bdddcf0Sjeffreytan81     perror("vfork");
308bdddcf0Sjeffreytan81     return 1;
318bdddcf0Sjeffreytan81   } else if (child_pid == 0) {
328bdddcf0Sjeffreytan81     // This code is executed by the child process
338bdddcf0Sjeffreytan81     g_pid = getpid();
348bdddcf0Sjeffreytan81     printf("Child process: %d\n", g_pid);
358bdddcf0Sjeffreytan81 
368bdddcf0Sjeffreytan81     if (g_call_exec) {
378bdddcf0Sjeffreytan81       std::string child_exit_code = std::to_string(index + 10);
388bdddcf0Sjeffreytan81       execl(g_program, g_program, "--child", child_exit_code.c_str(), NULL);
398bdddcf0Sjeffreytan81     } else {
408bdddcf0Sjeffreytan81       _exit(index + 10);
418bdddcf0Sjeffreytan81     }
428bdddcf0Sjeffreytan81   } else {
438bdddcf0Sjeffreytan81     // This code is executed by the parent process
448bdddcf0Sjeffreytan81     printf("[Parent] Forked process id: %d\n", child_pid);
458bdddcf0Sjeffreytan81   }
468bdddcf0Sjeffreytan81   return 0;
478bdddcf0Sjeffreytan81 }
488bdddcf0Sjeffreytan81 
wait_all_children_to_exit()498bdddcf0Sjeffreytan81 void wait_all_children_to_exit() {
508bdddcf0Sjeffreytan81   std::lock_guard<std::mutex> Lock(g_child_pids_mutex);
518bdddcf0Sjeffreytan81   for (pid_t child_pid : g_child_pids) {
528bdddcf0Sjeffreytan81     int child_status = 0;
538bdddcf0Sjeffreytan81     pid_t pid = waitpid(child_pid, &child_status, 0);
548bdddcf0Sjeffreytan81     if (child_status != 0) {
558bdddcf0Sjeffreytan81       int exit_code = WEXITSTATUS(child_status);
568bdddcf0Sjeffreytan81       if (exit_code > 15 || exit_code < 10) {
578bdddcf0Sjeffreytan81         printf("Error: child process exits with unexpected code %d\n",
588bdddcf0Sjeffreytan81                exit_code);
598bdddcf0Sjeffreytan81         _exit(1); // This will let our program know that some child processes
608bdddcf0Sjeffreytan81                   // didn't exist with an expected exit status.
618bdddcf0Sjeffreytan81       }
628bdddcf0Sjeffreytan81     }
638bdddcf0Sjeffreytan81     if (pid != child_pid)
648bdddcf0Sjeffreytan81       _exit(2); // This will let our program know it didn't succeed
658bdddcf0Sjeffreytan81   }
668bdddcf0Sjeffreytan81 }
678bdddcf0Sjeffreytan81 
create_threads(int num_threads)688bdddcf0Sjeffreytan81 void create_threads(int num_threads) {
698bdddcf0Sjeffreytan81   std::vector<std::thread> threads;
708bdddcf0Sjeffreytan81   for (int i = 0; i < num_threads; ++i) {
718bdddcf0Sjeffreytan81     threads.emplace_back(std::thread(call_vfork, i));
728bdddcf0Sjeffreytan81   }
738bdddcf0Sjeffreytan81   printf("Created %d threads, joining...\n",
748bdddcf0Sjeffreytan81          num_threads); // end_of_create_threads
758bdddcf0Sjeffreytan81   for (auto &thread : threads) {
768bdddcf0Sjeffreytan81     thread.join();
778bdddcf0Sjeffreytan81   }
788bdddcf0Sjeffreytan81   wait_all_children_to_exit();
798bdddcf0Sjeffreytan81 }
808bdddcf0Sjeffreytan81 
818bdddcf0Sjeffreytan81 // Can be called in various ways:
828bdddcf0Sjeffreytan81 // 1. [program]: use vfork and not call exec
838bdddcf0Sjeffreytan81 // 2. [program] --fork: use fork and not call exec
848bdddcf0Sjeffreytan81 // 3. [program] --fork --exec: use fork and call exec
858bdddcf0Sjeffreytan81 // 4. [program] --exec: use vfork and call exec
868bdddcf0Sjeffreytan81 // 5. [program] --child [exit_code]: child process
main(int argc,char * argv[])878bdddcf0Sjeffreytan81 int main(int argc, char *argv[]) {
888bdddcf0Sjeffreytan81   g_pid = getpid();
898bdddcf0Sjeffreytan81   g_program = argv[0];
908bdddcf0Sjeffreytan81 
918bdddcf0Sjeffreytan81   for (int i = 1; i < argc; ++i) {
928bdddcf0Sjeffreytan81     if (strcmp(argv[i], "--child") == 0) {
938bdddcf0Sjeffreytan81       assert(i + 1 < argc);
948bdddcf0Sjeffreytan81       int child_exit_code = std::stoi(argv[i + 1]);
958bdddcf0Sjeffreytan81       printf("Child process: %d, exiting with code %d\n", g_pid,
968bdddcf0Sjeffreytan81              child_exit_code);
978bdddcf0Sjeffreytan81       _exit(child_exit_code);
988bdddcf0Sjeffreytan81     } else if (strcmp(argv[i], "--fork") == 0)
998bdddcf0Sjeffreytan81       g_use_vfork = false;
1008bdddcf0Sjeffreytan81     else if (strcmp(argv[i], "--exec") == 0)
1018bdddcf0Sjeffreytan81       g_call_exec = true;
1028bdddcf0Sjeffreytan81   }
1038bdddcf0Sjeffreytan81 
1048bdddcf0Sjeffreytan81   int num_threads = 5; // break here
1058bdddcf0Sjeffreytan81   create_threads(num_threads);
1068bdddcf0Sjeffreytan81   return 0;
1078bdddcf0Sjeffreytan81 }
108