2 * This file has been modified for the cdrkit suite.
4 * The behaviour and appearence of the program code below can differ to a major
5 * extent from the version distributed by the original author(s).
7 * For details, see Changelog file distributed with the cdrkit package. If you
8 * received this file from another source then ask the distributing person for
9 * a log of modifications.
13 /* @(#)fifo.c 1.49 06/02/08 Copyright 1989,1997-2006 J. Schilling */
15 * A "fifo" that uses shared memory between two processes
17 * The actual code is a mixture of borrowed code from star's fifo.c
18 * and a proposal from Finn Arne Gangstad <finnag@guardian.no>
19 * who had the idea to use a ring buffer to handle average size chunks.
21 * Copyright (c) 1989,1997-2006 J. Schilling
24 * This program is free software; you can redistribute it and/or modify
25 * it under the terms of the GNU General Public License version 2
26 * as published by the Free Software Foundation.
28 * This program is distributed in the hope that it will be useful,
29 * but WITHOUT ANY WARRANTY; without even the implied warranty of
30 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 * GNU General Public License for more details.
33 * You should have received a copy of the GNU General Public License along with
34 * this program; see the file COPYING. If not, write to the Free Software
35 * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
44 /* We always wish FIFO unless it is disabled below */
49 #if defined(HAVE_OS_H) && \
50 defined(HAVE_CLONE_AREA) && defined(HAVE_CREATE_AREA) && \
51 defined(HAVE_DELETE_AREA)
53 # define HAVE_BEOS_AREAS /* BeOS/Zeta */
55 #if !defined(HAVE_SMMAP) && !defined(HAVE_USGSHM) && \
56 !defined(HAVE_DOSALLOCSHAREDMEM) && !defined(HAVE_BEOS_AREAS)
57 #undef FIFO /* We cannot have a FIFO on this platform */
59 #if !defined(HAVE_FORK)
60 #undef FIFO /* We cannot have a FIFO on this platform */
63 #if !defined(USE_MMAP) && !defined(USE_USGSHM)
68 # define USE_USGSHM /* now SYSV shared memory is the default*/
70 #ifdef USE_MMAP /* Only want to have one implementation */
71 # undef USE_USGSHM /* mmap() is preferred */
74 #ifdef HAVE_DOSALLOCSHAREDMEM /* This is for OS/2 */
80 #ifdef HAVE_BEOS_AREAS /* This is for BeOS/Zeta */
84 # define USE_BEOS_AREAS
89 #include <unixstd.h> /* includes <sys/types.h> */
92 #if defined(HAVE_SMMAP) && defined(USE_MMAP)
108 #define USDEBUG1 if (debug) {if (s == owner_reader) fprintf(ef, "r"); else fprintf(ef, "w"); fflush(ef); }
109 #define USDEBUG2 if (debug) {if (s == owner_reader) fprintf(ef, "R"); else fprintf(ef, "W"); fflush(ef); }
114 #define EDEBUG(a) if (debug) schily_error a
121 #define palign(x, a) (((char *)(x)) + ((a) - 1 - (((UIntptr_t)((x)-1))%(a))))
123 typedef enum faio_owner {
124 owner_none, /* Unused in real life */
125 owner_writer, /* owned by process that writes into FIFO */
126 owner_faio, /* Intermediate state when buf still in use */
127 owner_reader /* owned by process that reads from FIFO */
137 typedef struct faio {
139 volatile fowner_t owner;
156 #define MIN_BUFFERS 3
159 #define SECS (1000*MSECS)
162 * Note: WRITER_MAXWAIT & READER_MAXWAIT need to be greater than the SCSI
163 * timeout for commands that write to the media. This is currently 200s
164 * if we are in SAO mode.
166 /* microsecond delay between each buffer-ready probe by writing process */
167 #define WRITER_DELAY (20*MSECS)
168 #define WRITER_MAXWAIT (240*SECS) /* 240 seconds max wait for data */
170 /* microsecond delay between each buffer-ready probe by reading process */
171 #define READER_DELAY (80*MSECS)
172 #define READER_MAXWAIT (240*SECS) /* 240 seconds max wait for reader */
175 static char *bufbase;
177 static long buflen; /* The size of the FIFO buffer */
182 void init_fifo(long);
184 static char *mkshare(int size);
187 static char *mkshm(int size);
190 static char *mkos2shm(int size);
192 #ifdef USE_BEOS_AREAS
193 static char *mkbeosshm(int size);
194 static void beosshm_child(void);
197 BOOL init_faio(track_t *trackp, int);
198 BOOL await_faio(void);
199 void kill_faio(void);
201 static void faio_reader(track_t *trackp);
202 static void faio_read_track(track_t *trackp);
203 static void faio_wait_on_buffer(faio_t *f, fowner_t s, unsigned long delay,
204 unsigned long max_wait);
205 static int faio_read_segment(int fd, faio_t *f, track_t *track, long secno,
207 static faio_t *faio_ref(int n);
208 int faio_read_buf(int f, char *bp, int size);
209 int faio_get_buf(int f, char **bpp, int size);
210 void fifo_stats(void);
211 int fifo_percent(BOOL addone);
222 pagesize = getpagesize();
223 buflen = roundup(fs, pagesize) + pagesize;
224 EDEBUG(("fs: %ld buflen: %ld\n", fs, buflen));
226 #if defined(USE_MMAP)
227 buf = mkshare(buflen);
229 #if defined(USE_USGSHM)
232 #if defined(USE_OS2SHM)
233 buf = mkos2shm(buflen);
235 #if defined(USE_BEOS_AREAS)
236 buf = mkbeosshm(buflen);
240 bufend = buf + buflen;
241 EDEBUG(("buf: %p bufend: %p, buflen: %ld\n", buf, bufend, buflen));
242 buf = palign(buf, pagesize);
243 buflen -= buf - bufbase;
244 EDEBUG(("buf: %p bufend: %p, buflen: %ld (align %ld)\n", buf, bufend, buflen, (long)(buf - bufbase)));
247 * Dirty the whole buffer. This can die with various signals if
248 * we're trying to lock too much memory
250 fillbytes(buf, buflen, '\0');
254 ef = fopen("/tmp/ef", "w");
265 #ifdef MAP_ANONYMOUS /* HP/UX */
267 addr = mmap(0, mmap_sizeparm(size),
268 PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, f, 0);
270 if ((f = open("/dev/zero", O_RDWR)) < 0)
271 comerr("Cannot open '/dev/zero'.\n");
272 addr = mmap(0, mmap_sizeparm(size),
273 PROT_READ|PROT_WRITE, MAP_SHARED, f, 0);
275 if (addr == (char *)-1)
276 comerr("Cannot get mmap for %d Bytes on /dev/zero.\n", size);
280 if (debug) errmsgno(EX_BAD, "shared memory segment attached at: %p size %d\n",
296 * Unfortunately, a declaration of shmat() is missing in old
297 * implementations such as AT&T SVr0 and SunOS.
298 * We cannot add this definition here because the return-type
299 * changed on newer systems.
301 * We will get a warning like this:
303 * warning: assignment of pointer from integer lacks a cast
305 * warning: illegal combination of pointer and integer, op =
307 /* extern char *shmat();*/
309 if ((id = shmget(IPC_PRIVATE, size, IPC_CREAT|0600)) == -1)
310 comerr("shmget failed\n");
312 if (debug) errmsgno(EX_BAD, "shared memory segment allocated: %d\n", id);
314 if ((addr = shmat(id, (char *)0, 0600)) == (char *)-1)
315 comerr("shmat failed\n");
317 if (debug) errmsgno(EX_BAD, "shared memory segment attached at: %p size %d\n",
320 if (shmctl(id, IPC_RMID, 0) < 0)
321 comerr("shmctl failed to detach shared memory segment\n");
325 * Although SHM_LOCK is standard, it seems that all versions of AIX
326 * ommit this definition.
328 if (shmctl(id, SHM_LOCK, 0) < 0)
329 comerr("shmctl failed to lock shared memory segment\n");
343 * The OS/2 implementation of shm (using shm.dll) limits the size of one shared
344 * memory segment to 0x3fa000 (aprox. 4MBytes). Using OS/2 native API we have
345 * no such restriction so I decided to use it allowing fifos of arbitrary size.
347 if (DosAllocSharedMem(&addr, NULL, size, 0X100L | 0x1L | 0x2L | 0x10L))
348 comerr("DosAllocSharedMem() failed\n");
350 if (debug) errmsgno(EX_BAD, "shared memory allocated attached at: %p size %d\n",
357 #ifdef USE_BEOS_AREAS
358 static area_id faio_aid;
359 static void *faio_addr;
360 static char faio_name[32];
365 snprintf(faio_name, sizeof (faio_name), "cdrecord FIFO %lld",
368 faio_aid = create_area(faio_name, &faio_addr,
371 B_NO_LOCK, B_READ_AREA|B_WRITE_AREA);
372 if (faio_addr == NULL) {
374 "Cannot get create_area for %d Bytes FIFO.\n", size);
376 if (debug) errmsgno(EX_BAD, "shared memory allocated attached at: %p size %d\n",
377 (void *)faio_addr, size);
385 * Delete the area created by fork that is copy-on-write.
387 delete_area(area_for(faio_addr));
389 * Clone (share) the original one.
391 faio_aid = clone_area(faio_name, &faio_addr,
392 B_ANY_ADDRESS, B_READ_AREA|B_WRITE_AREA,
394 if (bufbase != faio_addr) {
395 errmsgno(EX_BAD, "Panic FIFO addr.\n");
401 static int faio_buffers;
402 static int faio_buf_size;
403 static int buf_idx = 0; /* Initialize to fix an Amiga bug */
404 static int buf_idx_reader = 0; /* Separate var to allow vfork() */
405 /* buf_idx_reader is for the process */
406 /* that fills the FIFO */
407 static pid_t faio_pid = -1;
408 static BOOL faio_didwait;
412 * On Amiga fork will be replaced by the speciall vfork() like call ix_vfork,
413 * which lets the parent asleep. The child process later wakes up the parent
414 * process by calling ix_fork_resume().
416 #define fork() ix_vfork()
417 #define __vfork_resume() ix_vfork_resume()
420 #define __vfork_resume()
424 /*#define faio_ref(n) (&((faio_t *)buf)[n])*/
428 init_faio(track_t *trackp, int bufsize)
438 pagesize = getpagesize();
439 faio_buf_size = bufsize;
443 * Compute space for buffer headers.
444 * Round bufsize up to pagesize to make each FIFO segment
445 * properly page aligned.
447 bufsize = roundup(bufsize, pagesize);
448 faio_buffers = (buflen - sizeof (*sp)) / bufsize;
449 EDEBUG(("bufsize: %d buffers: %d hdrsize %ld\n", bufsize, faio_buffers, (long)faio_buffers * sizeof (struct faio)));
452 * Reduce buffer space by header space.
454 n = sizeof (*sp) + faio_buffers * sizeof (struct faio);
455 n = roundup(n, pagesize);
456 faio_buffers = (buflen-n) / bufsize;
457 EDEBUG(("bufsize: %d buffers: %d hdrsize %ld\n", bufsize, faio_buffers, (long)faio_buffers * sizeof (struct faio)));
459 if (faio_buffers < MIN_BUFFERS) {
461 "write-buffer too small, minimum is %dk. Disabling.\n",
462 MIN_BUFFERS*bufsize/1024);
467 printf("Using %d buffers of %d bytes.\n", faio_buffers, faio_buf_size);
470 base = buf + roundup(sizeof (*sp) + faio_buffers * sizeof (struct faio),
473 for (n = 0; n < faio_buffers; n++, f++, base += bufsize) {
474 /* Give all the buffers to the file reader process */
475 f->owner = owner_writer;
480 sp = (struct faio_stats *)f; /* point past headers */
481 sp->gets = sp->puts = sp->done = 0L;
486 comerr("fork(2) failed");
490 * child (background) process that fills the FIFO.
492 raisepri(1); /* almost max priority */
495 DosGetSharedMem(buf, 3); /* PAG_READ|PAG_WRITE */
497 #ifdef USE_BEOS_AREAS
500 /* Ignoring SIGALRM cures the SCO usleep() bug */
501 /* signal(SIGALRM, SIG_IGN);*/
502 __vfork_resume(); /* Needed on some platforms */
510 faio_didwait = FALSE;
513 * XXX We used to close all track files in the foreground
514 * XXX process. This was not correct before we used "xio"
515 * XXX and with "xio" it will start to fail because we need
516 * XXX the fd handles for the faio_get_buf() function.
519 /* close all file-descriptors that only the child will use */
520 for (t = 1; t <= trackp->tracks; t++) {
521 if (trackp[t].xfp != NULL)
522 xclose(trackp[t].xfp);
538 * Wait until the reader is active and has filled the buffer.
540 if (lverbose || debug) {
541 printf("Waiting for reader process to fill input buffer ... ");
545 faio_wait_on_buffer(faio_ref(faio_buffers - 1), owner_reader,
548 if (lverbose || debug)
549 printf("input buffer ready.\n");
551 sp->empty = sp->full = 0L; /* set correct stat state */
552 sp->cont_low = faio_buffers; /* set cont to max value */
555 for (n = 0; n < faio_buffers; n++, f++) {
556 if (f->fd != lastfd &&
557 f->fd == STDIN_FILENO && f->len == 0) {
558 errmsgno(EX_BAD, "Premature EOF on stdin.\n");
559 kill(faio_pid, SIGKILL);
571 kill(faio_pid, SIGKILL);
578 if (faio_pid > 0 && !faio_didwait)
585 faio_reader(track_t *trackp)
587 /* This function should not return, but _exit. */
591 printf("\nfaio_reader starting\n");
593 for (trackno = 1; trackno <= trackp->tracks; trackno++) {
595 printf("\nfaio_reader reading track %u\n", trackno);
596 faio_read_track(&trackp[trackno]);
600 printf("\nfaio_reader all tracks read, exiting\n");
602 /* Prevent hang if buffer is larger than all the tracks combined */
604 faio_ref(faio_buffers - 1)->owner = owner_reader;
608 sleep(30000); /* XXX If calling _exit() here the parent process seems to be blocked */
609 /* XXX This should be fixed soon */
612 fprintf(stderr, "\nfaio_reader _exit(0)\n");
620 return (&((faio_t *)buf)[n]);
626 faio_read_track(track_t *trackp)
629 int bytespt = trackp->secsize * trackp->secspt;
630 int secspt = trackp->secspt;
632 long secno = trackp->trackstart;
633 tsize_t tracksize = trackp->tracksize;
634 tsize_t bytes_read = (tsize_t)0;
637 if (trackp->xfp != NULL)
638 fd = xfileno(trackp->xfp);
640 if (bytespt > faio_buf_size) {
642 "faio_read_track fatal: secsize %d secspt %d, bytespt(%d) > %d !!\n",
643 trackp->secsize, trackp->secspt, bytespt,
648 bytes_to_read = bytespt;
650 if ((tracksize - bytes_read) > bytespt) {
651 bytes_to_read = bytespt;
653 bytes_to_read = tracksize - bytes_read;
656 l = faio_read_segment(fd, faio_ref(buf_idx_reader), trackp, secno, bytes_to_read);
657 if (++buf_idx_reader >= faio_buffers)
663 } while (tracksize < 0 || bytes_read < tracksize);
665 xclose(trackp->xfp); /* Don't keep files open longer than neccesary */
670 faio_wait_on_buffer(faio_t *f, fowner_t s,
672 unsigned long max_wait)
674 faio_wait_on_buffer(faio_t *f, fowner_t *s, unsigned long delay, unsigned long max_wait)
677 unsigned long max_loops;
680 return; /* return immediately if the buffer is ours */
682 if (s == owner_reader)
687 max_loops = max_wait / delay + 1;
689 while (max_wait == 0 || max_loops--) {
699 "%lu microseconds passed waiting for %d current: %d idx: %ld\n",
700 max_wait, s, f->owner, (long)(f - faio_ref(0))/sizeof (*f));
702 comerrno(EX_BAD, "faio_wait_on_buffer for %s timed out.\n",
703 (s > owner_reader || s < owner_none) ? "bad_owner" : onames[s-owner_none]);
707 faio_read_segment(int fd, faio_t *f, track_t *trackp, long secno, int len)
711 faio_wait_on_buffer(f, owner_writer, WRITER_DELAY, WRITER_MAXWAIT);
714 l = fill_buf(fd, trackp, secno, f->bufp, len);
716 f->saved_errno = geterrno();
717 f->owner = owner_reader;
718 f->users = sp->users;
726 faio_read_buf(int fd, char *bp, int size)
730 int len = faio_get_buf(fd, &bufp, size);
732 movebytes(bufp, bp, len);
738 faio_get_buf(int fd, char **bpp, int size)
744 f = faio_ref(buf_idx);
745 if (f->owner == owner_faio) {
746 f->owner = owner_writer;
747 if (++buf_idx >= faio_buffers)
749 f = faio_ref(buf_idx);
752 if ((sp->puts - sp->gets) < sp->cont_low && sp->done == 0) {
753 EDEBUG(("gets: %ld puts: %ld cont: %ld low: %ld\n", sp->gets, sp->puts, sp->puts - sp->gets, sp->cont_low));
754 sp->cont_low = sp->puts - sp->gets;
756 faio_wait_on_buffer(f, owner_reader, READER_DELAY, READER_MAXWAIT);
762 * If the tracksize for this track was known, and
763 * the tracksize is 0 mod bytespt, this happens.
768 "faio_get_buf fatal: fd=%d, f->fd=%d, f->len=%d f->errno=%d\n",
769 fd, f->fd, f->len, f->saved_errno);
773 "unexpected short read-attempt in faio_get_buf. size = %d, len = %d\n",
778 seterrno(f->saved_errno);
784 f->owner = owner_faio;
791 if (sp == NULL) /* We might not use a FIFO */
794 errmsgno(EX_BAD, "fifo had %ld puts and %ld gets.\n",
796 errmsgno(EX_BAD, "fifo was %ld times empty and %ld times full, min fill was %ld%%.\n",
797 sp->empty, sp->full, (100L*sp->cont_low)/faio_buffers);
801 fifo_percent(BOOL addone)
805 if (sp == NULL) /* We might not use a FIFO */
810 percent = (100*(sp->puts + 1 - sp->gets)/faio_buffers);
817 #include <standard.h>
818 #include <utypes.h> /* includes sys/types.h */
823 void init_fifo(long);
824 BOOL init_faio(track_t *track, int);
825 BOOL await_faio(void);
826 void kill_faio(void);
828 int faio_read_buf(int f, char *bp, int size);
829 int faio_get_buf(int f, char **bpp, int size);
830 void fifo_stats(void);
831 int fifo_percent(BOOL addone);
834 void init_fifo(long fs)
836 errmsgno(EX_BAD, "Fifo not supported.\n");
839 BOOL init_faio(track_t *track,
840 int bufsize /* The size of a single transfer buffer */)
859 int faio_read_buf(int fd, char *bp, int size)
864 int faio_get_buf(int fd, char **bpp, int size)
873 int fifo_percent(BOOL addone)