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 ssize_t pipewrite(struct Fd *fd, const void *vbuf, size_t n, off_t offset) { // Your code here. See the lab text for a description of what // pipewrite needs to do. Write a loop that transfers one byte // at a time. Unlike in read, it is not okay to write only some // of the data. If the pipe fills and you've only copied some of // the data, wait for the pipe to empty and then keep copying. // If the pipe is full and closed, return 0. // Use _pipeisclosed to check whether the pipe is closed. struct Pipe *p; const uint8_t *buf; int i; USED(offset); p = (struct Pipe *)fd2data(fd); buf = vbuf; for (i = 0; i < n; i++) { while (p->p_wpos - p->p_rpos >= PIPEBUFSIZ) { if (_pipeisclosed(fd, p)) return 0; sys_yield(); } p->p_buf[p->p_wpos % PIPEBUFSIZ] = buf[i]; p->p_wpos++; } return n; }
static ssize_t devpipe_read(struct Fd *fd, void *vbuf, size_t n) { struct Pipe *p = (struct Pipe *) fd2data(fd); uint8_t *buf = (uint8_t *) vbuf; size_t i; for (i = 0; i < n; i++) { while (p->p_rpos == p->p_wpos) { // The pipe is currently empty. // If any data has been read, return it. // Otherwise, check for EOF; if not EOF, yield // and try again. if (i > 0) return i; else if (_pipeisclosed(fd, p)) return 0; else sys_yield(); } buf[i] = p->p_buf[p->p_rpos % PIPEBUFSIZ]; // The increment must come AFTER we write to the buffer, // or the C compiler might update the pointer before writing // to the buffer! In fact, we need a memory barrier here--- // on some machines a memory barrier instruction. asm volatile("" : : : "memory"); p->p_rpos++; } return i; }
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 piperead(struct Fd *fd, void *vbuf, size_t n, off_t offset) { // Your code here. See the lab text for a description of // what piperead needs to do. Write a loop that // transfers one byte at a time. If you decide you need // to yield (because the pipe is empty), only yield if // you have not yet copied any bytes. (If you have copied // some bytes, return what you have instead of yielding.) // If the pipe is empty and closed and you didn't copy any data out, // return 0. // Use _pipeisclosed to check whether the pipe is closed. struct Pipe *pipe; bool haveRead; int k; char *dest; haveRead = 0; dest = vbuf; pipe = (struct Pipe *)fd2data(fd); for(k=n; k!=0; k--) { afterYield: if(pipe->p_rpos >= pipe->p_wpos) { if(!haveRead) { if(_pipeisclosed(fd, pipe)) { return 0; } else { sys_yield(); goto afterYield; } } else return n-k; } *dest = pipe->p_buf[pipe->p_rpos % PIPEBUFSIZ]; dest++; pipe->p_rpos++; //race condition when multiple processes read from the same pipe simultaneously, don't know how to fix //without writing new system call to uninterruptable kernel - ren haveRead = 1; } return n; }
static ssize_t devpipe_write(struct Fd *fd, const void *vbuf, size_t n) { // Write a loop that transfers one byte at a time. // Your code should be patterned on pipe_read above. // Unlike in read, it is not okay to write only some of the data: // if the pipe fills and you've only copied some of the data, // wait for the pipe to empty and then keep copying. // If the pipe is full and closed, return the number of characters // written. Use _pipeisclosed to check whether the pipe is closed. // LAB 5: Your code here. struct Pipe *p = (struct Pipe *) fd2data(fd); uint8_t *buf = (uint8_t *)vbuf; size_t i; for (i = 0; i < n; i++) { lock(&p->p_wlock); while ((p->p_wpos - p->p_rpos) == PIPEBUFSIZ) { // The pipe is currently full. // Check for readers... if there // are still readers, yield and // try again. if (_pipeisclosed(fd, p)) { unlock(&p->p_wlock); return i; } else { unlock(&p->p_wlock); sys_yield(); lock(&p->p_wlock); } } p->p_buf[p->p_wpos % PIPEBUFSIZ] = buf[i]; // Need a memory barrier, just like for read. asm volatile("" : : : "memory"); p->p_wpos++; unlock(&p->p_wlock); } return i; }
static ssize_t pipewrite(struct Fd *fd, const void *vbuf, size_t n, off_t offset) { // Your code here. See the lab text for a description of what // pipewrite needs to do. Write a loop that transfers one byte // at a time. Unlike in read, it is not okay to write only some // of the data. If the pipe fills and you've only copied some of // the data, wait for the pipe to empty and then keep copying. // If the pipe is full and closed, return 0. // Use _pipeisclosed to check whether the pipe is closed. struct Pipe *pipe; int k; const char *src; src = vbuf; pipe = (struct Pipe *)fd2data(fd); for(k=n; k!=0; k--) { afterYield: if(pipe->p_wpos - pipe->p_rpos >= PIPEBUFSIZ) { if(_pipeisclosed(fd, pipe)) return 0; sys_yield(); goto afterYield; } pipe->p_buf[pipe->p_wpos % PIPEBUFSIZ] = *src; src++; pipe->p_wpos++; //unfixed race condition - ren } return n; }
static ssize_t piperead(struct Fd *fd, void *vbuf, size_t n, off_t offset) { // Your code here. See the lab text for a description of // what piperead needs to do. Write a loop that // transfers one byte at a time. If you decide you need // to yield (because the pipe is empty), only yield if // you have not yet copied any bytes. (If you have copied // some bytes, return what you have instead of yielding.) // If the pipe is empty and closed and you didn't copy any data out, // return 0. // Use _pipeisclosed to check whether the pipe is closed. struct Pipe *p; uint8_t *buf; int i; USED(offset); p = (struct Pipe *)fd2data(fd); buf = vbuf; for (i = 0; i < n; i++) { while (p->p_rpos >= p->p_wpos) { // copied some bytes, then just return if (i > 0) return i; // i == 0 // i.e. have not yet copied any bytes if (_pipeisclosed(fd, p)) return 0; sys_yield(); } buf[i] = p->p_buf[p->p_rpos % PIPEBUFSIZ]; p->p_rpos++; } return n; }
static ssize_t piperead(struct Fd *fd, void *vbuf, size_t n, off_t offset) { size_t i; uint8_t *buf; struct Pipe *p; USED(offset); // Your code here. See the lab text for a description of // what piperead needs to do. Write a loop that // transfers one byte at a time. If you decide you need // to yield (because the pipe is empty), only yield if // you have not yet copied any bytes. (If you have copied // some bytes, return what you have instead of yielding.) // If the pipe is empty and closed and you didn't copy any data out, // return 0. // Use _pipeisclosed to check whether the pipe is closed. p = (struct Pipe *) fd2data(fd); buf = (uint8_t *) vbuf; for (i = 0; i < n; i++) { while (pipe_is_empty(p)) { if (_pipeisclosed(fd, p) && !i) return 0; if (!i) sys_yield(); else return i; } buf[i] = p->p_buf[p->p_rpos]; p->p_rpos = (p->p_rpos + 1) % PIPEBUFSIZ; } return n; }