gcc/
[official-gcc.git] / libgo / runtime / netpoll.goc
blob02705734dd8eb1637e08933db2585c9460a8a394
1 // Copyright 2013 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // +build darwin dragonfly freebsd linux netbsd openbsd windows
7 package net
9 #include "runtime.h"
10 #include "defs.h"
11 #include "arch.h"
12 #include "malloc.h"
14 // Map gccgo field names to gc field names.
15 // Eface aka __go_empty_interface.
16 #define type __type_descriptor
17 #define data __object
19 // Integrated network poller (platform-independent part).
20 // A particular implementation (epoll/kqueue) must define the following functions:
21 // void runtime_netpollinit(void);                      // to initialize the poller
22 // int32 runtime_netpollopen(uintptr fd, PollDesc *pd); // to arm edge-triggered notifications
23                                                         // and associate fd with pd.
24 // An implementation must call the following function to denote that the pd is ready.
25 // void runtime_netpollready(G **gpp, PollDesc *pd, int32 mode);
27 #define READY ((G*)1)
29 struct PollDesc
31         PollDesc* link; // in pollcache, protected by pollcache.Lock
32         Lock;           // protectes the following fields
33         uintptr fd;
34         bool    closing;
35         uintptr seq;    // protects from stale timers and ready notifications
36         G*      rg;     // G waiting for read or READY (binary semaphore)
37         Timer   rt;     // read deadline timer (set if rt.fv != nil)
38         int64   rd;     // read deadline
39         G*      wg;     // the same for writes
40         Timer   wt;
41         int64   wd;
44 static struct
46         Lock;
47         PollDesc*       first;
48         // PollDesc objects must be type-stable,
49         // because we can get ready notification from epoll/kqueue
50         // after the descriptor is closed/reused.
51         // Stale notifications are detected using seq variable,
52         // seq is incremented when deadlines are changed or descriptor is reused.
53 } pollcache;
55 static bool     netpollblock(PollDesc*, int32);
56 static G*       netpollunblock(PollDesc*, int32, bool);
57 static void     deadline(int64, Eface);
58 static void     readDeadline(int64, Eface);
59 static void     writeDeadline(int64, Eface);
60 static PollDesc*        allocPollDesc(void);
61 static intgo    checkerr(PollDesc *pd, int32 mode);
63 static FuncVal deadlineFn       = {(void(*)(void))deadline};
64 static FuncVal readDeadlineFn   = {(void(*)(void))readDeadline};
65 static FuncVal writeDeadlineFn  = {(void(*)(void))writeDeadline};
67 func runtime_pollServerInit() {
68         runtime_netpollinit();
71 func runtime_pollOpen(fd uintptr) (pd *PollDesc, errno int) {
72         pd = allocPollDesc();
73         runtime_lock(pd);
74         if(pd->wg != nil && pd->wg != READY)
75                 runtime_throw("runtime_pollOpen: blocked write on free descriptor");
76         if(pd->rg != nil && pd->rg != READY)
77                 runtime_throw("runtime_pollOpen: blocked read on free descriptor");
78         pd->fd = fd;
79         pd->closing = false;
80         pd->seq++;
81         pd->rg = nil;
82         pd->rd = 0;
83         pd->wg = nil;
84         pd->wd = 0;
85         runtime_unlock(pd);
87         errno = runtime_netpollopen(fd, pd);
90 func runtime_pollClose(pd *PollDesc) {
91         if(!pd->closing)
92                 runtime_throw("runtime_pollClose: close w/o unblock");
93         if(pd->wg != nil && pd->wg != READY)
94                 runtime_throw("runtime_pollClose: blocked write on closing descriptor");
95         if(pd->rg != nil && pd->rg != READY)
96                 runtime_throw("runtime_pollClose: blocked read on closing descriptor");
97         runtime_netpollclose(pd->fd);
98         runtime_lock(&pollcache);
99         pd->link = pollcache.first;
100         pollcache.first = pd;
101         runtime_unlock(&pollcache);
104 func runtime_pollReset(pd *PollDesc, mode int) (err int) {
105         runtime_lock(pd);
106         err = checkerr(pd, mode);
107         if(err)
108                 goto ret;
109         if(mode == 'r')
110                 pd->rg = nil;
111         else if(mode == 'w')
112                 pd->wg = nil;
113 ret:
114         runtime_unlock(pd);
117 func runtime_pollWait(pd *PollDesc, mode int) (err int) {
118         runtime_lock(pd);
119         err = checkerr(pd, mode);
120         if(err == 0) {
121                 while(!netpollblock(pd, mode)) {
122                         err = checkerr(pd, mode);
123                         if(err != 0)
124                                 break;
125                         // Can happen if timeout has fired and unblocked us,
126                         // but before we had a chance to run, timeout has been reset.
127                         // Pretend it has not happened and retry.
128                 }
129         }
130         runtime_unlock(pd);
133 func runtime_pollWaitCanceled(pd *PollDesc, mode int) {
134         runtime_lock(pd);
135         // wait for ioready, ignore closing or timeouts.
136         while(!netpollblock(pd, mode))
137                 ;
138         runtime_unlock(pd);
141 func runtime_pollSetDeadline(pd *PollDesc, d int64, mode int) {
142         G *rg, *wg;
144         runtime_lock(pd);
145         if(pd->closing) {
146                 runtime_unlock(pd);
147                 return;
148         }
149         pd->seq++;  // invalidate current timers
150         // Reset current timers.
151         if(pd->rt.fv) {
152                 runtime_deltimer(&pd->rt);
153                 pd->rt.fv = nil;
154         }
155         if(pd->wt.fv) {
156                 runtime_deltimer(&pd->wt);
157                 pd->wt.fv = nil;
158         }
159         // Setup new timers.
160         if(d != 0 && d <= runtime_nanotime())
161                 d = -1;
162         if(mode == 'r' || mode == 'r'+'w')
163                 pd->rd = d;
164         if(mode == 'w' || mode == 'r'+'w')
165                 pd->wd = d;
166         if(pd->rd > 0 && pd->rd == pd->wd) {
167                 pd->rt.fv = &deadlineFn;
168                 pd->rt.when = pd->rd;
169                 // Copy current seq into the timer arg.
170                 // Timer func will check the seq against current descriptor seq,
171                 // if they differ the descriptor was reused or timers were reset.
172                 pd->rt.arg.type = (Type*)pd->seq;
173                 pd->rt.arg.data = pd;
174                 runtime_addtimer(&pd->rt);
175         } else {
176                 if(pd->rd > 0) {
177                         pd->rt.fv = &readDeadlineFn;
178                         pd->rt.when = pd->rd;
179                         pd->rt.arg.type = (Type*)pd->seq;
180                         pd->rt.arg.data = pd;
181                         runtime_addtimer(&pd->rt);
182                 }
183                 if(pd->wd > 0) {
184                         pd->wt.fv = &writeDeadlineFn;
185                         pd->wt.when = pd->wd;
186                         pd->wt.arg.type = (Type*)pd->seq;
187                         pd->wt.arg.data = pd;
188                         runtime_addtimer(&pd->wt);
189                 }
190         }
191         // If we set the new deadline in the past, unblock currently pending IO if any.
192         rg = nil;
193         wg = nil;
194         if(pd->rd < 0)
195                 rg = netpollunblock(pd, 'r', false);
196         if(pd->wd < 0)
197                 wg = netpollunblock(pd, 'w', false);
198         runtime_unlock(pd);
199         if(rg)
200                 runtime_ready(rg);
201         if(wg)
202                 runtime_ready(wg);
205 func runtime_pollUnblock(pd *PollDesc) {
206         G *rg, *wg;
208         runtime_lock(pd);
209         if(pd->closing)
210                 runtime_throw("runtime_pollUnblock: already closing");
211         pd->closing = true;
212         pd->seq++;
213         rg = netpollunblock(pd, 'r', false);
214         wg = netpollunblock(pd, 'w', false);
215         if(pd->rt.fv) {
216                 runtime_deltimer(&pd->rt);
217                 pd->rt.fv = nil;
218         }
219         if(pd->wt.fv) {
220                 runtime_deltimer(&pd->wt);
221                 pd->wt.fv = nil;
222         }
223         runtime_unlock(pd);
224         if(rg)
225                 runtime_ready(rg);
226         if(wg)
227                 runtime_ready(wg);
230 uintptr
231 runtime_netpollfd(PollDesc *pd)
233         return pd->fd;
236 // make pd ready, newly runnable goroutines (if any) are enqueued info gpp list
237 void
238 runtime_netpollready(G **gpp, PollDesc *pd, int32 mode)
240         G *rg, *wg;
242         rg = wg = nil;
243         runtime_lock(pd);
244         if(mode == 'r' || mode == 'r'+'w')
245                 rg = netpollunblock(pd, 'r', true);
246         if(mode == 'w' || mode == 'r'+'w')
247                 wg = netpollunblock(pd, 'w', true);
248         runtime_unlock(pd);
249         if(rg) {
250                 rg->schedlink = *gpp;
251                 *gpp = rg;
252         }
253         if(wg) {
254                 wg->schedlink = *gpp;
255                 *gpp = wg;
256         }
259 static intgo
260 checkerr(PollDesc *pd, int32 mode)
262         if(pd->closing)
263                 return 1;  // errClosing
264         if((mode == 'r' && pd->rd < 0) || (mode == 'w' && pd->wd < 0))
265                 return 2;  // errTimeout
266         return 0;
269 // returns true if IO is ready, or false if timedout or closed
270 static bool
271 netpollblock(PollDesc *pd, int32 mode)
273         G **gpp;
275         gpp = &pd->rg;
276         if(mode == 'w')
277                 gpp = &pd->wg;
278         if(*gpp == READY) {
279                 *gpp = nil;
280                 return true;
281         }
282         if(*gpp != nil)
283                 runtime_throw("netpollblock: double wait");
284         *gpp = runtime_g();
285         runtime_park(runtime_unlock, &pd->Lock, "IO wait");
286         runtime_lock(pd);
287         if(runtime_g()->param)
288                 return true;
289         return false;
292 static G*
293 netpollunblock(PollDesc *pd, int32 mode, bool ioready)
295         G **gpp, *old;
297         gpp = &pd->rg;
298         if(mode == 'w')
299                 gpp = &pd->wg;
300         if(*gpp == READY)
301                 return nil;
302         if(*gpp == nil) {
303                 // Only set READY for ioready. runtime_pollWait
304                 // will check for timeout/cancel before waiting.
305                 if(ioready)
306                         *gpp = READY;
307                 return nil;
308         }
309         old = *gpp;
310         // pass unblock reason onto blocked g
311         old->param = (void*)(uintptr)ioready;
312         *gpp = nil;
313         return old;
316 static void
317 deadlineimpl(int64 now, Eface arg, bool read, bool write)
319         PollDesc *pd;
320         uint32 seq;
321         G *rg, *wg;
323         USED(now);
324         pd = (PollDesc*)arg.data;
325         // This is the seq when the timer was set.
326         // If it's stale, ignore the timer event.
327         seq = (uintptr)arg.type;
328         rg = wg = nil;
329         runtime_lock(pd);
330         if(seq != pd->seq) {
331                 // The descriptor was reused or timers were reset.
332                 runtime_unlock(pd);
333                 return;
334         }
335         if(read) {
336                 if(pd->rd <= 0 || pd->rt.fv == nil)
337                         runtime_throw("deadlineimpl: inconsistent read deadline");
338                 pd->rd = -1;
339                 pd->rt.fv = nil;
340                 rg = netpollunblock(pd, 'r', false);
341         }
342         if(write) {
343                 if(pd->wd <= 0 || (pd->wt.fv == nil && !read))
344                         runtime_throw("deadlineimpl: inconsistent write deadline");
345                 pd->wd = -1;
346                 pd->wt.fv = nil;
347                 wg = netpollunblock(pd, 'w', false);
348         }
349         runtime_unlock(pd);
350         if(rg)
351                 runtime_ready(rg);
352         if(wg)
353                 runtime_ready(wg);
356 static void
357 deadline(int64 now, Eface arg)
359         deadlineimpl(now, arg, true, true);
362 static void
363 readDeadline(int64 now, Eface arg)
365         deadlineimpl(now, arg, true, false);
368 static void
369 writeDeadline(int64 now, Eface arg)
371         deadlineimpl(now, arg, false, true);
374 static PollDesc*
375 allocPollDesc(void)
377         PollDesc *pd;
378         uint32 i, n;
380         runtime_lock(&pollcache);
381         if(pollcache.first == nil) {
382                 n = PageSize/sizeof(*pd);
383                 if(n == 0)
384                         n = 1;
385                 // Must be in non-GC memory because can be referenced
386                 // only from epoll/kqueue internals.
387                 pd = runtime_persistentalloc(n*sizeof(*pd), 0, &mstats.other_sys);
388                 for(i = 0; i < n; i++) {
389                         pd[i].link = pollcache.first;
390                         pollcache.first = &pd[i];
391                 }
392         }
393         pd = pollcache.first;
394         pollcache.first = pd->link;
395         runtime_unlock(&pollcache);
396         return pd;