github.com/prattmic/llgo-embedded@v0.0.0-20150820070356-41cfecea0e1e/third_party/gofrontend/libgo/runtime/netpoll.goc (about)

     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.
     4  
     5  // +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
     6  
     7  package net
     8  
     9  #include "runtime.h"
    10  #include "defs.h"
    11  #include "arch.h"
    12  #include "malloc.h"
    13  
    14  // Map gccgo field names to gc field names.
    15  // Eface aka __go_empty_interface.
    16  #define type __type_descriptor
    17  #define data __object
    18  
    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);
    26  
    27  // PollDesc contains 2 binary semaphores, rg and wg, to park reader and writer
    28  // goroutines respectively. The semaphore can be in the following states:
    29  // READY - io readiness notification is pending;
    30  //         a goroutine consumes the notification by changing the state to nil.
    31  // WAIT - a goroutine prepares to park on the semaphore, but not yet parked;
    32  //        the goroutine commits to park by changing the state to G pointer,
    33  //        or, alternatively, concurrent io notification changes the state to READY,
    34  //        or, alternatively, concurrent timeout/close changes the state to nil.
    35  // G pointer - the goroutine is blocked on the semaphore;
    36  //             io notification or timeout/close changes the state to READY or nil respectively
    37  //             and unparks the goroutine.
    38  // nil - nothing of the above.
    39  #define READY ((G*)1)
    40  #define WAIT  ((G*)2)
    41  
    42  enum
    43  {
    44  	PollBlockSize	= 4*1024,
    45  };
    46  
    47  struct PollDesc
    48  {
    49  	PollDesc* link;	// in pollcache, protected by pollcache.Lock
    50  
    51  	// The lock protects pollOpen, pollSetDeadline, pollUnblock and deadlineimpl operations.
    52  	// This fully covers seq, rt and wt variables. fd is constant throughout the PollDesc lifetime.
    53  	// pollReset, pollWait, pollWaitCanceled and runtime_netpollready (IO rediness notification)
    54  	// proceed w/o taking the lock. So closing, rg, rd, wg and wd are manipulated
    55  	// in a lock-free way by all operations.
    56  	Lock	lock;	// protectes the following fields
    57  	uintptr	fd;
    58  	bool	closing;
    59  	uintptr	seq;	// protects from stale timers and ready notifications
    60  	G*	rg;	// READY, WAIT, G waiting for read or nil
    61  	Timer	rt;	// read deadline timer (set if rt.fv != nil)
    62  	int64	rd;	// read deadline
    63  	G*	wg;	// READY, WAIT, G waiting for write or nil
    64  	Timer	wt;	// write deadline timer
    65  	int64	wd;	// write deadline
    66  	void*	user;	// user settable cookie
    67  };
    68  
    69  static struct
    70  {
    71  	Lock		lock;
    72  	PollDesc*	first;
    73  	// PollDesc objects must be type-stable,
    74  	// because we can get ready notification from epoll/kqueue
    75  	// after the descriptor is closed/reused.
    76  	// Stale notifications are detected using seq variable,
    77  	// seq is incremented when deadlines are changed or descriptor is reused.
    78  } pollcache;
    79  
    80  static bool	netpollblock(PollDesc*, int32, bool);
    81  static G*	netpollunblock(PollDesc*, int32, bool);
    82  static void	deadline(Eface, uintptr);
    83  static void	readDeadline(Eface, uintptr);
    84  static void	writeDeadline(Eface, uintptr);
    85  static PollDesc*	allocPollDesc(void);
    86  static intgo	checkerr(PollDesc *pd, int32 mode);
    87  
    88  static FuncVal deadlineFn	= {(void(*)(void))deadline};
    89  static FuncVal readDeadlineFn	= {(void(*)(void))readDeadline};
    90  static FuncVal writeDeadlineFn	= {(void(*)(void))writeDeadline};
    91  
    92  // runtimeNano returns the current value of the runtime clock in nanoseconds.
    93  func runtimeNano() (ns int64) {
    94  	ns = runtime_nanotime();
    95  }
    96  
    97  func runtime_pollServerInit() {
    98  	runtime_netpollinit();
    99  }
   100  
   101  func runtime_pollOpen(fd uintptr) (pd *PollDesc, errno int) {
   102  	pd = allocPollDesc();
   103  	runtime_lock(&pd->lock);
   104  	if(pd->wg != nil && pd->wg != READY)
   105  		runtime_throw("runtime_pollOpen: blocked write on free descriptor");
   106  	if(pd->rg != nil && pd->rg != READY)
   107  		runtime_throw("runtime_pollOpen: blocked read on free descriptor");
   108  	pd->fd = fd;
   109  	pd->closing = false;
   110  	pd->seq++;
   111  	pd->rg = nil;
   112  	pd->rd = 0;
   113  	pd->wg = nil;
   114  	pd->wd = 0;
   115  	runtime_unlock(&pd->lock);
   116  
   117  	errno = runtime_netpollopen(fd, pd);
   118  }
   119  
   120  func runtime_pollClose(pd *PollDesc) {
   121  	if(!pd->closing)
   122  		runtime_throw("runtime_pollClose: close w/o unblock");
   123  	if(pd->wg != nil && pd->wg != READY)
   124  		runtime_throw("runtime_pollClose: blocked write on closing descriptor");
   125  	if(pd->rg != nil && pd->rg != READY)
   126  		runtime_throw("runtime_pollClose: blocked read on closing descriptor");
   127  	runtime_netpollclose(pd->fd);
   128  	runtime_lock(&pollcache.lock);
   129  	pd->link = pollcache.first;
   130  	pollcache.first = pd;
   131  	runtime_unlock(&pollcache.lock);
   132  }
   133  
   134  func runtime_pollReset(pd *PollDesc, mode int) (err int) {
   135  	err = checkerr(pd, mode);
   136  	if(err)
   137  		goto ret;
   138  	if(mode == 'r')
   139  		pd->rg = nil;
   140  	else if(mode == 'w')
   141  		pd->wg = nil;
   142  ret:
   143  }
   144  
   145  func runtime_pollWait(pd *PollDesc, mode int) (err int) {
   146  	err = checkerr(pd, mode);
   147  	if(err == 0) {
   148  		// As for now only Solaris uses level-triggered IO.
   149  		if(Solaris)
   150  			runtime_netpollarm(pd, mode);
   151  		while(!netpollblock(pd, mode, false)) {
   152  			err = checkerr(pd, mode);
   153  			if(err != 0)
   154  				break;
   155  			// Can happen if timeout has fired and unblocked us,
   156  			// but before we had a chance to run, timeout has been reset.
   157  			// Pretend it has not happened and retry.
   158  		}
   159  	}
   160  }
   161  
   162  func runtime_pollWaitCanceled(pd *PollDesc, mode int) {
   163  	// This function is used only on windows after a failed attempt to cancel
   164  	// a pending async IO operation. Wait for ioready, ignore closing or timeouts.
   165  	while(!netpollblock(pd, mode, true))
   166  		;
   167  }
   168  
   169  func runtime_pollSetDeadline(pd *PollDesc, d int64, mode int) {
   170  	G *rg, *wg;
   171  
   172  	runtime_lock(&pd->lock);
   173  	if(pd->closing) {
   174  		runtime_unlock(&pd->lock);
   175  		return;
   176  	}
   177  	pd->seq++;  // invalidate current timers
   178  	// Reset current timers.
   179  	if(pd->rt.fv) {
   180  		runtime_deltimer(&pd->rt);
   181  		pd->rt.fv = nil;
   182  	}
   183  	if(pd->wt.fv) {
   184  		runtime_deltimer(&pd->wt);
   185  		pd->wt.fv = nil;
   186  	}
   187  	// Setup new timers.
   188  	if(d != 0 && d <= runtime_nanotime())
   189  		d = -1;
   190  	if(mode == 'r' || mode == 'r'+'w')
   191  		pd->rd = d;
   192  	if(mode == 'w' || mode == 'r'+'w')
   193  		pd->wd = d;
   194  	if(pd->rd > 0 && pd->rd == pd->wd) {
   195  		pd->rt.fv = &deadlineFn;
   196  		pd->rt.when = pd->rd;
   197  		// Copy current seq into the timer arg.
   198  		// Timer func will check the seq against current descriptor seq,
   199  		// if they differ the descriptor was reused or timers were reset.
   200  		pd->rt.arg.type = nil; // should be *pollDesc type descriptor.
   201  		pd->rt.arg.data = pd;
   202  		pd->rt.seq = pd->seq;
   203  		runtime_addtimer(&pd->rt);
   204  	} else {
   205  		if(pd->rd > 0) {
   206  			pd->rt.fv = &readDeadlineFn;
   207  			pd->rt.when = pd->rd;
   208  			pd->rt.arg.type = nil; // should be *pollDesc type descriptor.
   209  			pd->rt.arg.data = pd;
   210  			pd->rt.seq = pd->seq;
   211  			runtime_addtimer(&pd->rt);
   212  		}
   213  		if(pd->wd > 0) {
   214  			pd->wt.fv = &writeDeadlineFn;
   215  			pd->wt.when = pd->wd;
   216  			pd->wt.arg.type = nil; // should be *pollDesc type descriptor.
   217  			pd->wt.arg.data = pd;
   218  			pd->wt.seq = pd->seq;
   219  			runtime_addtimer(&pd->wt);
   220  		}
   221  	}
   222  	// If we set the new deadline in the past, unblock currently pending IO if any.
   223  	rg = nil;
   224  	runtime_atomicstorep(&wg, nil);  // full memory barrier between stores to rd/wd and load of rg/wg in netpollunblock
   225  	if(pd->rd < 0)
   226  		rg = netpollunblock(pd, 'r', false);
   227  	if(pd->wd < 0)
   228  		wg = netpollunblock(pd, 'w', false);
   229  	runtime_unlock(&pd->lock);
   230  	if(rg)
   231  		runtime_ready(rg);
   232  	if(wg)
   233  		runtime_ready(wg);
   234  }
   235  
   236  func runtime_pollUnblock(pd *PollDesc) {
   237  	G *rg, *wg;
   238  
   239  	runtime_lock(&pd->lock);
   240  	if(pd->closing)
   241  		runtime_throw("runtime_pollUnblock: already closing");
   242  	pd->closing = true;
   243  	pd->seq++;
   244  	runtime_atomicstorep(&rg, nil);  // full memory barrier between store to closing and read of rg/wg in netpollunblock
   245  	rg = netpollunblock(pd, 'r', false);
   246  	wg = netpollunblock(pd, 'w', false);
   247  	if(pd->rt.fv) {
   248  		runtime_deltimer(&pd->rt);
   249  		pd->rt.fv = nil;
   250  	}
   251  	if(pd->wt.fv) {
   252  		runtime_deltimer(&pd->wt);
   253  		pd->wt.fv = nil;
   254  	}
   255  	runtime_unlock(&pd->lock);
   256  	if(rg)
   257  		runtime_ready(rg);
   258  	if(wg)
   259  		runtime_ready(wg);
   260  }
   261  
   262  uintptr
   263  runtime_netpollfd(PollDesc *pd)
   264  {
   265  	return pd->fd;
   266  }
   267  
   268  void**
   269  runtime_netpolluser(PollDesc *pd)
   270  {
   271  	return &pd->user;
   272  }
   273  
   274  bool
   275  runtime_netpollclosing(PollDesc *pd)
   276  {
   277  	return pd->closing;
   278  }
   279  
   280  void
   281  runtime_netpolllock(PollDesc *pd)
   282  {
   283  	runtime_lock(&pd->lock);
   284  }
   285  
   286  void
   287  runtime_netpollunlock(PollDesc *pd)
   288  {
   289  	runtime_unlock(&pd->lock);
   290  }
   291  
   292  // make pd ready, newly runnable goroutines (if any) are enqueued info gpp list
   293  void
   294  runtime_netpollready(G **gpp, PollDesc *pd, int32 mode)
   295  {
   296  	G *rg, *wg;
   297  
   298  	rg = wg = nil;
   299  	if(mode == 'r' || mode == 'r'+'w')
   300  		rg = netpollunblock(pd, 'r', true);
   301  	if(mode == 'w' || mode == 'r'+'w')
   302  		wg = netpollunblock(pd, 'w', true);
   303  	if(rg) {
   304  		rg->schedlink = *gpp;
   305  		*gpp = rg;
   306  	}
   307  	if(wg) {
   308  		wg->schedlink = *gpp;
   309  		*gpp = wg;
   310  	}
   311  }
   312  
   313  static intgo
   314  checkerr(PollDesc *pd, int32 mode)
   315  {
   316  	if(pd->closing)
   317  		return 1;  // errClosing
   318  	if((mode == 'r' && pd->rd < 0) || (mode == 'w' && pd->wd < 0))
   319  		return 2;  // errTimeout
   320  	return 0;
   321  }
   322  
   323  static bool
   324  blockcommit(G *gp, G **gpp)
   325  {
   326  	return runtime_casp(gpp, WAIT, gp);
   327  }
   328  
   329  // returns true if IO is ready, or false if timedout or closed
   330  // waitio - wait only for completed IO, ignore errors
   331  static bool
   332  netpollblock(PollDesc *pd, int32 mode, bool waitio)
   333  {
   334  	G **gpp, *old;
   335  
   336  	gpp = &pd->rg;
   337  	if(mode == 'w')
   338  		gpp = &pd->wg;
   339  
   340  	// set the gpp semaphore to WAIT
   341  	for(;;) {
   342  		old = *gpp;
   343  		if(old == READY) {
   344  			*gpp = nil;
   345  			return true;
   346  		}
   347  		if(old != nil)
   348  			runtime_throw("netpollblock: double wait");
   349  		if(runtime_casp(gpp, nil, WAIT))
   350  			break;
   351  	}
   352  
   353  	// need to recheck error states after setting gpp to WAIT
   354  	// this is necessary because runtime_pollUnblock/runtime_pollSetDeadline/deadlineimpl
   355  	// do the opposite: store to closing/rd/wd, membarrier, load of rg/wg
   356  	if(waitio || checkerr(pd, mode) == 0)
   357  		runtime_park((bool(*)(G*, void*))blockcommit, gpp, "IO wait");
   358  	// be careful to not lose concurrent READY notification
   359  	old = runtime_xchgp(gpp, nil);
   360  	if(old > WAIT)
   361  		runtime_throw("netpollblock: corrupted state");
   362  	return old == READY;
   363  }
   364  
   365  static G*
   366  netpollunblock(PollDesc *pd, int32 mode, bool ioready)
   367  {
   368  	G **gpp, *old, *new;
   369  
   370  	gpp = &pd->rg;
   371  	if(mode == 'w')
   372  		gpp = &pd->wg;
   373  
   374  	for(;;) {
   375  		old = *gpp;
   376  		if(old == READY)
   377  			return nil;
   378  		if(old == nil && !ioready) {
   379  			// Only set READY for ioready. runtime_pollWait
   380  			// will check for timeout/cancel before waiting.
   381  			return nil;
   382  		}
   383  		new = nil;
   384  		if(ioready)
   385  			new = READY;
   386  		if(runtime_casp(gpp, old, new))
   387  			break;
   388  	}
   389  	if(old > WAIT)
   390  		return old;  // must be G*
   391  	return nil;
   392  }
   393  
   394  static void
   395  deadlineimpl(Eface arg, uintptr seq, bool read, bool write)
   396  {
   397  	PollDesc *pd;
   398  	G *rg, *wg;
   399  
   400  	pd = (PollDesc*)arg.data;
   401  	rg = wg = nil;
   402  	runtime_lock(&pd->lock);
   403  	// Seq arg is seq when the timer was set.
   404  	// If it's stale, ignore the timer event.
   405  	if(seq != pd->seq) {
   406  		// The descriptor was reused or timers were reset.
   407  		runtime_unlock(&pd->lock);
   408  		return;
   409  	}
   410  	if(read) {
   411  		if(pd->rd <= 0 || pd->rt.fv == nil)
   412  			runtime_throw("deadlineimpl: inconsistent read deadline");
   413  		pd->rd = -1;
   414  		runtime_atomicstorep(&pd->rt.fv, nil);  // full memory barrier between store to rd and load of rg in netpollunblock
   415  		rg = netpollunblock(pd, 'r', false);
   416  	}
   417  	if(write) {
   418  		if(pd->wd <= 0 || (pd->wt.fv == nil && !read))
   419  			runtime_throw("deadlineimpl: inconsistent write deadline");
   420  		pd->wd = -1;
   421  		runtime_atomicstorep(&pd->wt.fv, nil);  // full memory barrier between store to wd and load of wg in netpollunblock
   422  		wg = netpollunblock(pd, 'w', false);
   423  	}
   424  	runtime_unlock(&pd->lock);
   425  	if(rg)
   426  		runtime_ready(rg);
   427  	if(wg)
   428  		runtime_ready(wg);
   429  }
   430  
   431  static void
   432  deadline(Eface arg, uintptr seq)
   433  {
   434  	deadlineimpl(arg, seq, true, true);
   435  }
   436  
   437  static void
   438  readDeadline(Eface arg, uintptr seq)
   439  {
   440  	deadlineimpl(arg, seq, true, false);
   441  }
   442  
   443  static void
   444  writeDeadline(Eface arg, uintptr seq)
   445  {
   446  	deadlineimpl(arg, seq, false, true);
   447  }
   448  
   449  static PollDesc*
   450  allocPollDesc(void)
   451  {
   452  	PollDesc *pd;
   453  	uint32 i, n;
   454  
   455  	runtime_lock(&pollcache.lock);
   456  	if(pollcache.first == nil) {
   457  		n = PollBlockSize/sizeof(*pd);
   458  		if(n == 0)
   459  			n = 1;
   460  		// Must be in non-GC memory because can be referenced
   461  		// only from epoll/kqueue internals.
   462  		pd = runtime_persistentalloc(n*sizeof(*pd), 0, &mstats.other_sys);
   463  		for(i = 0; i < n; i++) {
   464  			pd[i].link = pollcache.first;
   465  			pollcache.first = &pd[i];
   466  		}
   467  	}
   468  	pd = pollcache.first;
   469  	pollcache.first = pd->link;
   470  	runtime_unlock(&pollcache.lock);
   471  	return pd;
   472  }