returns FORKED_PARENT, FORKED_GRANDCHILD, FORKED_CKPT_FAILED
| 108 | |
| 109 | // returns FORKED_PARENT, FORKED_GRANDCHILD, FORKED_CKPT_FAILED |
| 110 | static forked_rc_t |
| 111 | double_fork() |
| 112 | { |
| 113 | pid_t pid; |
| 114 | // 0. This assumes SIGCHLD is already blocked. This works during checkpoint. |
| 115 | struct sigaction saved_sigchld_action; |
| 116 | // 1. Set SIGCHLD handler to trivial handler; save original SIGCHLD handler |
| 117 | // NOTE: allow_delivery_of_one_sigchld calls sigsuspend(). This is unblocked |
| 118 | // when SIGCHLD arrives, only for trivial_sigchld_action, not SIG_IGN. |
| 119 | struct sigaction trivial_sigchld_action; |
| 120 | set_trivial_sigchld_action(&trivial_sigchld_action); |
| 121 | sigaction(SIGCHLD, &trivial_sigchld_action, &saved_sigchld_action); |
| 122 | pid = _real_sys_fork(); |
| 123 | JASSERT(pid >= 0)(pid); // Or could do: return FORKED_CKPT_FAILED; |
| 124 | |
| 125 | if (pid > 0) { |
| 126 | // 2. Allow delivery of just one SIGCHLD (due to the child that did _exit()) |
| 127 | allow_delivery_of_one_sigchld(); |
| 128 | |
| 129 | // 3. Next, we reap that child process (which is now a zombie). |
| 130 | JWARNING(_real_waitpid(pid, NULL, 0) != -1) (pid) (JASSERT_ERRNO); |
| 131 | |
| 132 | // 4. ... and we then restore the original SIGCHLD handler |
| 133 | sigaction(SIGCHLD, &saved_sigchld_action, NULL); |
| 134 | return FORKED_CKPT_PARENT; |
| 135 | } else { |
| 136 | pid = _real_sys_fork(); |
| 137 | JASSERT(pid >= 0)(pid); |
| 138 | if (pid > 0) { |
| 139 | _exit(EXIT_SUCCESS); // Child process exits, delivers SIGCHLD |
| 140 | } |
| 141 | return FORKED_CKPT_GRANDCHILD; |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | /* |
| 146 | * This function returns the fd to which the checkpoint file should be written. |