static void qtcSpawnCb(void *_data) { const QtcSpawnData *data = (const QtcSpawnData*)_data; qtcCall(data->cb, data->cb_data); execvp(data->file, data->argv); }
int main() { qtcCall(func_void); assert(qtcCall(func_int, arg_int()) == 0); assert(arg_int_times == 0); func_int = real_func_int; assert(qtcCall(func_int, arg_int()) == 10); assert(arg_int_times == 1); func_void = real_func_void; qtcCall(func_void); assert(real_func_void_times == 1); #ifdef __cplusplus qtcCall(func_struct); #endif return 0; }
QTC_EXPORT bool qtcForkBackground(QtcCallback cb, void *data, QtcCallback fail_cb) { QTC_RET_IF_FAIL(cb, false); // On linux, waitpid will not accept (discard) SIGCHLD therefore if there is // a signal handler registered for SIGCHLD and the child process exit // inside waitpid()/wait(), it will be run after the process state is // cleared and would therefore block if it call wait() (or waitpid(-1)) // and if there are other child processes. As a workaround we only call // waitpid() if the main program did not set up any signal handlers for // SIGCHLD. See (the RATIONALE section of) wait(3P) for more detail. pid_t child = fork(); if (child < 0) { return false; } else if (child == 0) { pid_t grandchild = fork(); if (grandchild < 0) { qtcCall(fail_cb, data); _exit(1); } else if (grandchild == 0) { /* grandchild */ cb(data); _exit(0); } else { _exit(0); } return true; } else { /* parent */ if (qtcSignalHandlerSet(SIGCHLD)) { // If we create a child process, the signal handler will recieve // the signal anyway (and there is no way to only block SIGCHLD // only for our child process). Since the signal handler may // hang and should already take care of getting rid of // zombie processes, we do not call waitpid in this case.... return true; } // If SIGCHLD is ignored, waitpid will return -1 with errno // set to ECHILD, treat this as success (good enough for our purpose // and not likely to fail anyway...) int status = 0; return ((waitpid(child, &status, 0) > 0 && status == 0) || errno == ECHILD); } }
QTC_EXPORT bool qtcForkBackground(QtcCallback cb, void *data, QtcCallback fail_cb) { QTC_RET_IF_FAIL(cb, false); // On linux, waitpid will not accept (discard) SIGCHLD therefore if there is // a signal handler registered for SIGCHLD and the child process exit // inside waitpid()/wait(), it will be run after the process state is // cleared and would therefore block if it call wait() (or waitpid(-1)) // and if there are other child processes. As a workaround we use vfork() // to block the parent until direct child exit so that waitpid() will always // be called after the process exit and would never hang because of signal // handler. See (the RATIONALE section of) wait(3P) for more detail. pid_t child = vfork(); if (child < 0) { return false; } else if (child == 0) { pid_t grandchild = fork(); if (grandchild < 0) { qtcCall(fail_cb, data); _exit(1); } else if (grandchild == 0) { /* grandchild */ cb(data); _exit(0); } else { _exit(0); } return true; } else { /* parent */ // If SIGCHLD is ignored, waitpid will return -1 with errno // set to ECHILD, treat this as success (good enough for our purpose // and not likely to fail anyway...) int status = 0; return ((waitpid(child, &status, 0) > 0 && status == 0) || errno == ECHILD); } }
static void qtcSpawnFailCb(void *_data) { const QtcSpawnData *data = (const QtcSpawnData*)_data; qtcCall(data->fail_cb, data->cb_data); }