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//go:build unix || windows || wasip1
6
7package poll
8
9import (
10	"errors"
11	"sync"
12	"syscall"
13	"time"
14	_ "unsafe" // for go:linkname
15)
16
17// runtimeNano returns the current value of the runtime clock in nanoseconds.
18//
19//go:linkname runtimeNano runtime.nanotime
20func runtimeNano() int64
21
22func runtime_pollServerInit()
23func runtime_pollOpen(fd uintptr) (uintptr, int)
24func runtime_pollClose(ctx uintptr)
25func runtime_pollWait(ctx uintptr, mode int) int
26func runtime_pollWaitCanceled(ctx uintptr, mode int)
27func runtime_pollReset(ctx uintptr, mode int) int
28func runtime_pollSetDeadline(ctx uintptr, d int64, mode int)
29func runtime_pollUnblock(ctx uintptr)
30func runtime_isPollServerDescriptor(fd uintptr) bool
31
32type pollDesc struct {
33	runtimeCtx uintptr
34}
35
36var serverInit sync.Once
37
38func (pd *pollDesc) init(fd *FD) error {
39	serverInit.Do(runtime_pollServerInit)
40	ctx, errno := runtime_pollOpen(uintptr(fd.Sysfd))
41	if errno != 0 {
42		return errnoErr(syscall.Errno(errno))
43	}
44	pd.runtimeCtx = ctx
45	return nil
46}
47
48func (pd *pollDesc) close() {
49	if pd.runtimeCtx == 0 {
50		return
51	}
52	runtime_pollClose(pd.runtimeCtx)
53	pd.runtimeCtx = 0
54}
55
56// Evict evicts fd from the pending list, unblocking any I/O running on fd.
57func (pd *pollDesc) evict() {
58	if pd.runtimeCtx == 0 {
59		return
60	}
61	runtime_pollUnblock(pd.runtimeCtx)
62}
63
64func (pd *pollDesc) prepare(mode int, isFile bool) error {
65	if pd.runtimeCtx == 0 {
66		return nil
67	}
68	res := runtime_pollReset(pd.runtimeCtx, mode)
69	return convertErr(res, isFile)
70}
71
72func (pd *pollDesc) prepareRead(isFile bool) error {
73	return pd.prepare('r', isFile)
74}
75
76func (pd *pollDesc) prepareWrite(isFile bool) error {
77	return pd.prepare('w', isFile)
78}
79
80func (pd *pollDesc) wait(mode int, isFile bool) error {
81	if pd.runtimeCtx == 0 {
82		return errors.New("waiting for unsupported file type")
83	}
84	res := runtime_pollWait(pd.runtimeCtx, mode)
85	return convertErr(res, isFile)
86}
87
88func (pd *pollDesc) waitRead(isFile bool) error {
89	return pd.wait('r', isFile)
90}
91
92func (pd *pollDesc) waitWrite(isFile bool) error {
93	return pd.wait('w', isFile)
94}
95
96func (pd *pollDesc) waitCanceled(mode int) {
97	if pd.runtimeCtx == 0 {
98		return
99	}
100	runtime_pollWaitCanceled(pd.runtimeCtx, mode)
101}
102
103func (pd *pollDesc) pollable() bool {
104	return pd.runtimeCtx != 0
105}
106
107// Error values returned by runtime_pollReset and runtime_pollWait.
108// These must match the values in runtime/netpoll.go.
109const (
110	pollNoError        = 0
111	pollErrClosing     = 1
112	pollErrTimeout     = 2
113	pollErrNotPollable = 3
114)
115
116func convertErr(res int, isFile bool) error {
117	switch res {
118	case pollNoError:
119		return nil
120	case pollErrClosing:
121		return errClosing(isFile)
122	case pollErrTimeout:
123		return ErrDeadlineExceeded
124	case pollErrNotPollable:
125		return ErrNotPollable
126	}
127	println("unreachable: ", res)
128	panic("unreachable")
129}
130
131// SetDeadline sets the read and write deadlines associated with fd.
132func (fd *FD) SetDeadline(t time.Time) error {
133	return setDeadlineImpl(fd, t, 'r'+'w')
134}
135
136// SetReadDeadline sets the read deadline associated with fd.
137func (fd *FD) SetReadDeadline(t time.Time) error {
138	return setDeadlineImpl(fd, t, 'r')
139}
140
141// SetWriteDeadline sets the write deadline associated with fd.
142func (fd *FD) SetWriteDeadline(t time.Time) error {
143	return setDeadlineImpl(fd, t, 'w')
144}
145
146func setDeadlineImpl(fd *FD, t time.Time, mode int) error {
147	var d int64
148	if !t.IsZero() {
149		d = int64(time.Until(t))
150		if d == 0 {
151			d = -1 // don't confuse deadline right now with no deadline
152		}
153	}
154	if err := fd.incref(); err != nil {
155		return err
156	}
157	defer fd.decref()
158	if fd.pd.runtimeCtx == 0 {
159		return ErrNoDeadline
160	}
161	runtime_pollSetDeadline(fd.pd.runtimeCtx, d, mode)
162	return nil
163}
164
165// IsPollDescriptor reports whether fd is the descriptor being used by the poller.
166// This is only used for testing.
167//
168// IsPollDescriptor should be an internal detail,
169// but widely used packages access it using linkname.
170// Notable members of the hall of shame include:
171//   - github.com/opencontainers/runc
172//
173// Do not remove or change the type signature.
174// See go.dev/issue/67401.
175//
176//go:linkname IsPollDescriptor
177func IsPollDescriptor(fd uintptr) bool {
178	return runtime_isPollServerDescriptor(fd)
179}
180