github.com/ice-blockchain/go/src@v0.0.0-20240403114104-1564d284e521/runtime/netpoll_wasip1.go (about) 1 // Copyright 2023 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 //go:build wasip1 6 7 package runtime 8 9 import "unsafe" 10 11 // WASI network poller. 12 // 13 // WASI preview 1 includes a poll_oneoff host function that behaves similarly 14 // to poll(2) on Linux. Like poll(2), poll_oneoff is level triggered. It 15 // accepts one or more subscriptions to FD read or write events. 16 // 17 // Major differences to poll(2): 18 // - the events are not written to the input entries (like pollfd.revents), and 19 // instead are appended to a separate events buffer. poll_oneoff writes zero 20 // or more events to the buffer (at most one per input subscription) and 21 // returns the number of events written. Although the index of the 22 // subscriptions might not match the index of the associated event in the 23 // events buffer, both the subscription and event structs contain a userdata 24 // field and when a subscription yields an event the userdata fields will 25 // match. 26 // - there's no explicit timeout parameter, although a time limit can be added 27 // by using "clock" subscriptions. 28 // - each FD subscription can either be for a read or a write, but not both. 29 // This is in contrast to poll(2) which accepts a mask with POLLIN and 30 // POLLOUT bits, allowing for a subscription to either, neither, or both 31 // reads and writes. 32 // 33 // Since poll_oneoff is similar to poll(2), the implementation here was derived 34 // from netpoll_aix.go. 35 36 const _EINTR = 27 37 38 var ( 39 evts []event 40 subs []subscription 41 pds []*pollDesc 42 mtx mutex 43 ) 44 45 func netpollinit() { 46 // Unlike poll(2), WASI's poll_oneoff doesn't accept a timeout directly. To 47 // prevent it from blocking indefinitely, a clock subscription with a 48 // timeout field needs to be submitted. Reserve a slot here for the clock 49 // subscription, and set fields that won't change between poll_oneoff calls. 50 51 subs = make([]subscription, 1, 128) 52 evts = make([]event, 0, 128) 53 pds = make([]*pollDesc, 0, 128) 54 55 timeout := &subs[0] 56 eventtype := timeout.u.eventtype() 57 *eventtype = eventtypeClock 58 clock := timeout.u.subscriptionClock() 59 clock.id = clockMonotonic 60 clock.precision = 1e3 61 } 62 63 func netpollIsPollDescriptor(fd uintptr) bool { 64 return false 65 } 66 67 func netpollopen(fd uintptr, pd *pollDesc) int32 { 68 lock(&mtx) 69 70 // We don't worry about pd.fdseq here, 71 // as mtx protects us from stale pollDescs. 72 73 pds = append(pds, pd) 74 75 // The 32-bit pd.user field holds the index of the read subscription in the 76 // upper 16 bits, and index of the write subscription in the lower bits. 77 // A disarmed=^uint16(0) sentinel is used to represent no subscription. 78 // There is thus a maximum of 65535 total subscriptions. 79 pd.user = uint32(disarmed)<<16 | uint32(disarmed) 80 81 unlock(&mtx) 82 return 0 83 } 84 85 const disarmed = 0xFFFF 86 87 func netpollarm(pd *pollDesc, mode int) { 88 lock(&mtx) 89 90 var s subscription 91 92 s.userdata = userdata(uintptr(unsafe.Pointer(pd))) 93 94 fdReadwrite := s.u.subscriptionFdReadwrite() 95 fdReadwrite.fd = int32(pd.fd) 96 97 ridx := int(pd.user >> 16) 98 widx := int(pd.user & 0xFFFF) 99 100 if (mode == 'r' && ridx != disarmed) || (mode == 'w' && widx != disarmed) { 101 unlock(&mtx) 102 return 103 } 104 105 eventtype := s.u.eventtype() 106 switch mode { 107 case 'r': 108 *eventtype = eventtypeFdRead 109 ridx = len(subs) 110 case 'w': 111 *eventtype = eventtypeFdWrite 112 widx = len(subs) 113 } 114 115 if len(subs) == disarmed { 116 throw("overflow") 117 } 118 119 pd.user = uint32(ridx)<<16 | uint32(widx) 120 121 subs = append(subs, s) 122 evts = append(evts, event{}) 123 124 unlock(&mtx) 125 } 126 127 func netpolldisarm(pd *pollDesc, mode int32) { 128 switch mode { 129 case 'r': 130 removesub(int(pd.user >> 16)) 131 case 'w': 132 removesub(int(pd.user & 0xFFFF)) 133 case 'r' + 'w': 134 removesub(int(pd.user >> 16)) 135 removesub(int(pd.user & 0xFFFF)) 136 } 137 } 138 139 func removesub(i int) { 140 if i == disarmed { 141 return 142 } 143 j := len(subs) - 1 144 145 pdi := (*pollDesc)(unsafe.Pointer(uintptr(subs[i].userdata))) 146 pdj := (*pollDesc)(unsafe.Pointer(uintptr(subs[j].userdata))) 147 148 swapsub(pdi, i, disarmed) 149 swapsub(pdj, j, i) 150 151 subs = subs[:j] 152 } 153 154 func swapsub(pd *pollDesc, from, to int) { 155 if from == to { 156 return 157 } 158 ridx := int(pd.user >> 16) 159 widx := int(pd.user & 0xFFFF) 160 if ridx == from { 161 ridx = to 162 } else if widx == from { 163 widx = to 164 } 165 pd.user = uint32(ridx)<<16 | uint32(widx) 166 if to != disarmed { 167 subs[to], subs[from] = subs[from], subs[to] 168 } 169 } 170 171 func netpollclose(fd uintptr) int32 { 172 lock(&mtx) 173 for i := 0; i < len(pds); i++ { 174 if pds[i].fd == fd { 175 netpolldisarm(pds[i], 'r'+'w') 176 pds[i] = pds[len(pds)-1] 177 pds = pds[:len(pds)-1] 178 break 179 } 180 } 181 unlock(&mtx) 182 return 0 183 } 184 185 func netpollBreak() {} 186 187 func netpoll(delay int64) (gList, int32) { 188 lock(&mtx) 189 190 // If delay >= 0, we include a subscription of type Clock that we use as 191 // a timeout. If delay < 0, we omit the subscription and allow poll_oneoff 192 // to block indefinitely. 193 pollsubs := subs 194 if delay >= 0 { 195 timeout := &subs[0] 196 clock := timeout.u.subscriptionClock() 197 clock.timeout = uint64(delay) 198 } else { 199 pollsubs = subs[1:] 200 } 201 202 if len(pollsubs) == 0 { 203 unlock(&mtx) 204 return gList{}, 0 205 } 206 207 evts = evts[:len(pollsubs)] 208 for i := range evts { 209 evts[i] = event{} 210 } 211 212 retry: 213 var nevents size 214 errno := poll_oneoff(unsafe.Pointer(&pollsubs[0]), unsafe.Pointer(&evts[0]), uint32(len(pollsubs)), unsafe.Pointer(&nevents)) 215 if errno != 0 { 216 if errno != _EINTR { 217 println("errno=", errno, " len(pollsubs)=", len(pollsubs)) 218 throw("poll_oneoff failed") 219 } 220 // If a timed sleep was interrupted, just return to 221 // recalculate how long we should sleep now. 222 if delay > 0 { 223 unlock(&mtx) 224 return gList{}, 0 225 } 226 goto retry 227 } 228 229 var toRun gList 230 delta := int32(0) 231 for i := 0; i < int(nevents); i++ { 232 e := &evts[i] 233 if e.typ == eventtypeClock { 234 continue 235 } 236 237 hangup := e.fdReadwrite.flags&fdReadwriteHangup != 0 238 var mode int32 239 if e.typ == eventtypeFdRead || e.error != 0 || hangup { 240 mode += 'r' 241 } 242 if e.typ == eventtypeFdWrite || e.error != 0 || hangup { 243 mode += 'w' 244 } 245 if mode != 0 { 246 pd := (*pollDesc)(unsafe.Pointer(uintptr(e.userdata))) 247 netpolldisarm(pd, mode) 248 pd.setEventErr(e.error != 0, 0) 249 delta += netpollready(&toRun, pd, mode) 250 } 251 } 252 253 unlock(&mtx) 254 return toRun, delta 255 }