Source file src/runtime/netpoll_aix.go

     1  // Copyright 2018 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  package runtime
     6  
     7  import (
     8  	"runtime/internal/atomic"
     9  	"unsafe"
    10  )
    11  
    12  // This is based on the former libgo/runtime/netpoll_select.c implementation
    13  // except that it uses poll instead of select and is written in Go.
    14  // It's also based on Solaris implementation for the arming mechanisms
    15  
    16  //go:cgo_import_dynamic libc_poll poll "libc.a/shr_64.o"
    17  //go:linkname libc_poll libc_poll
    18  
    19  var libc_poll libFunc
    20  
    21  //go:nosplit
    22  func poll(pfds *pollfd, npfds uintptr, timeout uintptr) (int32, int32) {
    23  	r, err := syscall3(&libc_poll, uintptr(unsafe.Pointer(pfds)), npfds, timeout)
    24  	return int32(r), int32(err)
    25  }
    26  
    27  // pollfd represents the poll structure for AIX operating system.
    28  type pollfd struct {
    29  	fd      int32
    30  	events  int16
    31  	revents int16
    32  }
    33  
    34  const _POLLIN = 0x0001
    35  const _POLLOUT = 0x0002
    36  const _POLLHUP = 0x2000
    37  const _POLLERR = 0x4000
    38  
    39  var (
    40  	pfds           []pollfd
    41  	pds            []*pollDesc
    42  	mtxpoll        mutex
    43  	mtxset         mutex
    44  	rdwake         int32
    45  	wrwake         int32
    46  	pendingUpdates int32
    47  
    48  	netpollWakeSig atomic.Uint32 // used to avoid duplicate calls of netpollBreak
    49  )
    50  
    51  func netpollinit() {
    52  	// Create the pipe we use to wakeup poll.
    53  	r, w, errno := nonblockingPipe()
    54  	if errno != 0 {
    55  		throw("netpollinit: failed to create pipe")
    56  	}
    57  	rdwake = r
    58  	wrwake = w
    59  
    60  	// Pre-allocate array of pollfd structures for poll.
    61  	pfds = make([]pollfd, 1, 128)
    62  
    63  	// Poll the read side of the pipe.
    64  	pfds[0].fd = rdwake
    65  	pfds[0].events = _POLLIN
    66  
    67  	pds = make([]*pollDesc, 1, 128)
    68  	pds[0] = nil
    69  }
    70  
    71  func netpollIsPollDescriptor(fd uintptr) bool {
    72  	return fd == uintptr(rdwake) || fd == uintptr(wrwake)
    73  }
    74  
    75  // netpollwakeup writes on wrwake to wakeup poll before any changes.
    76  func netpollwakeup() {
    77  	if pendingUpdates == 0 {
    78  		pendingUpdates = 1
    79  		b := [1]byte{0}
    80  		write(uintptr(wrwake), unsafe.Pointer(&b[0]), 1)
    81  	}
    82  }
    83  
    84  func netpollopen(fd uintptr, pd *pollDesc) int32 {
    85  	lock(&mtxpoll)
    86  	netpollwakeup()
    87  
    88  	lock(&mtxset)
    89  	unlock(&mtxpoll)
    90  
    91  	pd.user = uint32(len(pfds))
    92  	pfds = append(pfds, pollfd{fd: int32(fd)})
    93  	pds = append(pds, pd)
    94  	unlock(&mtxset)
    95  	return 0
    96  }
    97  
    98  func netpollclose(fd uintptr) int32 {
    99  	lock(&mtxpoll)
   100  	netpollwakeup()
   101  
   102  	lock(&mtxset)
   103  	unlock(&mtxpoll)
   104  
   105  	for i := 0; i < len(pfds); i++ {
   106  		if pfds[i].fd == int32(fd) {
   107  			pfds[i] = pfds[len(pfds)-1]
   108  			pfds = pfds[:len(pfds)-1]
   109  
   110  			pds[i] = pds[len(pds)-1]
   111  			pds[i].user = uint32(i)
   112  			pds = pds[:len(pds)-1]
   113  			break
   114  		}
   115  	}
   116  	unlock(&mtxset)
   117  	return 0
   118  }
   119  
   120  func netpollarm(pd *pollDesc, mode int) {
   121  	lock(&mtxpoll)
   122  	netpollwakeup()
   123  
   124  	lock(&mtxset)
   125  	unlock(&mtxpoll)
   126  
   127  	switch mode {
   128  	case 'r':
   129  		pfds[pd.user].events |= _POLLIN
   130  	case 'w':
   131  		pfds[pd.user].events |= _POLLOUT
   132  	}
   133  	unlock(&mtxset)
   134  }
   135  
   136  // netpollBreak interrupts a poll.
   137  func netpollBreak() {
   138  	// Failing to cas indicates there is an in-flight wakeup, so we're done here.
   139  	if !netpollWakeSig.CompareAndSwap(0, 1) {
   140  		return
   141  	}
   142  
   143  	b := [1]byte{0}
   144  	write(uintptr(wrwake), unsafe.Pointer(&b[0]), 1)
   145  }
   146  
   147  // netpoll checks for ready network connections.
   148  // Returns list of goroutines that become runnable.
   149  // delay < 0: blocks indefinitely
   150  // delay == 0: does not block, just polls
   151  // delay > 0: block for up to that many nanoseconds
   152  //
   153  //go:nowritebarrierrec
   154  func netpoll(delay int64) gList {
   155  	var timeout uintptr
   156  	if delay < 0 {
   157  		timeout = ^uintptr(0)
   158  	} else if delay == 0 {
   159  		// TODO: call poll with timeout == 0
   160  		return gList{}
   161  	} else if delay < 1e6 {
   162  		timeout = 1
   163  	} else if delay < 1e15 {
   164  		timeout = uintptr(delay / 1e6)
   165  	} else {
   166  		// An arbitrary cap on how long to wait for a timer.
   167  		// 1e9 ms == ~11.5 days.
   168  		timeout = 1e9
   169  	}
   170  retry:
   171  	lock(&mtxpoll)
   172  	lock(&mtxset)
   173  	pendingUpdates = 0
   174  	unlock(&mtxpoll)
   175  
   176  	n, e := poll(&pfds[0], uintptr(len(pfds)), timeout)
   177  	if n < 0 {
   178  		if e != _EINTR {
   179  			println("errno=", e, " len(pfds)=", len(pfds))
   180  			throw("poll failed")
   181  		}
   182  		unlock(&mtxset)
   183  		// If a timed sleep was interrupted, just return to
   184  		// recalculate how long we should sleep now.
   185  		if timeout > 0 {
   186  			return gList{}
   187  		}
   188  		goto retry
   189  	}
   190  	// Check if some descriptors need to be changed
   191  	if n != 0 && pfds[0].revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 {
   192  		if delay != 0 {
   193  			// A netpollwakeup could be picked up by a
   194  			// non-blocking poll. Only clear the wakeup
   195  			// if blocking.
   196  			var b [1]byte
   197  			for read(rdwake, unsafe.Pointer(&b[0]), 1) == 1 {
   198  			}
   199  			netpollWakeSig.Store(0)
   200  		}
   201  		// Still look at the other fds even if the mode may have
   202  		// changed, as netpollBreak might have been called.
   203  		n--
   204  	}
   205  	var toRun gList
   206  	for i := 1; i < len(pfds) && n > 0; i++ {
   207  		pfd := &pfds[i]
   208  
   209  		var mode int32
   210  		if pfd.revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 {
   211  			mode += 'r'
   212  			pfd.events &= ^_POLLIN
   213  		}
   214  		if pfd.revents&(_POLLOUT|_POLLHUP|_POLLERR) != 0 {
   215  			mode += 'w'
   216  			pfd.events &= ^_POLLOUT
   217  		}
   218  		if mode != 0 {
   219  			pds[i].setEventErr(pfd.revents == _POLLERR)
   220  			netpollready(&toRun, pds[i], mode)
   221  			n--
   222  		}
   223  	}
   224  	unlock(&mtxset)
   225  	return toRun
   226  }
   227  

View as plain text