eliminate pipe race.
[mit-jos.git] / lib / pipe.c
blob85f5f40c37a66874ad21e612f76b2e051d216dd7
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 if (pageref(fd) == pageref(p))
94 return 1;
95 return 0;
98 int
99 pipeisclosed(int fdnum)
101 struct Fd *fd;
102 struct Pipe *p;
103 int r;
105 if ((r = fd_lookup(fdnum, &fd)) < 0)
106 return r;
107 p = (struct Pipe*) fd2data(fd);
108 return _pipeisclosed(fd, p);
111 static ssize_t
112 piperead(struct Fd *fd, void *vbuf, size_t n, off_t offset)
114 // Your code here. See the lab text for a description of
115 // what piperead needs to do. Write a loop that
116 // transfers one byte at a time. If you decide you need
117 // to yield (because the pipe is empty), only yield if
118 // you have not yet copied any bytes. (If you have copied
119 // some bytes, return what you have instead of yielding.)
120 // If the pipe is empty and closed and you didn't copy any data out,
121 // return 0.
122 // Use _pipeisclosed to check whether the pipe is closed.
124 struct Pipe *p;
125 uint8_t *buf;
126 int i;
128 USED(offset);
130 p = (struct Pipe *)fd2data(fd);
131 buf = vbuf;
132 for (i = 0; i < n; i++) {
133 while (p->p_rpos >= p->p_wpos) {
134 // copied some bytes, then just return
135 if (i > 0)
136 return i;
137 // i == 0
138 // i.e. have not yet copied any bytes
139 if (_pipeisclosed(fd, p))
140 return 0;
141 sys_yield();
143 buf[i] = p->p_buf[p->p_rpos % PIPEBUFSIZ];
144 p->p_rpos++;
146 return n;
149 static ssize_t
150 pipewrite(struct Fd *fd, const void *vbuf, size_t n, off_t offset)
152 // Your code here. See the lab text for a description of what
153 // pipewrite needs to do. Write a loop that transfers one byte
154 // at a time. Unlike in read, it is not okay to write only some
155 // of the data. If the pipe fills and you've only copied some of
156 // the data, wait for the pipe to empty and then keep copying.
157 // If the pipe is full and closed, return 0.
158 // Use _pipeisclosed to check whether the pipe is closed.
160 struct Pipe *p;
161 const uint8_t *buf;
162 int i;
164 USED(offset);
166 p = (struct Pipe *)fd2data(fd);
167 buf = vbuf;
168 for (i = 0; i < n; i++) {
169 while (p->p_wpos - p->p_rpos >= PIPEBUFSIZ) {
170 if (_pipeisclosed(fd, p))
171 return 0;
172 sys_yield();
174 p->p_buf[p->p_wpos % PIPEBUFSIZ] = buf[i];
175 p->p_wpos++;
177 return n;
180 static int
181 pipestat(struct Fd *fd, struct Stat *stat)
183 struct Pipe *p = (struct Pipe*) fd2data(fd);
184 strcpy(stat->st_name, "<pipe>");
185 stat->st_size = p->p_wpos - p->p_rpos;
186 stat->st_isdir = 0;
187 stat->st_dev = &devpipe;
188 return 0;
191 static int
192 pipeclose(struct Fd *fd)
194 void *p;
195 int r;
197 p = fd2data(fd);
198 if ((r = sys_page_unmap(0, fd)) < 0)
199 return r;
200 return sys_page_unmap(0, p);