1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package syscalls
import (
"syscall"
"time"
"gvisor.googlesource.com/gvisor/pkg/sentry/fs"
"gvisor.googlesource.com/gvisor/pkg/sentry/kernel"
"gvisor.googlesource.com/gvisor/pkg/sentry/kernel/kdefs"
"gvisor.googlesource.com/gvisor/pkg/waiter"
)
// PollFD describes a pollable FD.
type PollFD struct {
FD kdefs.FD
Events waiter.EventMask
REvents waiter.EventMask
}
// pollState tracks the associated file descriptor and waiter of a PollFD.
type pollState struct {
file *fs.File
waiter waiter.Entry
}
// initReadiness gets the current ready mask for the file represented by the FD
// stored in pfd.FD. If a channel is passed in, the waiter entry in "state" is
// used to register with the file for event notifications, and a reference to
// the file is stored in "state".
func (pfd *PollFD) initReadiness(t *kernel.Task, state *pollState, ch chan struct{}) {
if pfd.FD < 0 {
pfd.REvents = 0
return
}
file := t.FDMap().GetFile(pfd.FD)
if file == nil {
pfd.REvents = waiter.EventNVal
return
}
if ch == nil {
defer file.DecRef()
} else {
state.file = file
state.waiter, _ = waiter.NewChannelEntry(ch)
file.EventRegister(&state.waiter, pfd.Events)
}
pfd.REvents = file.Readiness(pfd.Events) & pfd.Events
}
// releaseState releases all the pollState in "state".
func releaseState(state []pollState) {
for i := range state {
if state[i].file != nil {
state[i].file.EventUnregister(&state[i].waiter)
state[i].file.DecRef()
}
}
}
// Poll polls the PollFDs in "pfd" with a bounded time specified in "timeout"
// when "timeout" is greater than zero.
//
// Poll returns the remaining timeout, which is always 0 on a timeout; and 0 or
// positive if interrupted by a signal.
func Poll(t *kernel.Task, pfd []PollFD, timeout time.Duration) (time.Duration, uintptr, error) {
var ch chan struct{}
if timeout != 0 {
ch = make(chan struct{}, 1)
}
// Register for event notification in the files involved if we may
// block (timeout not zero). Once we find a file that has a non-zero
// result, we stop registering for events but still go through all files
// to get their ready masks.
state := make([]pollState, len(pfd))
defer releaseState(state)
n := uintptr(0)
for i := range pfd {
pfd[i].initReadiness(t, &state[i], ch)
if pfd[i].REvents != 0 {
n++
ch = nil
}
}
if timeout == 0 {
return timeout, n, nil
}
forever := timeout < 0
for n == 0 {
var err error
// Wait for a notification.
timeout, err = t.BlockWithTimeout(ch, !forever, timeout)
if err != nil {
if err == syscall.ETIMEDOUT {
err = nil
}
return timeout, 0, err
}
// We got notified, count how many files are ready. If none,
// then this was a spurious notification, and we just go back
// to sleep with the remaining timeout.
for i := range state {
if state[i].file == nil {
continue
}
ready := state[i].file.Readiness(pfd[i].Events) & pfd[i].Events
if ready != 0 {
pfd[i].REvents = ready
n++
}
}
}
return timeout, n, nil
}
|