home *** CD-ROM | disk | FTP | other *** search
- head 1.2;
- access;
- symbols
- version39-41:1.2;
- locks;
- comment @ * @;
-
-
- 1.2
- date 92.07.04.19.21.08; author mwild; state Exp;
- branches;
- next 1.1;
-
- 1.1
- date 92.05.14.19.55.40; author mwild; state Exp;
- branches;
- next ;
-
-
- desc
- @create pair of pipes
- @
-
-
- 1.2
- log
- @(finally..) fix the bug which could cause pipe readers/writers to deadlock
- @
- text
- @/*
- * This file is part of ixemul.library for the Amiga.
- * Copyright (C) 1991, 1992 Markus M. Wild
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Id: pipe.c,v 1.1 1992/05/14 19:55:40 mwild Exp $
- *
- * $Log: pipe.c,v $
- * Revision 1.1 1992/05/14 19:55:40 mwild
- * Initial revision
- *
- */
-
- #define KERNEL
- #include "ixemul.h"
- #include <sys/ioctl.h>
- #include "select.h"
-
- /* #undef DEBUG */
- #ifdef DEBUG
- #define DP(a) do {Disable (); kprintf a; Enable ();} while (0)
- #else
- #define DP(a)
- #endif
-
- /* information for the temporary implementation of pipes.
- PIPE: has the big disadvantage that it blocks in the most unpleasent
- situations, and doesn't send SIGPIPE to processes that write on
- readerless pipes. Unacceptable for this library ;-)) */
-
- #define PIPE_SIZE 5120
-
- struct tmp_pipe {
- u_short tp_flags; /* see below */
- u_char tp_buffer[PIPE_SIZE];
- u_char *tp_reader, *tp_writer; /* buffer pointers.
- when tp_reader==tp_writer, no data
- is available */
- };
-
- #define TPF_NO_READER (1<<0)
- #define TPF_NO_WRITER (1<<1)
- #define TPF_LOCKED (1<<2)
- #define TPF_WANT_LOCK (1<<3)
-
- static int __pread(), __pwrite(), __pselect(), __pioctl(), __pclose();
- static struct tmp_pipe *__pinit();
-
- static inline void
- __get_pipe (struct file *f)
- {
- struct tmp_pipe *tp = f->f_tp;
-
- retry:
- Forbid ();
- for (;;)
- {
- if (!(tp->tp_flags & TPF_LOCKED))
- {
- tp->tp_flags &= ~TPF_WANT_LOCK;
- tp->tp_flags |= TPF_LOCKED;
- /* got it ! */
- break;
- }
- tp->tp_flags |= TPF_WANT_LOCK;
- DP(("__get_pipe: going to sleep\n"));
- if (ix_sleep (&tp->tp_flags, "get_pipe") < 0)
- {
- DP(("__get_pipe: interrupted\n"));
- Permit ();
- setrun (FindTask (0));
- goto retry;
- }
- DP(("__get_pipe: back from sleep (not interrupted)\n"));
- /* have to always recheck whether we really got the lock */
- }
- Permit ();
- }
-
- static inline void
- __release_pipe (struct file *f)
- {
- struct tmp_pipe *tp = f->f_tp;
-
- Forbid ();
- if (tp->tp_flags & TPF_WANT_LOCK)
- ix_wakeup (&tp->tp_flags);
-
- tp->tp_flags &= ~(TPF_WANT_LOCK|TPF_LOCKED);
- Permit ();
- }
-
-
- int
- pipe (int pv[2])
- {
- struct file *f1, *f2;
- struct tmp_pipe *tp;
- int res, err, omask;
-
- omask = syscall (SYS_sigsetmask, ~0);
- res = -1;
- err = EMFILE;
- if (tp = __pinit ())
- {
- if (! falloc (&f1, pv))
- {
- if (! falloc (&f2, pv+1))
- {
- f1->f_tp = tp;
- f1->f_stb.st_mode = 0666 | S_IFCHR;
- f1->f_stb.st_size = PIPE_SIZE;
- f1->f_stb.st_blksize = 512;
- f1->f_flags = FREAD;
- f1->f_type = DTYPE_PIPE;
- f1->f_read = __pread;
- f1->f_write = 0;
- f1->f_ioctl = __pioctl;
- f1->f_close = __pclose;
- f1->f_select = __pselect;
-
- f2->f_tp = tp;
- f2->f_stb.st_mode = 0666 | S_IFCHR;
- f2->f_stb.st_size = PIPE_SIZE;
- f2->f_stb.st_blksize = 512;
- f2->f_flags = FWRITE;
- f2->f_type = DTYPE_PIPE;
- f2->f_read = 0;
- f2->f_write = __pwrite;
- f2->f_ioctl = __pioctl;
- f2->f_close = __pclose;
- f2->f_select = __pselect;
-
- res = err =0;
- goto ret;
- }
- f1->f_count = 0;
- }
-
- kfree (tp);
- }
-
- ret:
- syscall (SYS_sigsetmask, omask);
-
- errno = err;
- return res;
- }
-
- static struct tmp_pipe *
- __pinit (void)
- {
- struct tmp_pipe *tp = (struct tmp_pipe *) kmalloc (sizeof (*tp));
-
- if (tp)
- {
- tp->tp_flags = 0;
- tp->tp_reader = tp->tp_writer = tp->tp_buffer;
- }
-
- return tp;
- }
-
- static int
- __pclose (struct file *f)
- {
- int omask;
-
- ix_lock_base ();
-
- f->f_count--;
-
- if (f->f_count == 0)
- {
- if (f->f_read)
- f->f_tp->tp_flags |= TPF_NO_READER;
- else
- f->f_tp->tp_flags |= TPF_NO_WRITER;
-
- if ((f->f_tp->tp_flags & (TPF_NO_READER|TPF_NO_WRITER)) ==
- (TPF_NO_READER|TPF_NO_WRITER))
- kfree (f->f_tp);
- else
- ix_wakeup (f->f_tp);
- }
-
- ix_unlock_base ();
-
- return 0;
-
- }
-
- static int
- __pread (struct file *f, char *buf, int len)
- {
- int omask = syscall (SYS_sigsetmask, ~0);
- int err = errno;
- int really_read = 0;
- struct tmp_pipe *tp = f->f_tp;
-
- __get_pipe (f);
-
- while (len)
- {
- if (tp->tp_reader == tp->tp_writer)
- {
- DP(("__pread: len == %ld, buffer full\n", len));
- if (tp->tp_flags & TPF_NO_WRITER)
- {
- DP(("__pread: EOF\n"));
- err = 0;
- break;
- }
-
- if (f->f_flags & FNDELAY)
- {
- if (! really_read)
- {
- really_read = -1;
- err = EWOULDBLOCK; /* is this the right thing to do ?????? */
- }
- break;
- }
- else
- {
- int sleep_rc;
- DP(("__pread: going to sleep.\n"));
- /* wait for something to be read or all readers to close */
- Forbid ();
- /* sigh.. Forbid() is necessary, or the other end may change
- the pipe, and in the worst case also settle for sleep(), and
- there it is.. deadlock.. */
- __release_pipe (f);
-
- /* make write interruptible */
- syscall (SYS_sigsetmask, omask);
- sleep_rc = ix_sleep (tp, "pwrite");
- Permit ();
- if (sleep_rc < 0)
- setrun (FindTask (0));
- omask = syscall (SYS_sigsetmask, ~0);
-
- __get_pipe (f);
- continue; /* retry */
- }
- }
- else
- {
- /* okay, there's something to read from the pipe */
- if (tp->tp_reader > tp->tp_writer)
- {
- /* read till end of buffer and wrap around */
- int avail = PIPE_SIZE - (tp->tp_reader - tp->tp_buffer);
- int do_read = len < avail ? len : avail;
-
- /* DP(("__pread-1: reading %ld bytes.\n", do_read)); */
-
- really_read += do_read;
- bcopy (tp->tp_reader, buf, do_read);
- len -= do_read;
- buf += do_read;
- tp->tp_reader += do_read;
- if (tp->tp_reader - tp->tp_buffer == PIPE_SIZE)
- /* wrap around */
- tp->tp_reader = tp->tp_buffer;
- }
- if (len && tp->tp_reader < tp->tp_writer)
- {
- int avail = tp->tp_writer - tp->tp_reader;
- int do_read = len < avail ? len : avail;
-
- /* DP(("__pread-2: reading %ld bytes.\n", do_read)); */
-
- really_read += do_read;
- bcopy (tp->tp_reader, buf, do_read);
- tp->tp_reader += do_read;
- len -= do_read;
- buf += do_read;
- }
- }
-
- ix_wakeup (tp);
- }
-
- __release_pipe (f);
-
- syscall (SYS_sigsetmask, omask);
- errno = err;
- return really_read;
- }
-
-
- static int
- __pwrite (struct file *f, char *buf, int len)
- {
- int omask = syscall (SYS_sigsetmask, ~0);
- int err = errno;
- int really_written = 0;
- struct tmp_pipe *tp = f->f_tp;
-
- __get_pipe (f);
-
- while (len)
- {
- if (tp->tp_flags & TPF_NO_READER)
- {
- DP(("__pwrite: SIGPIPE\n"));
- really_written = -1;
- err = EINTR;
- /* this is something no `real' Amiga pipe handler will do ;-)) */
- _psignal (FindTask (0), SIGPIPE);
- break;
- }
-
- /* buffer full ?? */
- if (tp->tp_reader == tp->tp_writer + 1
- || (tp->tp_reader == tp->tp_buffer
- && tp->tp_writer == tp->tp_buffer + PIPE_SIZE - 1))
- {
- DP(("__pwrite: buffer full, len == %ld\n", len));
- if (f->f_flags & FNDELAY)
- {
- if (! really_written)
- {
- really_written = -1;
- err = EWOULDBLOCK;
- }
- break;
- }
- else
- {
- int sleep_rc;
- DP(("__pwrite: going to sleep\n"));
- /* wait for something to be read or all readers to close */
- Forbid ();
- /* sigh.. Forbid() is necessary, or the other end may change
- the pipe, and in the worst case also settle for sleep(), and
- there it is.. deadlock.. */
- __release_pipe (f);
-
- /* make write interruptible */
- syscall (SYS_sigsetmask, omask);
- sleep_rc = ix_sleep (tp, "pwrite");
- Permit ();
- if (sleep_rc < 0)
- setrun (FindTask (0));
- omask = syscall (SYS_sigsetmask, ~0);
-
- __get_pipe (f);
- continue; /* retry */
- }
- }
- else
- {
- /* okay, there's some space left to write to the pipe */
-
- if (tp->tp_writer >= tp->tp_reader)
- {
- /* write till end of buffer */
- int avail = PIPE_SIZE - 1 - (tp->tp_writer - tp->tp_buffer);
- int do_write;
-
- if (tp->tp_reader > tp->tp_buffer)
- avail++;
- do_write = len < avail ? len : avail;
-
- /* DP(("__pwrite-1: writing %ld bytes.\n", do_write)); */
- really_written += do_write;
- bcopy (buf, tp->tp_writer, do_write);
- len -= do_write;
- buf += do_write;
- tp->tp_writer += do_write;
- if (tp->tp_writer - tp->tp_buffer == PIPE_SIZE)
- tp->tp_writer = tp->tp_buffer;
- }
-
- if (tp->tp_writer < tp->tp_reader - 1)
- {
- int avail = tp->tp_reader - tp->tp_writer - 1;
- int do_write = len < avail ? len : avail;
-
- /* DP(("__pwrite-2: writing %ld bytes.\n", do_write)); */
- really_written += do_write;
- bcopy (buf, tp->tp_writer, do_write);
- tp->tp_writer += do_write;
- len -= do_write;
- buf += do_write;
- }
- }
-
- ix_wakeup (tp);
- }
-
- __release_pipe (f);
-
- syscall (SYS_sigsetmask, omask);
- errno = err;
- return really_written;
- }
-
- static int
- __pselect (struct file *f, int select_cmd, int io_mode)
- {
- struct tmp_pipe *tp = f->f_tp;
-
- /* I currently only check whether io is possible, no setup needed.
- This would be quite different if select() waited the given timeout,
- and wouldn't split the timeout into smaller slices */
-
- if (select_cmd == SELCMD_CHECK)
- {
- /* we support both, read and write checks (hey, something new ;-)) */
- if (io_mode == SELMODE_IN)
- return tp->tp_reader != tp->tp_writer;
-
- else if (io_mode == SELMODE_OUT)
- return !(tp->tp_reader == tp->tp_writer + 1
- || (tp->tp_reader == tp->tp_buffer
- && tp->tp_writer == tp->tp_buffer + PIPE_SIZE - 1));
- }
-
- return 0;
- }
-
- static int
- __pioctl (struct file *f, unsigned int cmd, unsigned int inout,
- unsigned int arglen, unsigned int arg)
- {
- int omask;
- int result;
- struct tmp_pipe *tp = f->f_tp;
-
- omask = syscall (SYS_sigsetmask, ~0);
- __get_pipe (f);
-
- switch (cmd)
- {
- case FIONREAD:
- {
- unsigned int *pt = (unsigned int *)arg;
- if (tp->tp_reader < tp->tp_writer)
- *pt = tp->tp_writer - tp->tp_reader;
- else if (tp->tp_reader > tp->tp_writer)
- *pt = PIPE_SIZE - (tp->tp_reader - tp->tp_writer);
- else
- *pt = 0;
- result = 0;
- break;
- }
-
- case FIONBIO:
- {
- result = f->f_flags & FNDELAY ? 1 : 0;
- if (*(unsigned int *)arg)
- f->f_flags |= FNDELAY;
- else
- f->f_flags &= ~FNDELAY;
- /* I didn't find it documented in a manpage, but I assume, we
- * should return the former state, not just zero.. */
- break;
- }
-
- case FIOASYNC:
- {
- /* DOESN'T WORK YET */
-
- int flags = *(unsigned long*)arg;
- result = f->f_flags & FASYNC ? 1 : 0;
- if (flags)
- f->f_flags |= FASYNC;
- else
- f->f_flags &= ~FASYNC;
-
- /* ATTENTION: have to call some function here in the future !!! */
-
- /* I didn't find it documented in a manpage, but I assume, we
- * should return the former state, not just zero.. */
- break;
- }
-
- case FIOCLEX:
- case FIONCLEX:
- case FIOSETOWN:
- case FIOGETOWN:
- /* this is no error, but nevertheless we don't take any actions.. */
- result = 0;
- break;
- }
-
- __release_pipe (f);
- syscall (SYS_sigsetmask, omask);
- return result;
- }
- @
-
-
- 1.1
- log
- @Initial revision
- @
- text
- @d19 1
- a19 1
- * $Id$
- d21 4
- a24 1
- * $Log$
- d73 1
- d80 1
- a80 1
- if (ix_sleep (&tp->tp_flags) < 0)
- d239 1
- d241 5
- a245 1
- /* wait for something to arrive or all writers to close */
- d248 1
- d250 4
- a253 2
- if (ix_sleep (tp) < 0)
- setrun (FindTask (0));
- d345 1
- d348 4
- d356 3
- a358 1
- if (ix_sleep (tp) < 0)
- @
-