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