github.com/tetratelabs/wazero@v1.2.1/imports/wasi_snapshot_preview1/poll_test.go (about) 1 package wasi_snapshot_preview1_test 2 3 import ( 4 "io/fs" 5 "strings" 6 "syscall" 7 "testing" 8 "time" 9 10 "github.com/tetratelabs/wazero" 11 "github.com/tetratelabs/wazero/api" 12 "github.com/tetratelabs/wazero/internal/fsapi" 13 "github.com/tetratelabs/wazero/internal/sys" 14 "github.com/tetratelabs/wazero/internal/testing/require" 15 "github.com/tetratelabs/wazero/internal/wasip1" 16 "github.com/tetratelabs/wazero/internal/wasm" 17 ) 18 19 func Test_pollOneoff(t *testing.T) { 20 mod, r, log := requireProxyModule(t, wazero.NewModuleConfig()) 21 defer r.Close(testCtx) 22 23 mem := []byte{ 24 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 25 wasip1.EventTypeClock, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // event type and padding 26 wasip1.ClockIDMonotonic, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // clockID 27 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // timeout (ns) 28 0x01, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // precision (ns) 29 0x00, 0x00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // flags (relative) 30 '?', // stopped after encoding 31 } 32 33 expectedMem := []byte{ 34 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 35 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 36 wasip1.EventTypeClock, 0x0, 0x0, 0x0, // 4 bytes for type enum 37 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 38 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, '?', // stopped after encoding 39 } 40 41 in := uint32(0) // past in 42 out := uint32(128) // past in 43 nsubscriptions := uint32(1) 44 resultNevents := uint32(512) // past out 45 46 maskMemory(t, mod, 1024) 47 mod.Memory().Write(in, mem) 48 49 requireErrnoResult(t, wasip1.ErrnoSuccess, mod, wasip1.PollOneoffName, uint64(in), uint64(out), uint64(nsubscriptions), 50 uint64(resultNevents)) 51 require.Equal(t, ` 52 ==> wasi_snapshot_preview1.poll_oneoff(in=0,out=128,nsubscriptions=1) 53 <== (nevents=1,errno=ESUCCESS) 54 `, "\n"+log.String()) 55 56 outMem, ok := mod.Memory().Read(out, uint32(len(expectedMem))) 57 require.True(t, ok) 58 require.Equal(t, expectedMem, outMem) 59 60 nevents, ok := mod.Memory().ReadUint32Le(resultNevents) 61 require.True(t, ok) 62 require.Equal(t, nsubscriptions, nevents) 63 } 64 65 func Test_pollOneoff_Errors(t *testing.T) { 66 mod, r, log := requireProxyModule(t, wazero.NewModuleConfig()) 67 defer r.Close(testCtx) 68 69 tests := []struct { 70 name string 71 in, out, nsubscriptions, resultNevents uint32 72 mem []byte // at offset in 73 expectedErrno wasip1.Errno 74 expectedMem []byte // at offset out 75 expectedLog string 76 }{ 77 { 78 name: "in out of range", 79 in: wasm.MemoryPageSize, 80 nsubscriptions: 1, 81 out: 128, // past in 82 resultNevents: 512, // past out 83 expectedErrno: wasip1.ErrnoFault, 84 expectedLog: ` 85 ==> wasi_snapshot_preview1.poll_oneoff(in=65536,out=128,nsubscriptions=1) 86 <== (nevents=,errno=EFAULT) 87 `, 88 }, 89 { 90 name: "out out of range", 91 out: wasm.MemoryPageSize, 92 resultNevents: 512, // past out 93 nsubscriptions: 1, 94 expectedErrno: wasip1.ErrnoFault, 95 expectedLog: ` 96 ==> wasi_snapshot_preview1.poll_oneoff(in=0,out=65536,nsubscriptions=1) 97 <== (nevents=,errno=EFAULT) 98 `, 99 }, 100 { 101 name: "resultNevents out of range", 102 resultNevents: wasm.MemoryPageSize, 103 nsubscriptions: 1, 104 expectedErrno: wasip1.ErrnoFault, 105 expectedLog: ` 106 ==> wasi_snapshot_preview1.poll_oneoff(in=0,out=0,nsubscriptions=1) 107 <== (nevents=,errno=EFAULT) 108 `, 109 }, 110 { 111 name: "nsubscriptions zero", 112 out: 128, // past in 113 resultNevents: 512, // past out 114 expectedErrno: wasip1.ErrnoInval, 115 expectedLog: ` 116 ==> wasi_snapshot_preview1.poll_oneoff(in=0,out=128,nsubscriptions=0) 117 <== (nevents=,errno=EINVAL) 118 `, 119 }, 120 } 121 122 for _, tt := range tests { 123 tc := tt 124 t.Run(tc.name, func(t *testing.T) { 125 defer log.Reset() 126 127 maskMemory(t, mod, 1024) 128 129 if tc.mem != nil { 130 mod.Memory().Write(tc.in, tc.mem) 131 } 132 133 requireErrnoResult(t, tc.expectedErrno, mod, wasip1.PollOneoffName, uint64(tc.in), uint64(tc.out), 134 uint64(tc.nsubscriptions), uint64(tc.resultNevents)) 135 require.Equal(t, tc.expectedLog, "\n"+log.String()) 136 137 out, ok := mod.Memory().Read(tc.out, uint32(len(tc.expectedMem))) 138 require.True(t, ok) 139 require.Equal(t, tc.expectedMem, out) 140 141 // Events should be written on success regardless of nested failure. 142 if tc.expectedErrno == wasip1.ErrnoSuccess { 143 nevents, ok := mod.Memory().ReadUint32Le(tc.resultNevents) 144 require.True(t, ok) 145 require.Equal(t, uint32(1), nevents) 146 _ = nevents 147 } 148 }) 149 } 150 } 151 152 func Test_pollOneoff_Stdin(t *testing.T) { 153 tests := []struct { 154 name string 155 in, out, nsubscriptions, resultNevents uint32 156 mem []byte // at offset in 157 stdin fsapi.File 158 expectedErrno wasip1.Errno 159 expectedMem []byte // at offset out 160 expectedLog string 161 expectedNevents uint32 162 }{ 163 { 164 name: "Read without explicit timeout (no tty)", 165 nsubscriptions: 1, 166 expectedNevents: 1, 167 stdin: &sys.StdinFile{Reader: strings.NewReader("test")}, 168 mem: fdReadSub, 169 expectedErrno: wasip1.ErrnoSuccess, 170 out: 128, // past in 171 resultNevents: 512, // past out 172 expectedMem: []byte{ 173 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 174 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 175 wasip1.EventTypeFdRead, 0x0, 0x0, 0x0, // 4 bytes for type enum 176 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 177 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 178 0x0, 0x0, 179 180 '?', // stopped after encoding 181 }, 182 expectedLog: ` 183 ==> wasi_snapshot_preview1.poll_oneoff(in=0,out=128,nsubscriptions=1) 184 <== (nevents=1,errno=ESUCCESS) 185 `, 186 }, 187 { 188 name: "20ms timeout, fdread on tty (buffer ready): both events are written", 189 nsubscriptions: 2, 190 expectedNevents: 2, 191 stdin: &ttyStdinFile{StdinFile: sys.StdinFile{Reader: strings.NewReader("test")}}, 192 mem: concat( 193 clockNsSub(20*1000*1000), 194 fdReadSub, 195 singleton('?'), 196 ), 197 expectedErrno: wasip1.ErrnoSuccess, 198 out: 128, // past in 199 resultNevents: 512, // past out 200 expectedMem: []byte{ 201 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 202 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 203 wasip1.EventTypeClock, 0x0, 0x0, 0x0, // 4 bytes for type enum 204 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // pad to 32 205 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 206 0x0, 0x0, 207 208 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 209 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 210 wasip1.EventTypeFdRead, 0x0, 0x0, 0x0, // 4 bytes for type enum 211 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // pad to 32 212 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 213 0x0, 0x0, 214 215 '?', // stopped after encoding 216 }, 217 expectedLog: ` 218 ==> wasi_snapshot_preview1.poll_oneoff(in=0,out=128,nsubscriptions=2) 219 <== (nevents=2,errno=ESUCCESS) 220 `, 221 }, 222 { 223 name: "0ns timeout, fdread on tty (buffer ready): both are written", 224 nsubscriptions: 2, 225 expectedNevents: 2, 226 stdin: &ttyStdinFile{StdinFile: sys.StdinFile{Reader: strings.NewReader("test")}}, 227 mem: concat( 228 clockNsSub(20*1000*1000), 229 fdReadSub, 230 singleton('?'), 231 ), 232 expectedErrno: wasip1.ErrnoSuccess, 233 out: 128, // past in 234 resultNevents: 512, // past out 235 expectedMem: []byte{ 236 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 237 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 238 wasip1.EventTypeClock, 0x0, 0x0, 0x0, // 4 bytes for type enum 239 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // pad to 32 240 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 241 0x0, 0x0, 242 243 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 244 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 245 wasip1.EventTypeFdRead, 0x0, 0x0, 0x0, // 4 bytes for type enum 246 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // pad to 32 247 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 248 0x0, 0x0, 249 250 '?', // stopped after encoding 251 }, 252 expectedLog: ` 253 ==> wasi_snapshot_preview1.poll_oneoff(in=0,out=128,nsubscriptions=2) 254 <== (nevents=2,errno=ESUCCESS) 255 `, 256 }, 257 { 258 name: "0ns timeout, fdread on regular file: both events are written", 259 nsubscriptions: 2, 260 expectedNevents: 2, 261 stdin: &sys.StdinFile{Reader: strings.NewReader("test")}, 262 mem: concat( 263 clockNsSub(20*1000*1000), 264 fdReadSub, 265 singleton('?'), 266 ), 267 expectedErrno: wasip1.ErrnoSuccess, 268 out: 128, // past in 269 resultNevents: 512, // past out 270 expectedMem: []byte{ 271 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 272 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 273 wasip1.EventTypeClock, 0x0, 0x0, 0x0, // 4 bytes for type enum 274 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // pad to 32 275 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 276 0x0, 0x0, 277 278 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 279 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 280 wasip1.EventTypeFdRead, 0x0, 0x0, 0x0, // 4 bytes for type enum 281 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // pad to 32 282 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 283 0x0, 0x0, 284 285 '?', // stopped after encoding 286 }, 287 expectedLog: ` 288 ==> wasi_snapshot_preview1.poll_oneoff(in=0,out=128,nsubscriptions=2) 289 <== (nevents=2,errno=ESUCCESS) 290 `, 291 }, 292 { 293 name: "1ns timeout, fdread on regular file: both events are written", 294 nsubscriptions: 2, 295 expectedNevents: 2, 296 stdin: &sys.StdinFile{Reader: strings.NewReader("test")}, 297 mem: concat( 298 clockNsSub(20*1000*1000), 299 fdReadSub, 300 singleton('?'), 301 ), 302 expectedErrno: wasip1.ErrnoSuccess, 303 out: 128, // past in 304 resultNevents: 512, // past out 305 expectedMem: []byte{ 306 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 307 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 308 wasip1.EventTypeClock, 0x0, 0x0, 0x0, // 4 bytes for type enum 309 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // pad to 32 310 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 311 0x0, 0x0, 312 313 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 314 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 315 wasip1.EventTypeFdRead, 0x0, 0x0, 0x0, // 4 bytes for type enum 316 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // pad to 32 317 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 318 0x0, 0x0, 319 320 '?', // stopped after encoding 321 }, 322 expectedLog: ` 323 ==> wasi_snapshot_preview1.poll_oneoff(in=0,out=128,nsubscriptions=2) 324 <== (nevents=2,errno=ESUCCESS) 325 `, 326 }, 327 { 328 name: "20ms timeout, fdread on blocked tty: only clock event is written", 329 nsubscriptions: 2, 330 expectedNevents: 1, 331 stdin: &neverReadyTtyStdinFile{StdinFile: sys.StdinFile{Reader: newBlockingReader(t)}}, 332 mem: concat( 333 clockNsSub(20*1000*1000), 334 fdReadSub, 335 singleton('?'), 336 ), 337 338 expectedErrno: wasip1.ErrnoSuccess, 339 out: 128, // past in 340 resultNevents: 512, // past out 341 expectedMem: []byte{ 342 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 343 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 344 wasip1.EventTypeClock, 0x0, 0x0, 0x0, // 4 bytes for type enum 345 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // pad to 32 346 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 347 0x0, 0x0, 348 349 // 32 empty bytes 350 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 351 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 352 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 353 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 354 355 '?', // stopped after encoding 356 }, 357 expectedLog: ` 358 ==> wasi_snapshot_preview1.poll_oneoff(in=0,out=128,nsubscriptions=2) 359 <== (nevents=1,errno=ESUCCESS) 360 `, 361 }, 362 } 363 364 for _, tt := range tests { 365 tc := tt 366 t.Run(tc.name, func(t *testing.T) { 367 mod, r, log := requireProxyModule(t, wazero.NewModuleConfig()) 368 defer r.Close(testCtx) 369 defer log.Reset() 370 371 setStdin(t, mod, tc.stdin) 372 373 maskMemory(t, mod, 1024) 374 if tc.mem != nil { 375 mod.Memory().Write(tc.in, tc.mem) 376 } 377 378 requireErrnoResult(t, tc.expectedErrno, mod, wasip1.PollOneoffName, uint64(tc.in), uint64(tc.out), 379 uint64(tc.nsubscriptions), uint64(tc.resultNevents)) 380 require.Equal(t, tc.expectedLog, "\n"+log.String()) 381 382 out, ok := mod.Memory().Read(tc.out, uint32(len(tc.expectedMem))) 383 require.True(t, ok) 384 require.Equal(t, tc.expectedMem, out) 385 386 // Events should be written on success regardless of nested failure. 387 if tc.expectedErrno == wasip1.ErrnoSuccess { 388 nevents, ok := mod.Memory().ReadUint32Le(tc.resultNevents) 389 require.True(t, ok) 390 require.Equal(t, tc.expectedNevents, nevents) 391 _ = nevents 392 } 393 }) 394 } 395 } 396 397 func setStdin(t *testing.T, mod api.Module, stdin fsapi.File) { 398 fsc := mod.(*wasm.ModuleInstance).Sys.FS() 399 f, ok := fsc.LookupFile(sys.FdStdin) 400 require.True(t, ok) 401 f.File = stdin 402 } 403 404 func Test_pollOneoff_Zero(t *testing.T) { 405 poller := &pollStdinFile{StdinFile: sys.StdinFile{Reader: strings.NewReader("test")}, ready: true} 406 407 mod, r, log := requireProxyModule(t, wazero.NewModuleConfig()) 408 defer r.Close(testCtx) 409 defer log.Reset() 410 411 setStdin(t, mod, poller) 412 413 maskMemory(t, mod, 1024) 414 415 out := uint32(128) 416 nsubscriptions := 2 417 resultNevents := uint32(512) 418 419 mod.Memory().Write(0, 420 concat( 421 clockNsSub(20*1000*1000), 422 fdReadSub, 423 singleton('?'), 424 ), 425 ) 426 427 expectedMem := []byte{ 428 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 429 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 430 wasip1.EventTypeClock, 0x0, 0x0, 0x0, // 4 bytes for type enum 431 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 432 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 433 434 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 435 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 436 wasip1.EventTypeFdRead, 0x0, 0x0, 0x0, // 4 bytes for type enum 437 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // pad to 32 438 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 439 0x0, 0x0, 440 441 '?', // stopped after encoding 442 } 443 444 requireErrnoResult(t, wasip1.ErrnoSuccess, mod, wasip1.PollOneoffName, uint64(0), uint64(out), 445 uint64(nsubscriptions), uint64(resultNevents)) 446 447 outMem, ok := mod.Memory().Read(out, uint32(len(expectedMem))) 448 require.True(t, ok) 449 require.Equal(t, expectedMem, outMem) 450 451 // Events should be written on success regardless of nested failure. 452 nevents, ok := mod.Memory().ReadUint32Le(resultNevents) 453 require.True(t, ok) 454 require.Equal(t, uint32(2), nevents) 455 456 // second run: simulate no more data on the fd 457 poller.ready = false 458 459 mod.Memory().Write(0, 460 concat( 461 clockNsSub(20*1000*1000), 462 fdReadSub, 463 singleton('?'), 464 ), 465 ) 466 467 expectedMem = []byte{ 468 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 469 byte(wasip1.ErrnoSuccess), 0x0, // errno is 16 bit 470 wasip1.EventTypeClock, 0x0, 0x0, 0x0, // 4 bytes for type enum 471 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 472 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 473 474 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 475 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 476 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 477 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 478 479 '?', // stopped after encoding 480 } 481 482 requireErrnoResult(t, wasip1.ErrnoSuccess, mod, wasip1.PollOneoffName, uint64(0), uint64(out), 483 uint64(nsubscriptions), uint64(resultNevents)) 484 485 outMem, ok = mod.Memory().Read(out, uint32(len(expectedMem))) 486 require.True(t, ok) 487 require.Equal(t, expectedMem, outMem) 488 489 nevents, ok = mod.Memory().ReadUint32Le(resultNevents) 490 require.True(t, ok) 491 require.Equal(t, uint32(1), nevents) 492 } 493 494 func singleton(b byte) []byte { 495 return []byte{b} 496 } 497 498 func concat(bytes ...[]byte) []byte { 499 var res []byte 500 for i := range bytes { 501 res = append(res, bytes[i]...) 502 } 503 return res 504 } 505 506 // subscription for a given timeout in ns 507 func clockNsSub(ns uint64) []byte { 508 return []byte{ 509 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 510 wasip1.EventTypeClock, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // event type and padding 511 wasip1.ClockIDMonotonic, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 512 byte(ns), byte(ns >> 8), byte(ns >> 16), byte(ns >> 24), 513 byte(ns >> 32), byte(ns >> 40), byte(ns >> 48), byte(ns >> 56), // timeout (ns) 514 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // precision (ns) 515 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // flags 516 } 517 } 518 519 // subscription for an EventTypeFdRead on a given fd 520 func fdReadSubFd(fd byte) []byte { 521 return []byte{ 522 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // userdata 523 wasip1.EventTypeFdRead, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 524 fd, 0x0, 0x0, 0x0, // valid readable FD 525 } 526 } 527 528 // subscription for an EventTypeFdRead on stdin 529 var fdReadSub = fdReadSubFd(byte(sys.FdStdin)) 530 531 // ttyStat returns fs.ModeCharDevice as an approximation for isatty. 532 // See go-isatty for a more specific approach: 533 // https://github.com/mattn/go-isatty/blob/v0.0.18/isatty_tcgets.go#LL11C1-L12C1 534 type ttyStat struct{} 535 536 // Stat implements the same method as documented on internalapi.File 537 func (ttyStat) Stat() (fsapi.Stat_t, syscall.Errno) { 538 return fsapi.Stat_t{ 539 Mode: fs.ModeDevice | fs.ModeCharDevice, 540 Nlink: 1, 541 }, 0 542 } 543 544 type ttyStdinFile struct { 545 sys.StdinFile 546 ttyStat 547 } 548 549 type neverReadyTtyStdinFile struct { 550 sys.StdinFile 551 ttyStat 552 } 553 554 // PollRead implements the same method as documented on internalapi.File 555 func (neverReadyTtyStdinFile) PollRead(timeout *time.Duration) (ready bool, errno syscall.Errno) { 556 time.Sleep(*timeout) 557 return false, 0 558 } 559 560 type pollStdinFile struct { 561 sys.StdinFile 562 ttyStat 563 ready bool 564 } 565 566 // PollRead implements the same method as documented on internalapi.File 567 func (p *pollStdinFile) PollRead(*time.Duration) (ready bool, errno syscall.Errno) { 568 return p.ready, 0 569 }