ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/rc3.c
(Generate patch)

Comparing ray/src/rt/rc3.c (file contents):
Revision 2.5 by greg, Tue Jun 12 01:06:59 2012 UTC vs.
Revision 2.26 by greg, Tue Jul 2 23:54:16 2024 UTC

# Line 6 | Line 6 | static const char RCSid[] = "$Id$";
6   * Controlling process for multiple children
7   */
8  
9 + #include <signal.h>
10   #include "rcontrib.h"
10 #include "platform.h"
11 #include "rtprocess.h"
11   #include "selcall.h"
12  
13 + #define MAXIQ           (int)(PIPE_BUF/(sizeof(FVECT)*2))
14 +
15   /* Modifier contribution queue (results waiting to be output) */
16   typedef struct s_binq {
17 <        int             ndx;            /* index for this entry */
18 <        int             nadded;         /* accumulated so far */
17 >        RNUMBER         ndx;            /* index for this entry */
18 >        RNUMBER         nadded;         /* accumulated so far */
19          struct s_binq   *next;          /* next in queue */
20          MODCONT         *mca[1];        /* contrib. array (extends struct) */
21   } BINQ;
# Line 22 | Line 23 | typedef struct s_binq {
23   static BINQ     *out_bq = NULL;         /* output bin queue */
24   static BINQ     *free_bq = NULL;        /* free queue entries */
25  
26 < static SUBPROC  kida[MAXPROCESS];       /* child processes */
26 < static FILE     *inq_fp[MAXPROCESS];    /* input streams */
26 > static SUBPROC  kidpr[MAXPROCESS];      /* our child processes */
27  
28 + static struct {
29 +        RNUMBER r1;                     /* assigned ray starting index */
30 +        FILE    *infp;                  /* file pointer to read from process */
31 +        int     nr;                     /* number of rays to sum (0 if free) */
32 + } kida[MAXPROCESS];             /* our child process i/o */
33  
34 +
35   /* Get new bin queue entry */
36   static BINQ *
37   new_binq()
# Line 37 | Line 43 | new_binq()
43                  bp = free_bq;
44                  free_bq = bp->next;
45                  bp->next = NULL;
46 <                bp->nadded = 1;
46 >                bp->nadded = 0;
47                  return(bp);
48          }
49                                          /* else allocate fresh */
# Line 46 | Line 52 | new_binq()
52                  goto memerr;
53          for (i = nmods; i--; ) {
54                  MODCONT *mp = (MODCONT *)lu_find(&modconttab,modname[i])->data;
55 <                bp->mca[i] = (MODCONT *)malloc(sizeof(MODCONT) +
50 <                                                sizeof(DCOLOR)*(mp->nbins-1));
55 >                bp->mca[i] = (MODCONT *)malloc(mcsize(mp->nbins));
56                  if (bp->mca[i] == NULL)
57                          goto memerr;
58 <                memcpy(bp->mca[i], mp, sizeof(MODCONT)-sizeof(DCOLOR));
59 <                /* memset(bp->mca[i]->cbin, 0, sizeof(DCOLOR)*mp->nbins); */
58 >                memcpy(bp->mca[i], mp, sizeof(MODCONT)-sizeof(DCOLORV));
59 >                /* memset(bp->mca[i]->cbin, 0, DCOLORSIZ*mp->nbins); */
60          }
61          bp->ndx = 0;
62 <        bp->nadded = 1;
62 >        bp->nadded = 0;
63          bp->next = NULL;
64          return(bp);
65   memerr:
# Line 81 | Line 86 | free_binq(BINQ *bp)
86          }
87                                          /* clear sums for next use */
88   /*      for (i = nmods; i--; )
89 <                memset(bp->mca[i]->cbin, 0, sizeof(DCOLOR)*bp->mca[i]->nbins);
89 >                memset(bp->mca[i]->cbin, 0, DCOLORSIZ*bp->mca[i]->nbins);
90   */
91 +        if (bp->next != NULL)
92 +                error(CONSISTENCY, "free_binq() handed list");
93          bp->ndx = 0;
94          bp->next = free_bq;             /* push onto free list */
95          free_bq = bp;
# Line 90 | Line 97 | free_binq(BINQ *bp)
97  
98  
99   /* Add modifier values to accumulation record in queue and clear */
100 < void
100 > static void
101   queue_modifiers()
102   {
103          MODCONT *mpin, *mpout;
104 <        int     i, j;
104 >        DCOLORV *ssrc, *sdst;
105 >        int     i;
106  
107          if ((accumulate > 0) | (out_bq == NULL))
108                  error(CONSISTENCY, "bad call to queue_modifiers()");
# Line 102 | Line 110 | queue_modifiers()
110          for (i = nmods; i--; ) {
111                  mpin = (MODCONT *)lu_find(&modconttab,modname[i])->data;
112                  mpout = out_bq->mca[i];
113 <                for (j = mpout->nbins; j--; )
114 <                        addcolor(mpout->cbin[j], mpin->cbin[j]);
115 <                memset(mpin->cbin, 0, sizeof(DCOLOR)*mpin->nbins);
113 >                ssrc = mcbin(mpin, mpin->nbins);
114 >                sdst = mcbin(mpout, mpout->nbins);
115 >                while (sdst > mpout->cbin)
116 >                        *--sdst += *--ssrc;
117 >                memset(mpin->cbin, 0, DCOLORSIZ*mpin->nbins);
118          }
119          out_bq->nadded++;
120   }
# Line 114 | Line 124 | queue_modifiers()
124   static void
125   add_modbin(BINQ *dst, BINQ *src)
126   {
127 <        int     i, j;
128 <        
127 >        MODCONT *mpin, *mpout;
128 >        DCOLORV *ssrc, *sdst;
129 >        int     i;
130 >
131          for (i = nmods; i--; ) {
132 <                MODCONT *mpin = src->mca[i];
133 <                MODCONT *mpout = dst->mca[i];
134 <                for (j = mpout->nbins; j--; )
135 <                        addcolor(mpout->cbin[j], mpin->cbin[j]);
132 >                mpin = src->mca[i];
133 >                mpout = dst->mca[i];
134 >                ssrc = mcbin(mpin, mpin->nbins);
135 >                sdst = mcbin(mpout, mpout->nbins);
136 >                while (sdst > mpout->cbin)
137 >                        *--sdst += *--ssrc;
138          }
139          dst->nadded += src->nadded;
140   }
# Line 142 | Line 156 | queue_output(BINQ *bp)
156                  }
157                  return;
158          }
159 <        b_last = NULL;                  /* else insert in output queue */
159 >        b_last = NULL;                  /* insert in output queue */
160          for (b_cur = out_bq; b_cur != NULL && b_cur->ndx < bp->ndx;
161                                  b_cur = b_cur->next)
162                  b_last = b_cur;
# Line 180 | Line 194 | queue_ready()
194          int     nready = 0;
195          BINQ    *bp;
196  
183        if (accumulate <= 0)            /* just accumulating? */
184                return(0);
185
197          for (bp = out_bq; bp != NULL && bp->nadded >= accumulate &&
198                                  bp->ndx == lastdone+nready*accumulate+1;
199                                  bp = bp->next)
# Line 199 | Line 210 | output_catchup(int nmax)
210          int     nout = 0;
211          BINQ    *bp;
212          int     i;
213 <
203 <        if (accumulate <= 0)            /* just accumulating? */
204 <                return(0);
205 <                                        /* else output ready results */
213 >                                        /* output ready results */
214          while (out_bq != NULL && out_bq->nadded >= accumulate
215                                  && out_bq->ndx == lastdone+1) {
216                  if ((nmax > 0) & (nout >= nmax))
# Line 229 | Line 237 | put_zero_record(int ndx)
237          int     i;
238  
239          for (i = nmods; i--; )
240 <                memset(bp->mca[i]->cbin, 0, sizeof(DCOLOR)*bp->mca[i]->nbins);
240 >                memset(bp->mca[i]->cbin, 0, DCOLORSIZ*bp->mca[i]->nbins);
241          bp->ndx = ndx;
242 +        bp->nadded = 1;
243          queue_output(bp);
244          output_catchup(0);
245   }
246  
247  
248 + /* Get results from child process and add to queue */
249 + static void
250 + queue_results(int k)
251 + {
252 +        BINQ    *bq = new_binq();       /* get results holder */
253 +        int     j;
254 +
255 +        bq->ndx = kida[k].r1;
256 +        bq->nadded = kida[k].nr;
257 +                                        /* read from child */
258 +        for (j = 0; j < nmods; j++)
259 +                if (getbinary(bq->mca[j]->cbin, DCOLORSIZ, bq->mca[j]->nbins,
260 +                                        kida[k].infp) != bq->mca[j]->nbins)
261 +                        error(SYSTEM, "read error from render process");
262 +                        
263 +        queue_output(bq);               /* put results in output queue */
264 +        kida[k].nr = 0;                 /* mark child as available */
265 + }
266 +
267 +
268   /* callback to set output spec to NULL (stdout) */
269   static int
270   set_stdout(const LUENT *le, void *p)
# Line 245 | Line 274 | set_stdout(const LUENT *le, void *p)
274   }
275  
276  
277 < /* Start child processes if we can */
277 > /* Start child processes if we can (call only once in parent!) */
278   int
279   in_rchild()
280   {
281 < #ifdef _WIN32
282 <        error(WARNING, "multiprocessing unsupported -- running solo");
283 <        nproc = 1;
255 <        return(1);
256 < #else
257 <                                        /* try to fork ourselves */
258 <        while (nchild < nproc) {
259 <                int     p0[2], p1[2];
260 <                int     pid;
261 <                                        /* prepare i/o pipes */
281 >        int     rval;
282 >
283 >        while (nchild < nproc) {        /* fork until target reached */
284                  errno = 0;
285 <                if (pipe(p0) < 0 || pipe(p1) < 0)
286 <                        error(SYSTEM, "pipe() call failed!");
287 <                pid = fork();           /* fork parent process */
288 <                if (pid == 0) {         /* if in child, set up & return true */
289 <                        close(p0[1]); close(p1[0]);
268 <                        lu_doall(&modconttab, set_stdout, NULL);
285 >                rval = open_process(&kidpr[nchild], NULL);
286 >                if (rval < 0)
287 >                        error(SYSTEM, "open_process() call failed");
288 >                if (rval == 0) {        /* if in child, set up & return true */
289 >                        lu_doall(&modconttab, &set_stdout, NULL);
290                          lu_done(&ofiletab);
291                          while (nchild--) {      /* don't share other pipes */
292 <                                close(kida[nchild].w);
293 <                                fclose(inq_fp[nchild]);
292 >                                close(kidpr[nchild].w);
293 >                                fclose(kida[nchild].infp);
294                          }
274                        dup2(p0[0], 0); close(p0[0]);
275                        dup2(p1[1], 1); close(p1[1]);
295                          inpfmt = (sizeof(RREAL)==sizeof(double)) ? 'd' : 'f';
296                          outfmt = 'd';
297                          header = 0;
279                        waitflush = xres = 1;
298                          yres = 0;
299                          raysleft = 0;
300 <                        account = accumulate = 1;
301 <                        return(1);      /* child return value */
300 >                        if (accumulate == 1) {
301 >                                waitflush = xres = 1;
302 >                                account = accumulate = 1;
303 >                        } else {        /* parent controls accumulation */
304 >                                waitflush = xres = 0;
305 >                                account = accumulate = 0;
306 >                        }
307 >                        return(1);      /* return "true" in child */
308                  }
309 <                if (pid < 0)
310 <                        error(SYSTEM, "fork() call failed!");
311 <                                        /* connect parent's pipes */
312 <                close(p0[0]); close(p1[1]);
313 <                kida[nchild].r = p1[0];
290 <                kida[nchild].w = p0[1];
291 <                kida[nchild].pid = pid;
292 <                kida[nchild].running = -1;
293 <                inq_fp[nchild] = fdopen(p1[0], "rb");
294 <                if (inq_fp[nchild] == NULL)
309 >                if (rval != PIPE_BUF)
310 >                        error(CONSISTENCY, "bad value from open_process()");
311 >                                        /* connect to child's output */
312 >                kida[nchild].infp = fdopen(kidpr[nchild].r, "rb");
313 >                if (kida[nchild].infp == NULL)
314                          error(SYSTEM, "out of memory in in_rchild()");
315 +                kida[nchild++].nr = 0;  /* mark as available */
316 +        }
317   #ifdef getc_unlocked
318 <                flockfile(inq_fp[nchild]);      /* avoid mutex overhead */
318 >        for (rval = nchild; rval--; )   /* avoid mutex overhead */
319 >                flockfile(kida[rval].infp);
320   #endif
321 <                ++nchild;
300 <        }
301 <        return(0);                      /* parent return value */
302 < #endif
321 >        return(0);                      /* return "false" in parent */
322   }
323  
324  
325   /* Close child processes */
326   void
327 < end_children()
327 > end_children(int immed)
328   {
329 <        int     status;
330 <        
331 <        while (nchild > 0) {
332 <                nchild--;
333 <                kida[nchild].r = -1;    /* close(-1) error is ignored */
334 <                if ((status = close_process(&kida[nchild])) > 0) {
335 <                        sprintf(errmsg,
336 <                                "rendering process returned bad status (%d)",
337 <                                        status);
329 >        int     i;
330 >
331 > #ifdef SIGKILL                          /* error mode -- quick exit */
332 >        for (i = nchild*immed; i-- > 0; )
333 >                kill(kidpr[nchild].pid, SIGKILL);
334 > #endif
335 >        if ((i = close_processes(kidpr, nchild)) > 0 && !immed) {
336 >                sprintf(errmsg, "rendering process returned bad status (%d)",
337 >                                        i);
338                          error(WARNING, errmsg);
320                }
321                fclose(inq_fp[nchild]); /* performs actual close() */
339          }
340 +        while (nchild-- > 0)
341 +                fclose(kida[nchild].infp);
342   }
343  
344  
# Line 330 | Line 349 | next_child_nq(int flushing)
349          static struct timeval   polling;
350          struct timeval          *pmode;
351          fd_set                  readset, errset;
352 <        int                     i, j, n, nr, nqr;
352 >        int                     i, n, nr, nqr;
353  
354          if (!flushing)                  /* see if there's one free */
355                  for (i = nchild; i--; )
356 <                        if (kida[i].running < 0)
356 >                        if (!kida[i].nr)
357                                  return(i);
358  
359          nqr = queue_ready();            /* choose blocking mode or polling */
# Line 352 | Line 371 | tryagain:                              /* catch up with output? */
371          FD_ZERO(&readset); FD_ZERO(&errset);
372          n = nr = 0;
373          for (i = nchild; i--; ) {
374 <                if (kida[i].running > 0) {
375 <                        FD_SET(kida[i].r, &readset);
374 >                if (kida[i].nr) {
375 >                        FD_SET(kidpr[i].r, &readset);
376                          ++nr;
377                  }
378 <                FD_SET(kida[i].r, &errset);
379 <                if (kida[i].r >= n)
380 <                        n = kida[i].r + 1;
378 >                FD_SET(kidpr[i].r, &errset);
379 >                if (kidpr[i].r >= n)
380 >                        n = kidpr[i].r + 1;
381          }
382          if (!nr)                        /* nothing to wait for? */
383                  return(-1);
# Line 375 | Line 394 | tryagain:                              /* catch up with output? */
394                  FD_ZERO(&errset);
395          n = -1;                         /* read results from child(ren) */
396          for (i = nchild; i--; ) {
397 <                BINQ    *bq;
379 <                if (FD_ISSET(kida[i].r, &errset))
397 >                if (FD_ISSET(kidpr[i].r, &errset))
398                          error(USER, "rendering process died");
399 <                if (!FD_ISSET(kida[i].r, &readset))
400 <                        continue;
383 <                bq = new_binq();        /* get results holder */
384 <                bq->ndx = kida[i].running;
385 <                                        /* read from child */
386 <                for (j = 0; j < nmods; j++) {
387 <                        nr = bq->mca[j]->nbins;
388 <                        if (fread(bq->mca[j]->cbin, sizeof(DCOLOR), nr,
389 <                                                        inq_fp[i]) != nr)
390 <                                error(SYSTEM, "read error from render process");
391 <                }
392 <                queue_output(bq);       /* add results to output queue */
393 <                kida[i].running = -1;   /* mark child as available */
394 <                n = i;
399 >                if (FD_ISSET(kidpr[i].r, &readset))
400 >                        queue_results(n = i);
401          }
402          return(n);                      /* first available child */
403   }
# Line 401 | Line 407 | tryagain:                              /* catch up with output? */
407   void
408   parental_loop()
409   {
410 <        static int      ignore_warning_given = 0;
411 <        FVECT           orgdir[2];
412 <        double          d;
413 <        int             i;
410 >        const int       qlimit = (accumulate == 1) ? 1 : MAXIQ-1;
411 >        int             ninq = 0;
412 >        FVECT           orgdir[2*MAXIQ];
413 >        int             i, n;
414                                          /* load rays from stdin & process */
415   #ifdef getc_unlocked
416          flockfile(stdin);               /* avoid lock/unlock overhead */
417   #endif
418 <        while (getvec(orgdir[0]) == 0 && getvec(orgdir[1]) == 0) {
419 <                d = normalize(orgdir[1]);
420 <                                                        /* asking for flush? */
421 <                if ((d == 0.0) & (accumulate != 1)) {
422 <                        if (!ignore_warning_given++)
423 <                                error(WARNING,
424 <                                "dummy ray(s) ignored during accumulation\n");
425 <                        continue;
418 >        while (getvec(orgdir[2*ninq]) == 0 && getvec(orgdir[2*ninq+1]) == 0) {
419 >                const int       zero_ray = orgdir[2*ninq+1][0] == 0.0 &&
420 >                                                (orgdir[2*ninq+1][1] == 0.0) &
421 >                                                (orgdir[2*ninq+1][2] == 0.0);
422 >                ninq += !zero_ray;
423 >                                /* Zero ray cannot go in input queue */
424 >                if (zero_ray ? ninq : ninq >= qlimit ||
425 >                            lastray/accumulate != (lastray+ninq)/accumulate) {
426 >                        i = next_child_nq(0);           /* manages output */
427 >                        n = ninq;
428 >                        if (accumulate > 1)             /* need terminator? */
429 >                                memset(orgdir[2*n++], 0, sizeof(FVECT)*2);
430 >                        n *= sizeof(FVECT)*2;           /* send assignment */
431 >                        if (writebuf(kidpr[i].w, orgdir, n) != n)
432 >                                error(SYSTEM, "pipe write error");
433 >                        kida[i].r1 = lastray+1;
434 >                        lastray += kida[i].nr = ninq;   /* mark as busy */
435 >                        if (lastray < lastdone) {       /* RNUMBER wrapped? */
436 >                                while (next_child_nq(1) >= 0)
437 >                                        ;
438 >                                lastray -= ninq;
439 >                                lastdone = lastray %= accumulate;
440 >                        }
441 >                        ninq = 0;
442                  }
443 <                if ((d == 0.0) | (lastray+1 < lastray)) {
444 <                        while (next_child_nq(1) >= 0)
445 <                                ;                       /* clear the queue */
446 <                        lastdone = lastray = 0;
447 <                }
448 <                if (d == 0.0) {
427 <                        if ((yres <= 0) | (xres <= 0))
443 >                if (zero_ray) {                         /* put bogus record? */
444 >                        if ((yres <= 0) | (xres <= 1) &&
445 >                                        (lastray+1) % accumulate == 0) {
446 >                                while (next_child_nq(1) >= 0)
447 >                                        ;               /* clear the queue */
448 >                                lastdone = lastray = accumulate-1;
449                                  waitflush = 1;          /* flush next */
450 +                        }
451                          put_zero_record(++lastray);
430                } else {                                /* else assign ray */
431                        i = next_child_nq(0);           /* manages output */
432                        if (writebuf(kida[i].w, (char *)orgdir,
433                                        sizeof(orgdir)) != sizeof(orgdir))
434                                error(SYSTEM, "pipe write error");
435                        kida[i].running = ++lastray;    /* busy now */
452                  }
453 <                if (raysleft && !--raysleft)
453 >                if (!morays())
454                          break;                          /* preemptive EOI */
455          }
456          while (next_child_nq(1) >= 0)           /* empty results queue */
457                  ;
458 <                                                /* output accumulated record */
459 <        if (accumulate <= 0 || account < accumulate) {
460 <                end_children();                 /* frees up file descriptors */
445 <                if (account < accumulate) {
446 <                        error(WARNING, "partial accumulation in final record");
447 <                        accumulate -= account;
448 <                }
449 <                for (i = 0; i < nmods; i++)
450 <                        mod_output(out_bq->mca[i]);
451 <                end_record();
452 <                free_binq(out_bq);
458 >        if (account < accumulate) {
459 >                error(WARNING, "partial accumulation in final record");
460 >                free_binq(out_bq);              /* XXX just ignore it */
461                  out_bq = NULL;
462          }
463 +        free_binq(NULL);                        /* clean up */
464 +        lu_done(&ofiletab);
465          if (raysleft)
466                  error(USER, "unexpected EOF on input");
467 <        free_binq(NULL);                        /* clean up */
467 > }
468 >
469 >
470 > /* Wait for the next available child by monitoring "to" pipes */
471 > static int
472 > next_child_ready()
473 > {
474 >        fd_set                  writeset, errset;
475 >        int                     i, n;
476 >
477 >        for (i = nchild; i--; )         /* see if there's one free first */
478 >                if (!kida[i].nr)
479 >                        return(i);
480 >                                        /* prepare select() call */
481 >        FD_ZERO(&writeset); FD_ZERO(&errset);
482 >        n = 0;
483 >        for (i = nchild; i--; ) {
484 >                FD_SET(kidpr[i].w, &writeset);
485 >                FD_SET(kidpr[i].r, &errset);
486 >                if (kidpr[i].w >= n)
487 >                        n = kidpr[i].w + 1;
488 >                if (kidpr[i].r >= n)
489 >                        n = kidpr[i].r + 1;
490 >        }
491 >        errno = 0;
492 >        n = select(n, NULL, &writeset, &errset, NULL);
493 >        if (n < 0)
494 >                error(SYSTEM, "select() error in next_child_ready()");
495 >        n = -1;                         /* identify waiting child */
496 >        for (i = nchild; i--; ) {
497 >                if (FD_ISSET(kidpr[i].r, &errset))
498 >                        error(USER, "rendering process died");
499 >                if (FD_ISSET(kidpr[i].w, &writeset))
500 >                        kida[n = i].nr = 0;
501 >        }
502 >        return(n);                      /* first available child */
503 > }
504 >
505 >
506 > /* Modified parental loop for full accumulation mode (-c 0) */
507 > void
508 > feeder_loop()
509 > {
510 >        static int      ignore_warning_given = 0;
511 >        int             ninq = 0;
512 >        FVECT           orgdir[2*MAXIQ];
513 >        int             i, n;
514 >                                        /* load rays from stdin & process */
515 > #ifdef getc_unlocked
516 >        flockfile(stdin);               /* avoid lock/unlock overhead */
517 > #endif
518 >        while (getvec(orgdir[2*ninq]) == 0 && getvec(orgdir[2*ninq+1]) == 0) {
519 >                if (orgdir[2*ninq+1][0] == 0.0 &&       /* asking for flush? */
520 >                                (orgdir[2*ninq+1][1] == 0.0) &
521 >                                (orgdir[2*ninq+1][2] == 0.0)) {
522 >                        if (!ignore_warning_given++)
523 >                                error(WARNING,
524 >                                "dummy ray(s) ignored during accumulation\n");
525 >                        continue;
526 >                }
527 >                if (++ninq >= MAXIQ) {
528 >                        i = next_child_ready();         /* get eager child */
529 >                        n = sizeof(FVECT)*2 * ninq;     /* give assignment */
530 >                        if (writebuf(kidpr[i].w, orgdir, n) != n)
531 >                                error(SYSTEM, "pipe write error");
532 >                        kida[i].r1 = lastray+1;
533 >                        lastray += kida[i].nr = ninq;
534 >                        if (lastray < lastdone)         /* RNUMBER wrapped? */
535 >                                lastdone = lastray = 0;
536 >                        ninq = 0;
537 >                }
538 >                if (!morays())
539 >                        break;                          /* preemptive EOI */
540 >        }
541 >        if (ninq) {                             /* polish off input */
542 >                i = next_child_ready();
543 >                n = sizeof(FVECT)*2 * ninq;
544 >                if (writebuf(kidpr[i].w, orgdir, n) != n)
545 >                        error(SYSTEM, "pipe write error");
546 >                kida[i].r1 = lastray+1;
547 >                lastray += kida[i].nr = ninq;
548 >                ninq = 0;
549 >        }
550 >        memset(orgdir, 0, sizeof(FVECT)*2);     /* get results */
551 >        for (i = nchild; i--; ) {
552 >                writebuf(kidpr[i].w, orgdir, sizeof(FVECT)*2);
553 >                queue_results(i);
554 >        }
555 >        if (recover)                            /* and from before? */
556 >                queue_modifiers();
557 >        end_children(0);                        /* free up file descriptors */
558 >        for (i = 0; i < nmods; i++)
559 >                mod_output(out_bq->mca[i]);     /* output accumulated record */
560 >        end_record();
561 >        free_binq(out_bq);                      /* clean up */
562 >        out_bq = NULL;
563 >        free_binq(NULL);
564          lu_done(&ofiletab);
565 +        if (raysleft)
566 +                error(USER, "unexpected EOF on input");
567   }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines