--- ray/src/rt/raypcalls.c 2009/12/12 05:20:10 2.22 +++ ray/src/rt/raypcalls.c 2024/08/21 20:42:20 2.40 @@ -1,5 +1,5 @@ #ifndef lint -static const char RCSid[] = "$Id: raypcalls.c,v 2.22 2009/12/12 05:20:10 greg Exp $"; +static const char RCSid[] = "$Id: raypcalls.c,v 2.40 2024/08/21 20:42:20 greg Exp $"; #endif /* * raypcalls.c - interface for parallel rendering using Radiance @@ -73,7 +73,9 @@ static const char RCSid[] = "$Id: raypcalls.c,v 2.22 2 * results aren't ready, but will immediately return 0. * If the second argument is 0, the call will block * until a value is available, returning 0 only if the - * queue is completely empty. A negative return value + * queue is completely empty. Setting the second argument + * to -1 returns 0 unless a ray is ready in the queue and + * no system calls are needed. A negative return value * indicates that a rendering process died. If this * happens, ray_pclose(0) is automatically called to close * all child processes, and ray_pnprocs is set to zero. @@ -86,9 +88,11 @@ static const char RCSid[] = "$Id: raypcalls.c,v 2.22 2 * ray_psend(&myRay); * } * - * Note that it is a fatal error to call ra_psend() when - * ray_pnidle is zero. The ray_presult() and/or ray_pqueue() - * functions may be called subsequently to read back the results. + * Note that it is a mistake to call ra_psend() when + * ray_pnidle is zero, and nothing will be sent in + * this case. Otherwise, the ray_presult() and/or ray_pqueue() + * functions may be called subsequently to read back the results + * of rays queued by ray_psend(). * * When you are done, you may call ray_pdone(1) to close * all child processes and clean up memory used by Radiance. @@ -97,6 +101,8 @@ static const char RCSid[] = "$Id: raypcalls.c,v 2.22 2 * and fonts that are likely to be used in subsequent renderings. * Whether you need to clean up memory or not, you should * at least call ray_pclose(0) to await the child processes. + * The caller should define a quit() function that calls + * ray_pclose(0) if ray_pnprocs > 0. * * Warning: You cannot affect any of the rendering processes * by changing global parameter values onece ray_pinit() has @@ -141,7 +147,7 @@ static const char RCSid[] = "$Id: raypcalls.c,v 2.22 2 #include "selcall.h" #ifndef RAYQLEN -#define RAYQLEN 12 /* # rays to send at once */ +#define RAYQLEN 24 /* # rays to send at once */ #endif #ifndef MAX_RPROCS @@ -152,13 +158,11 @@ static const char RCSid[] = "$Id: raypcalls.c,v 2.22 2 #endif #endif -extern char *shm_boundary; /* boundary of shared memory */ - int ray_pnprocs = 0; /* number of child processes */ int ray_pnidle = 0; /* number of idle children */ static struct child_proc { - int pid; /* child process id */ + RT_PID pid; /* child process id */ int fd_send; /* write to child here */ int fd_recv; /* read from child here */ int npending; /* # rays in process */ @@ -166,10 +170,12 @@ static struct child_proc { } r_proc[MAX_NPROCS]; /* our child processes */ static RAY r_queue[2*RAYQLEN]; /* ray i/o buffer */ -static int r_send_next; /* next send ray placement */ -static int r_recv_first; /* position of first unreported ray */ -static int r_recv_next; /* next received ray placement */ +static int r_send_next = 0; /* next send ray placement */ +static int r_recv_first = RAYQLEN; /* position of first unreported ray */ +static int r_recv_next = RAYQLEN; /* next received ray placement */ +static int samplestep = 1; /* sample step size */ + #define sendq_full() (r_send_next >= RAYQLEN) static int ray_pflush(void); @@ -187,9 +193,6 @@ ray_pinit( /* initialize ray-tracing processes */ ray_init(otnm); /* load the shared scene */ - r_send_next = 0; /* set up queue */ - r_recv_first = r_recv_next = RAYQLEN; - ray_popen(nproc); /* fork children */ } @@ -207,12 +210,12 @@ ray_pflush(void) /* send queued rays to idle childre for (i = ray_pnprocs; nc && i--; ) { if (r_proc[i].npending > 0) continue; /* child looks busy */ - n = (r_send_next - sfirst)/nc--; + n = (r_send_next - sfirst) / nc--; if (!n) continue; /* smuggle set size in crtype */ r_queue[sfirst].crtype = n; - nw = writebuf(r_proc[i].fd_send, (char *)&r_queue[sfirst], + nw = writebuf(r_proc[i].fd_send, &r_queue[sfirst], sizeof(RAY)*n); if (nw != sizeof(RAY)*n) return(-1); /* write error */ @@ -223,24 +226,27 @@ ray_pflush(void) /* send queued rays to idle childre ray_pnidle--; /* now she's busy */ } if (sfirst != r_send_next) - error(CONSISTENCY, "code screwup in ray_pflush"); + error(CONSISTENCY, "code screwup in ray_pflush()"); r_send_next = 0; return(sfirst); /* return total # sent */ } -void +int ray_psend( /* add a ray to our send queue */ RAY *r ) { - if (r == NULL) - return; + int rv; + + if ((r == NULL) | (ray_pnidle <= 0)) + return(0); /* flush output if necessary */ - if (sendq_full() && ray_pflush() <= 0) - error(INTERNAL, "ray_pflush failed in ray_psend"); + if (sendq_full() && (rv = ray_pflush()) <= 0) + return(rv); r_queue[r_send_next++] = *r; + return(1); } @@ -259,7 +265,7 @@ ray_pqueue( /* queue a ray for computation */ return(-1); /* put new ray in queue */ r_queue[r_send_next++] = mySend; - /* XXX r_send_next may now be > RAYQLEN */ + return(1); } /* else add ray to send queue */ @@ -282,7 +288,7 @@ ray_presult( /* check for a completed ray */ static struct timeval tpoll; /* zero timeval struct */ static fd_set readset, errset; int n, ok; - register int pn; + int pn; if (r == NULL) return(0); @@ -291,6 +297,9 @@ ray_presult( /* check for a completed ray */ *r = r_queue[r_recv_first++]; return(1); } + if (poll < 0) /* immediate polling mode? */ + return(0); + n = ray_pnprocs - ray_pnidle; /* pending before flush? */ if (ray_pflush() < 0) /* send new rays to process */ @@ -325,7 +334,7 @@ getready: /* any children waiting for us? */ poll ? &tpoll : (struct timeval *)NULL)) < 0) if (errno != EINTR) { error(WARNING, - "select call failed in ray_presult"); + "select call failed in ray_presult()"); ray_pclose(0); return(-1); } @@ -337,7 +346,7 @@ getready: /* any children waiting for us? */ error(CONSISTENCY, "buffer shortage in ray_presult()"); /* read rendered ray data */ - n = readbuf(r_proc[pn].fd_recv, (char *)&r_queue[r_recv_next], + n = readbuf(r_proc[pn].fd_recv, &r_queue[r_recv_next], sizeof(RAY)*r_proc[pn].npending); if (n > 0) { r_recv_next += n/sizeof(RAY); @@ -357,7 +366,7 @@ getready: /* any children waiting for us? */ } /* preen returned rays */ for (n = r_recv_next - r_recv_first; n--; ) { - register RAY *rp = &r_queue[r_recv_first + n]; + RAY *rp = &r_queue[r_recv_first + n]; rp->rno = r_proc[pn].rno[n]; rp->parent = NULL; rp->newcset = rp->clipset = NULL; @@ -377,10 +386,8 @@ ray_pdone( /* reap children and free data */ { ray_pclose(0); /* close child processes */ - if (shm_boundary != NULL) { /* clear shared memory boundary */ - free((void *)shm_boundary); - shm_boundary = NULL; - } + cow_doneshare(); /* clear shared memory boundary */ + ray_done(freall); /* free rendering data */ } @@ -392,7 +399,7 @@ ray_pchild( /* process rays (never returns) */ ) { int n; - register int i; + int i; /* flag child process for quit() */ ray_pnprocs = -1; /* read each ray request set */ @@ -403,7 +410,7 @@ ray_pchild( /* process rays (never returns) */ /* get smuggled set length */ n2 = sizeof(RAY)*r_queue[0].crtype - n; if (n2 < 0) - error(INTERNAL, "buffer over-read in ray_pchild"); + error(INTERNAL, "buffer over-read in ray_pchild()"); if (n2 > 0) { /* read the rest of the set */ i = readbuf(fd_in, (char *)r_queue + n, n2); if (i != n2) @@ -418,17 +425,17 @@ ray_pchild( /* process rays (never returns) */ r_queue[i].clipset = NULL; r_queue[i].slights = NULL; r_queue[i].rlvl = 0; - samplendx++; + samplendx += samplestep; rayclear(&r_queue[i]); rayvalue(&r_queue[i]); } /* write back our results */ - i = writebuf(fd_out, (char *)r_queue, sizeof(RAY)*n); + i = writebuf(fd_out, r_queue, sizeof(RAY)*n); if (i != sizeof(RAY)*n) - error(SYSTEM, "write error in ray_pchild"); + error(SYSTEM, "write error in ray_pchild()"); } if (n) - error(SYSTEM, "read error in ray_pchild"); + error(SYSTEM, "read error in ray_pchild()"); ambsync(); quit(0); /* normal exit */ } @@ -444,14 +451,12 @@ ray_popen( /* open the specified # processes */ nadd = MAX_NPROCS - ray_pnprocs; if (nadd <= 0) return; + if (nobjects <= 0) + error(CONSISTENCY, "ray_popen() called before scene loaded"); ambsync(); /* load any new ambient values */ - if (shm_boundary == NULL) { /* first child process? */ - preload_objs(); /* preload auxiliary data */ - /* set shared memory boundary */ - shm_boundary = (char *)malloc(16); - strcpy(shm_boundary, "SHM_BOUNDARY"); - } + cow_memshare(); /* copy-on-write shared memory */ fflush(NULL); /* clear pending output */ + samplestep = ray_pnprocs + nadd; while (nadd--) { /* fork each new process */ int p0[2], p1[2]; if (pipe(p0) < 0 || pipe(p1) < 0) @@ -463,12 +468,17 @@ ray_popen( /* open the specified # processes */ close(r_proc[pn].fd_recv); } close(p0[0]); close(p1[1]); + close(0); /* don't share stdin */ /* following call never returns */ ray_pchild(p1[0], p0[1]); } if (r_proc[ray_pnprocs].pid < 0) error(SYSTEM, "cannot fork child process"); close(p1[0]); close(p0[1]); + if (rand_samp) /* decorrelate random sequence */ + srandom(random()); + else + samplendx++; /* * Close write stream on exec to avoid multiprocessing deadlock. * No use in read stream without it, so set flag there as well. @@ -490,7 +500,11 @@ ray_pclose( /* close one or more child processes */ ) { static int inclose = 0; - RAY res; + RAY res; + int i, status = 0; + /* check no child / in child */ + if (ray_pnprocs <= 0) + return; /* check recursion */ if (inclose) return; @@ -499,33 +513,44 @@ ray_pclose( /* close one or more child processes */ if ((nsub <= 0) | (nsub > ray_pnprocs)) nsub = ray_pnprocs; /* clear our ray queue */ + i = r_send_next; + r_send_next = 0; while (ray_presult(&res,0) > 0) - ; - /* clean up children */ - while (nsub--) { - int status; - ray_pnprocs--; - close(r_proc[ray_pnprocs].fd_recv); - close(r_proc[ray_pnprocs].fd_send); - if (waitpid(r_proc[ray_pnprocs].pid, &status, 0) < 0) + ++i; + if (i) { + sprintf(errmsg, "dropped %d rays in ray_pclose()", i); + error(WARNING, errmsg); + } + r_recv_first = r_recv_next = RAYQLEN; + /* close send pipes */ + for (i = ray_pnprocs-nsub; i < ray_pnprocs; i++) + close(r_proc[i].fd_send); + + if (nsub == 1) { /* awaiting single process? */ + if (waitpid(r_proc[ray_pnprocs-1].pid, &status, 0) < 0) status = 127<<8; - if (status) { - sprintf(errmsg, - "rendering process %d exited with code %d", - r_proc[ray_pnprocs].pid, status>>8); - error(WARNING, errmsg); + close(r_proc[ray_pnprocs-1].fd_recv); + } else /* else unordered wait */ + for (i = 0; i < nsub; ) { + int j, mystatus; + RT_PID pid = wait(&mystatus); + if (pid < 0) { + status = 127<<8; + break; + } + for (j = ray_pnprocs-nsub; j < ray_pnprocs; j++) + if (r_proc[j].pid == pid) { + if (mystatus) + status = mystatus; + close(r_proc[j].fd_recv); + ++i; + } } - ray_pnidle--; + ray_pnprocs -= nsub; + ray_pnidle -= nsub; + if (status) { + sprintf(errmsg, "rendering process exited with code %d", status>>8); + error(WARNING, errmsg); } inclose--; -} - - -void -quit(ec) /* make sure exit is called */ -int ec; -{ - if (ray_pnprocs > 0) /* close children if any */ - ray_pclose(0); - exit(ec); }