#include #define debug 0 static ssize_t devpipe_read(struct Fd *fd, void *buf, size_t n); static ssize_t devpipe_write(struct Fd *fd, const void *buf, size_t n); static int devpipe_stat(struct Fd *fd, struct Stat *stat); static int devpipe_close(struct Fd *fd); struct Dev devpipe = { .dev_id = 'p', .dev_name = "pipe", .dev_read = devpipe_read, .dev_write = devpipe_write, .dev_close = devpipe_close, .dev_stat = devpipe_stat, }; #define PIPEBUFSIZ 32 // small to provoke races struct Pipe { off_t p_rpos; // read position off_t p_wpos; // write position uint8_t p_buf[PIPEBUFSIZ]; // data buffer }; int pipe(int pfd[2]) { int r; struct Fd *fd0, *fd1; void *va; // allocate the file descriptor table entries if ((r = fd_alloc(&fd0)) < 0 || (r = sys_page_alloc(0, fd0, PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0) goto err; if ((r = fd_alloc(&fd1)) < 0 || (r = sys_page_alloc(0, fd1, PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0) goto err1; // allocate the pipe structure as first data page in both va = fd2data(fd0); if ((r = sys_page_alloc(0, va, PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0) goto err2; if ((r = sys_page_map(0, va, 0, fd2data(fd1), PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0) goto err3; // set up fd structures fd0->fd_dev_id = devpipe.dev_id; fd0->fd_omode = O_RDONLY; fd1->fd_dev_id = devpipe.dev_id; fd1->fd_omode = O_WRONLY; if (debug) cprintf("[%08x] pipecreate %08x\n", thisenv->env_id, uvpt[PGNUM(va)]); pfd[0] = fd2num(fd0); pfd[1] = fd2num(fd1); return 0; err3: sys_page_unmap(0, va); err2: sys_page_unmap(0, fd1); err1: sys_page_unmap(0, fd0); err: return r; } static int _pipeisclosed(struct Fd *fd, struct Pipe *p) { int n, nn, ret; while (1) { n = thisenv->env_runs; ret = pageref(fd) == pageref(p); nn = thisenv->env_runs; if (n == nn) return ret; if (n != nn && ret == 1) cprintf("pipe race avoided\n", n, thisenv->env_runs, ret); } } int pipeisclosed(int fdnum) { struct Fd *fd; struct Pipe *p; int r; if ((r = fd_lookup(fdnum, &fd)) < 0) return r; p = (struct Pipe*) fd2data(fd); return _pipeisclosed(fd, p); } static ssize_t devpipe_read(struct Fd *fd, void *vbuf, size_t n) { uint8_t *buf; size_t i; struct Pipe *p; p = (struct Pipe*)fd2data(fd); if (debug) cprintf("[%08x] devpipe_read %08x %d rpos %d wpos %d\n", thisenv->env_id, uvpt[PGNUM(p)], n, p->p_rpos, p->p_wpos); buf = vbuf; for (i = 0; i < n; i++) { while (p->p_rpos == p->p_wpos) { // pipe is empty // if we got any data, return it if (i > 0) return i; // if all the writers are gone, note eof if (_pipeisclosed(fd, p)) return 0; // yield and see what happens if (debug) cprintf("devpipe_read yield\n"); sys_yield(); } // there's a byte. take it. // wait to increment rpos until the byte is taken! buf[i] = p->p_buf[p->p_rpos % PIPEBUFSIZ]; p->p_rpos++; } return i; } static ssize_t devpipe_write(struct Fd *fd, const void *vbuf, size_t n) { const uint8_t *buf; size_t i; struct Pipe *p; p = (struct Pipe*) fd2data(fd); if (debug) cprintf("[%08x] devpipe_write %08x %d rpos %d wpos %d\n", thisenv->env_id, uvpt[PGNUM(p)], n, p->p_rpos, p->p_wpos); buf = vbuf; for (i = 0; i < n; i++) { while (p->p_wpos >= p->p_rpos + sizeof(p->p_buf)) { // pipe is full // if all the readers are gone // (it's only writers like us now), // note eof if (_pipeisclosed(fd, p)) return 0; // yield and see what happens if (debug) cprintf("devpipe_write yield\n"); sys_yield(); } // there's room for a byte. store it. // wait to increment wpos until the byte is stored! p->p_buf[p->p_wpos % PIPEBUFSIZ] = buf[i]; p->p_wpos++; } return i; } static int devpipe_stat(struct Fd *fd, struct Stat *stat) { struct Pipe *p = (struct Pipe*) fd2data(fd); strcpy(stat->st_name, ""); stat->st_size = p->p_wpos - p->p_rpos; stat->st_isdir = 0; stat->st_dev = &devpipe; return 0; } static int devpipe_close(struct Fd *fd) { (void) sys_page_unmap(0, fd); return sys_page_unmap(0, fd2data(fd)); }