still serv_close bug...
[mit-jos.git] / lib / pipe.c
blobb7e4f6602798e72d378d39d725d878e70593fcfc
1 #include <inc/lib.h>
3 #define debug 0
5 static int pipeclose(struct Fd *fd);
6 static ssize_t piperead(struct Fd *fd, void *buf, size_t n, off_t offset);
7 static int pipestat(struct Fd *fd, struct Stat *stat);
8 static ssize_t pipewrite(struct Fd *fd, const void *buf, size_t n, off_t offset);
10 struct Dev devpipe =
12 .dev_id= 'p',
13 .dev_name= "pipe",
14 .dev_read= piperead,
15 .dev_write= pipewrite,
16 .dev_close= pipeclose,
17 .dev_stat= pipestat,
20 #define PIPEBUFSIZ 32 // small to provoke races
22 struct Pipe {
23 off_t p_rpos; // read position
24 off_t p_wpos; // write position
25 uint8_t p_buf[PIPEBUFSIZ]; // data buffer
28 int
29 pipe(int pfd[2])
31 int r;
32 struct Fd *fd0, *fd1;
33 void *va;
35 // allocate the file descriptor table entries
36 if ((r = fd_alloc(&fd0)) < 0
37 || (r = sys_page_alloc(0, fd0, PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0)
38 goto err;
40 if ((r = fd_alloc(&fd1)) < 0
41 || (r = sys_page_alloc(0, fd1, PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0)
42 goto err1;
44 // allocate the pipe structure as first data page in both
45 va = fd2data(fd0);
46 if ((r = sys_page_alloc(0, va, PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0)
47 goto err2;
48 if ((r = sys_page_map(0, va, 0, fd2data(fd1), PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0)
49 goto err3;
51 // set up fd structures
52 fd0->fd_dev_id = devpipe.dev_id;
53 fd0->fd_omode = O_RDONLY;
55 fd1->fd_dev_id = devpipe.dev_id;
56 fd1->fd_omode = O_WRONLY;
58 if (debug)
59 cprintf("[%08x] pipecreate %08x\n", env->env_id, vpt[VPN(va)]);
61 pfd[0] = fd2num(fd0);
62 pfd[1] = fd2num(fd1);
63 return 0;
65 err3:
66 sys_page_unmap(0, va);
67 err2:
68 sys_page_unmap(0, fd1);
69 err1:
70 sys_page_unmap(0, fd0);
71 err:
72 return r;
75 static int
76 _pipeisclosed(struct Fd *fd, struct Pipe *p)
78 // Your code here.
79 //
80 // Check pageref(fd) and pageref(p),
81 // returning 1 if they're the same, 0 otherwise.
82 //
83 // The logic here is that pageref(p) is the total
84 // number of readers *and* writers, whereas pageref(fd)
85 // is the number of file descriptors like fd (readers if fd is
86 // a reader, writers if fd is a writer).
87 //
88 // If the number of file descriptors like fd is equal
89 // to the total number of readers and writers, then
90 // everybody left is what fd is. So the other end of
91 // the pipe is closed.
93 int runs, r;
94 do {
95 runs = env->env_runs;
96 r = 0;
97 if (pageref(fd) == pageref(p))
98 r = 1;
99 } while (runs != env->env_runs);
101 return r;
105 pipeisclosed(int fdnum)
107 struct Fd *fd;
108 struct Pipe *p;
109 int r;
111 if ((r = fd_lookup(fdnum, &fd)) < 0)
112 return r;
113 p = (struct Pipe*) fd2data(fd);
114 return _pipeisclosed(fd, p);
117 static ssize_t
118 piperead(struct Fd *fd, void *vbuf, size_t n, off_t offset)
120 // Your code here. See the lab text for a description of
121 // what piperead needs to do. Write a loop that
122 // transfers one byte at a time. If you decide you need
123 // to yield (because the pipe is empty), only yield if
124 // you have not yet copied any bytes. (If you have copied
125 // some bytes, return what you have instead of yielding.)
126 // If the pipe is empty and closed and you didn't copy any data out,
127 // return 0.
128 // Use _pipeisclosed to check whether the pipe is closed.
130 struct Pipe *p;
131 uint8_t *buf;
132 int i;
134 USED(offset);
136 p = (struct Pipe *)fd2data(fd);
137 buf = vbuf;
138 for (i = 0; i < n; i++) {
139 while (p->p_rpos >= p->p_wpos) {
140 // copied some bytes, then just return
141 if (i > 0)
142 return i;
143 // i == 0
144 // i.e. have not yet copied any bytes
145 if (_pipeisclosed(fd, p))
146 return 0;
147 sys_yield();
149 buf[i] = p->p_buf[p->p_rpos % PIPEBUFSIZ];
150 p->p_rpos++;
152 return n;
155 static ssize_t
156 pipewrite(struct Fd *fd, const void *vbuf, size_t n, off_t offset)
158 // Your code here. See the lab text for a description of what
159 // pipewrite needs to do. Write a loop that transfers one byte
160 // at a time. Unlike in read, it is not okay to write only some
161 // of the data. If the pipe fills and you've only copied some of
162 // the data, wait for the pipe to empty and then keep copying.
163 // If the pipe is full and closed, return 0.
164 // Use _pipeisclosed to check whether the pipe is closed.
166 struct Pipe *p;
167 const uint8_t *buf;
168 int i;
170 USED(offset);
172 p = (struct Pipe *)fd2data(fd);
173 buf = vbuf;
174 for (i = 0; i < n; i++) {
175 while (p->p_wpos - p->p_rpos >= PIPEBUFSIZ) {
176 if (_pipeisclosed(fd, p))
177 return 0;
178 sys_yield();
180 p->p_buf[p->p_wpos % PIPEBUFSIZ] = buf[i];
181 p->p_wpos++;
183 return n;
186 static int
187 pipestat(struct Fd *fd, struct Stat *stat)
189 struct Pipe *p = (struct Pipe*) fd2data(fd);
190 strcpy(stat->st_name, "<pipe>");
191 stat->st_size = p->p_wpos - p->p_rpos;
192 stat->st_isdir = 0;
193 stat->st_dev = &devpipe;
194 return 0;
197 static int
198 pipeclose(struct Fd *fd)
200 void *p;
201 int r;
203 p = fd2data(fd);
204 if ((r = sys_page_unmap(0, fd)) < 0)
205 return r;
206 return sys_page_unmap(0, p);