static SEXP read_child_ci(child_info_t *ci) { unsigned int len = 0; int fd = ci->pfd; ssize_t n = read(fd, &len, sizeof(len)); #ifdef MC_DEBUG Dprintf(" read_child_ci(%d) - read length returned %d\n", ci->pid, n); #endif if (n != sizeof(len) || len == 0) { /* error or child is exiting */ int pid = ci->pid; close(fd); ci->pfd = -1; rm_child_(pid); return ScalarInteger(pid); } else { SEXP rv = allocVector(RAWSXP, len); unsigned char *rvb = RAW(rv); unsigned int i = 0; while (i < len) { n = read(fd, rvb + i, len - i); #ifdef MC_DEBUG Dprintf(" read_child_ci(%d) - read %d at %d returned %d\n", ci->pid, len-i, i, n); #endif if (n < 1) { int pid = ci->pid; close(fd); ci->pfd = -1; rm_child_(pid); return ScalarInteger(pid); } i += n; } PROTECT(rv); { SEXP pa = allocVector(INTSXP, 1); PROTECT(pa); INTEGER(pa)[0] = ci->pid; setAttrib(rv, install("pid"), pa); UNPROTECT(1); } UNPROTECT(1); return rv; } }
SEXP mc_rm_child(SEXP sPid) { int pid = asInteger(sPid); return ScalarLogical(rm_child_(pid)); }
SEXP mc_select_children(SEXP sTimeout, SEXP sWhich) { int maxfd = 0, sr, zombies = 0; unsigned int wlen = 0, wcount = 0; SEXP res; int *res_i, *which = 0; child_info_t *ci = children; fd_set fs; struct timeval tv = { 0, 0 }, *tvp = &tv; if (isReal(sTimeout) && LENGTH(sTimeout) == 1) { double tov = asReal(sTimeout); if (tov < 0.0) tvp = 0; /* Note: I'm not sure we really should allow this .. */ else { tv.tv_sec = (int) tov; tv.tv_usec = (int) ((tov - ((double) tv.tv_sec)) * 1000000.0); } } if (TYPEOF(sWhich) == INTSXP && LENGTH(sWhich)) { which = INTEGER(sWhich); wlen = LENGTH(sWhich); } { int wstat; while (waitpid(-1, &wstat, WNOHANG) > 0) ; /* check for zombies */ } FD_ZERO(&fs); while (ci && ci->pid) { if (ci->pfd == -1) zombies++; if (ci->pfd > maxfd) maxfd = ci->pfd; if (ci->pfd > 0) { if (which) { /* check for the FD only if it's on the list */ unsigned int k = 0; while (k < wlen) if (which[k++] == ci->pid) { FD_SET(ci->pfd, &fs); wcount++; break; } } else FD_SET(ci->pfd, &fs); } ci = ci -> next; } #ifdef MC_DEBUG Dprintf("select_children: maxfd=%d, wlen=%d, wcount=%d, zombies=%d, timeout=%d:%d\n", maxfd, wlen, wcount, zombies, (int)tv.tv_sec, (int)tv.tv_usec); #endif if (zombies) { /* oops, this should never really happen - it did * while we had a bug in rm_child_ but hopefully * not anymore */ while (zombies) { /* this is rather more complicated than it * should be if we used pointers to delete, * but well ... */ ci = children; while (ci) { if (ci->pfd == -1) { #ifdef MC_DEBUG Dprintf("detected zombie: pid=%d, pfd=%d, sifd=%d\n", ci->pid, ci->pfd, ci->sifd); #endif rm_child_(ci->pid); zombies--; break; } ci = ci->next; } if (!ci) break; } } if (maxfd == 0 || (wlen && !wcount)) return R_NilValue; /* NULL signifies no children to tend to */ sr = select(maxfd + 1, &fs, 0, 0, tvp); #ifdef MC_DEBUG Dprintf(" sr = %d\n", sr); #endif if (sr < 0) { warning(_("error '%s' in select"), strerror(errno)); return ScalarLogical(0); /* FALSE on select error */ } if (sr < 1) return ScalarLogical(1); /* TRUE on timeout */ ci = children; maxfd = 0; while (ci && ci->pid) { /* pass 1 - count the FDs (in theory not necessary since that's what select should have returned) */ if (ci->pfd > 0 && FD_ISSET(ci->pfd, &fs)) maxfd++; ci = ci -> next; } ci = children; #ifdef MC_DEBUG Dprintf(" - read select %d children: ", maxfd); #endif res = allocVector(INTSXP, maxfd); res_i = INTEGER(res); while (ci && ci->pid) { /* pass 2 - fill the array */ if (ci->pfd > 0 && FD_ISSET(ci->pfd, &fs)) (res_i++)[0] = ci->pid; #ifdef MC_DEBUG if (ci->pfd > 0 && FD_ISSET(ci->pfd, &fs)) Dprintf("%d ", ci->pid); #endif ci = ci -> next; } #ifdef MC_DEBUG Dprintf("\n"); #endif return res; }