int main() { struct Process *child; struct TagItem tags[] = { { NP_Entry, (IPTR) entry }, { NP_Cli, (IPTR) TRUE }, { NP_Name, (IPTR) "test" }, { NP_NotifyOnDeath, (IPTR) TRUE }, { TAG_DONE, 0 } }; child = CreateNewProc(tags); if(child) { ULONG childid = GetETask((struct Task*) child)->et_UniqueID; Printf("Waiting for child with id %d\n", childid); ChildWait(childid); Printf("Child exited, freeing child\n"); ChildFree(childid); } else PrintFault(IoErr(), "Couldn't create child process"); return 0; }
IPTR TaskGetStorageSlot(struct Task * t, LONG id) { struct ETask *et = t ? GetETask(t) : NULL; IPTR *ts; D(bug("TaskGetStorageSlot: %p: Get TaskGetStorageSlot %d\n", et, id);) if (!et) {
int main() { tmpname = mktemp("T:flockXXXXXX"); int fd = open(tmpname, O_CREAT); TEST((fd != -1)); TEST((flock(fd, LOCK_SH|LOCK_NB) == 0)); TEST((flock(fd, LOCK_UN) == 0)); TEST((flock(fd, LOCK_EX|LOCK_NB) == 0)); TEST((flock(fd, LOCK_UN) == 0)); TEST((flock(fd, LOCK_SH) == 0)); TEST((flock(fd, LOCK_UN) == 0)); TEST((flock(fd, LOCK_EX) == 0)); TEST((flock(fd, LOCK_UN) == 0)); close(fd); /* Create NPROCS processes increasing counter ITERATIONS times in an ugly way */ int counter = 0; struct Process *procs[NPROCS]; ULONG ids[NPROCS]; struct TagItem tags[] = { { NP_Entry, (IPTR) entry }, { NP_Name, (IPTR) "flocker" }, { NP_Output, (IPTR) Output() }, { NP_CloseOutput, (IPTR) FALSE }, { NP_UserData, (IPTR) &counter }, { NP_NotifyOnDeath, (IPTR) TRUE }, { TAG_DONE, 0 } }; int i; InitSemaphore(&sem); for(i = 0; i < NPROCS; i++) { procs[i] = CreateNewProc(tags); TEST((procs[i])); ids[i] = GetETask(procs[i])->et_UniqueID; Signal((struct Task *)procs[i], SIGBREAKF_CTRL_C); } for(i = 0; i < NPROCS; i++) { ChildWait(ids[i]); ChildFree(ids[i]); } putchar('\n'); TEST((counter == NPROCS * ITERATIONS)); cleanup(); return OK; }
int main() { struct Process *child; struct TagItem tags[] = { { NP_Entry, (IPTR) entry }, { NP_Cli, (IPTR) TRUE }, { NP_Name, (IPTR) "test" }, { NP_NotifyOnDeath, (IPTR) TRUE }, { TAG_DONE, 0 } }; child = CreateNewProc(tags); if(child) { ULONG childstatus; ULONG childid = GetETask((struct Task*) child)->et_UniqueID; Printf("Checking status value for non-existing child id\n"); childstatus = ChildStatus(-1); assert(childstatus == CHILD_NOTFOUND); Printf("Result: CHILD_NOTFOUND\n"); Printf("Checking status value for running child id\n"); childstatus = ChildStatus(childid); assert(childstatus == CHILD_ACTIVE); Printf("Result: CHILD_ACTIVE\n"); ChildWait(childid); Printf("Checking status value for died child id\n"); childstatus = ChildStatus(childid); assert(childstatus == CHILD_EXITED); Printf("Result: CHILD_EXITED\n"); ChildFree(childid); Printf("Checking status value for freed child id\n"); childstatus = ChildStatus(childid); assert(childstatus == CHILD_NOTFOUND); Printf("Result: CHILD_NOTFOUND\n"); } else PrintFault(IoErr(), "Couldn't create child process"); return 0; }
EXAMPLE BUGS SEE ALSO INTERNALS *****************************************************************************/ { AROS_LIBFUNC_INIT struct Task *this = FindTask(NULL); struct ETask *et, *child; et = GetETask(this); if(et == NULL) return CHILD_NOTNEW; if(tid == 0L) { ForeachNode(&et->et_Children, child) { /* Don't need to Remove(), because I'll blow away the entire list at the end of the loop. */ child->et_Parent = NULL; } NEWLIST(&et->et_Children); }
EXAMPLE BUGS SEE ALSO RestoreTaskStorage() INTERNALS ******************************************************************************/ { AROS_LIBFUNC_INIT struct Task *ThisTask = GET_THIS_TASK; struct ETask *et = ThisTask ? GetETask(ThisTask) : NULL; IPTR *taskstorage, *tsout; IPTR slots; if (!et || et->et_TaskStorage == NULL) return NULL; taskstorage = et->et_TaskStorage; slots = taskstorage[__TS_FIRSTSLOT]; /* NOTE: Saved task storage *must not* be passed around * to another task! This is why it is not MEMF_PUBLIC. */ tsout = AllocMem(slots * sizeof(tsout[0]), MEMF_ANY); if (tsout) { CopyMemQuick(taskstorage, tsout, slots * sizeof(tsout[0]));
pid_t waitpid( /* SYNOPSIS */ pid_t pid, int *status, int options) /* FUNCTION Waits for child process with given process id to change state. State change is one of the following events: child has exited, child was terminated by a signal, child was stopped by a signal, child was resumed by a signal. The function stores status of the process that changed state in the pointer given as status argument. The following macros can be used to extract information from the status value: WIFEXITED(status) - true if the process has exited WEXITSTATUS(status) - exit status of the exited process WIFSIGNALED(status) - true if the child process was terminated by a signal WTERMSIG(status) - number of the signal that caused process termination WIFSTOPPED(status) - true if the child process was stopped by a signal WSTOPSIG(status) - number of the signal that caused child process stop WIFCONTINUED(status) - true if the child process was resumed by the SIGCONT signal. Unless WNOHANG option is set, parent process will be suspended until a child changes state. If a child process has already changed state, function returns immediately. INPUTS pid - Process id of the process you want to wait for or -1 to wait for any child process status - Pointer to int where child status will be stored or NULL if you don't want to store status. options - ORed zero or more of the following constants: WNOHANG - return immediately if no child process changed state RESULT Process id of the child process on success or -1 on error. If an error occurred, the global variable errno is set. NOTES This function will work only for child processeses notifying parent process of their death, for example processes created by vfork() call. If you want to use it for other processes, remember to set the NP_NotifyOnDeath tag value to TRUE during child process creation. EXAMPLE BUGS SEE ALSO wait() INTERNALS Since POSIX signals are not yet implemented, WIFSIGNALED, WIFSTOPPED and WIFCONTINUED macros always return 0. WIFEXITED always returns 1. The et_UniqueID field of the ETask structure is used as process id. ******************************************************************************/ { struct ETask *et; ULONG tid = pid; pid_t ret = -1; int exchildno; D(bug("waitpid(%d, %p, %d)\n", pid, status, options)); et = GetETask(FindTask(NULL)); if(!et) { /* only ETasks are fertile */ errno = ECHILD; return -1; } if (pid < -1 || pid == 0) { /* process groups not yet supported */ errno = EINVAL; return -1; } if (pid == -1) tid = 0; if(tid != 0 && ChildStatus(tid) == CHILD_NOTFOUND) { /* error if there's no such child */ errno = ECHILD; return -1; } if (options & ~WNOHANG) { /* option not yet supported */ errno = EINVAL; return -1; } /* not very pretty, perhaps we need a function for counting dead children? */ ListLength(&et->et_TaskMsgPort.mp_MsgList, exchildno); if ((options & WNOHANG) && ( (tid != 0 && ChildStatus(tid) != CHILD_EXITED) || (tid == 0 && exchildno == 0) )) { D(bug("waitpid: no dead children\n")); /* no dead children atm */ return 0; } D(bug("waitpid: wait for %d to die\n", tid)); et = (struct ETask *)ChildWait(tid); if (et != (struct ETask *)CHILD_NOTNEW) { if(status) { *status = et->et_Result1; } ret = et->et_UniqueID; ChildFree(et->et_UniqueID); } else errno = ECHILD; D(bug("waitpid: leaving (%d)\n", ret)); return ret; }
LONG launcher() { D(bug("launcher: Entered child launcher\n")); struct Task *this = FindTask(NULL); struct vfork_data *udata = this->tc_UserData; BYTE child_signal; struct Library *aroscbase = NULL; GetIntETask(this)->iet_startup = GetETask(this)->et_Result2 = AllocVec(sizeof(struct aros_startup), MEMF_ANY | MEMF_CLEAR); /* Allocate signal for parent->child communication */ child_signal = udata->child_signal = AllocSignal(-1); D(bug("launcher: Allocated child signal: %d\n", udata->child_signal)); if(udata->child_signal == -1) { /* Lie */ udata->child_errno = ENOMEM; Signal(udata->parent, 1 << udata->parent_signal); return -1; } if(__register_init_fdarray(udata->ppriv->acpd_fd_array, udata->ppriv->acpd_numslots)) aroscbase = OpenLibrary((STRPTR) "arosc.library", 0); if(!aroscbase) { FreeSignal(child_signal); udata->child_errno = ENOMEM; Signal(udata->parent, 1 << udata->parent_signal); return -1; } udata->cpriv = __get_arosc_privdata(); udata->cpriv->acpd_parent_does_upath = udata->ppriv->acpd_doupath; udata->cpriv->acpd_flags |= DO_NOT_CLONE_ENV_VARS; if(setjmp(__aros_startup_jmp_buf) == 0) { /* Setup complete, signal parent */ D(bug("launcher: Signaling parent that we finished setup\n")); Signal(udata->parent, 1 << udata->parent_signal); D(bug("launcher: Child waiting for exec or exit\n")); Wait(1 << udata->child_signal); if(udata->child_executed) { APTR exec_id; BPTR dir; D(bug("launcher: child executed\n")); /* Set current dir to parent's current dir */ dir = DupLock(((struct Process *)udata->parent)->pr_CurrentDir); UnLock(CurrentDir(dir)); /* Don't mind updating aroscbase->acb_startup_cd_changed as we will exit from process after __exec_do has finished */ /* Filenames passed from parent obey parent's __doupath */ __doupath = udata->cpriv->acpd_parent_does_upath; D(bug("launcher: __doupath == %d for __exec_prepare()\n", __doupath)); exec_id = udata->exec_id = __exec_prepare( udata->exec_filename, udata->exec_searchpath, udata->exec_argv, udata->exec_envp ); udata->child_errno = errno; /* Clear __doupath again, command will set it if wanted */ __doupath = 0; D(bug("launcher: informing parent that we have run __exec_prepare\n")); /* Inform parent that we have run __exec_prepare */ Signal(udata->parent, 1 << udata->parent_signal); /* Wait 'till __exec_do() is called on parent process */ D(bug("launcher: Waiting parent to get the result\n")); Wait(1 << udata->child_signal); D(bug("launcher: informing parent that we won't use udata anymore\n")); /* Inform parent that we won't use udata anymore */ Signal(udata->parent, 1 << udata->parent_signal); if (exec_id) { D(bug("launcher: executing command\n")); __exec_do(exec_id); assert(0); /* Should not be reached */ } else { D(bug("launcher: exit because execve returned with an error\n")); _exit(0); } } else { D(bug("launcher: informing parent that we won't use udata anymore\n")); /* Inform parent that we won't use udata anymore */ Signal(udata->parent, 1 << udata->parent_signal); } } else { D(bug("launcher: freeing child_signal\n")); FreeSignal(child_signal); CloseLibrary(aroscbase); } return 0; }
pid_t wait( /* SYNOPSIS */ int *status) /* FUNCTION Waits for child process to change state. State change is one of the following events: child has exited, child was terminated by a signal, child was stopped by a signal, child was resumed by a signal. The function stores status of the process that changed state in the pointer given as status argument. The following macros can be used to extract information from the status value: WIFEXITED(status) - true if the process has exited WEXITSTATUS(status) - exit status of the exited process WIFSIGNALED(status) - true if the child process was terminated by a signal WTERMSIG(status) - number of the signal that caused process termination WIFSTOPPED(status) - true if the child process was stopped by a signal WSTOPSIG(status) - number of the signal that caused child process stop WIFCONTINUED(status) - true if the child process was resumed by the SIGCONT signal. Parent process will be suspended until a child changes state. If a child process has already changed state, function returns immediately. INPUTS status - Pointer to int where child return status will be stored or NULL if you don't want to store status. RESULT Process id of the child process on success or -1 on error. If an error occurred, the global variable errno is set. NOTES This function will work only for child processeses notifying parent process of their death, for example processes created by vfork() call. If you want to use it for other processes, remember to set the NP_NotifyOnDeath tag value to TRUE during child process creation. EXAMPLE BUGS SEE ALSO waitpid() INTERNALS Since POSIX signals are not yet implemented, WIFSIGNALED, WIFSTOPPED and WIFCONTINUED macros always return 0. WIFEXITED always returns 1. The et_UniqueID field of the ETask structure is used as process id. ******************************************************************************/ { pid_t ret = -1; struct ETask *et; D(bug("wait()\n")); et = GetETask(FindTask(NULL)); if(!et) { /* only ETasks are fertile */ errno = ECHILD; return -1; } et = (struct ETask *)ChildWait(0); if (et != CHILD_NOTNEW) { if(status && IntETask(et)->iet_startup) { struct aros_startup *startup = IntETask(et)->iet_startup; *status = startup->as_startup_error; } ret = et->et_UniqueID; ChildFree(et->et_UniqueID); } else errno = ECHILD; return ret; }