github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/kernel/task_block.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package kernel 16 17 import ( 18 "runtime" 19 "runtime/trace" 20 "time" 21 22 "github.com/SagerNet/gvisor/pkg/errors/linuxerr" 23 ktime "github.com/SagerNet/gvisor/pkg/sentry/kernel/time" 24 "github.com/SagerNet/gvisor/pkg/sync" 25 "github.com/SagerNet/gvisor/pkg/syserror" 26 ) 27 28 // BlockWithTimeout blocks t until an event is received from C, the application 29 // monotonic clock indicates that timeout has elapsed (only if haveTimeout is true), 30 // or t is interrupted. It returns: 31 // 32 // - The remaining timeout, which is guaranteed to be 0 if the timeout expired, 33 // and is unspecified if haveTimeout is false. 34 // 35 // - An error which is nil if an event is received from C, ETIMEDOUT if the timeout 36 // expired, and syserror.ErrInterrupted if t is interrupted. 37 // 38 // Preconditions: The caller must be running on the task goroutine. 39 func (t *Task) BlockWithTimeout(C chan struct{}, haveTimeout bool, timeout time.Duration) (time.Duration, error) { 40 if !haveTimeout { 41 return timeout, t.block(C, nil) 42 } 43 44 start := t.Kernel().MonotonicClock().Now() 45 deadline := start.Add(timeout) 46 err := t.BlockWithDeadline(C, true, deadline) 47 48 // Timeout, explicitly return a remaining duration of 0. 49 if linuxerr.Equals(linuxerr.ETIMEDOUT, err) { 50 return 0, err 51 } 52 53 // Compute the remaining timeout. Note that even if block() above didn't 54 // return due to a timeout, we may have used up any of the remaining time 55 // since then. We cap the remaining timeout to 0 to make it easier to 56 // directly use the returned duration. 57 end := t.Kernel().MonotonicClock().Now() 58 remainingTimeout := timeout - end.Sub(start) 59 if remainingTimeout < 0 { 60 remainingTimeout = 0 61 } 62 63 return remainingTimeout, err 64 } 65 66 // BlockWithDeadline blocks t until an event is received from C, the 67 // application monotonic clock indicates a time of deadline (only if 68 // haveDeadline is true), or t is interrupted. It returns nil if an event is 69 // received from C, ETIMEDOUT if the deadline expired, and 70 // syserror.ErrInterrupted if t is interrupted. 71 // 72 // Preconditions: The caller must be running on the task goroutine. 73 func (t *Task) BlockWithDeadline(C <-chan struct{}, haveDeadline bool, deadline ktime.Time) error { 74 if !haveDeadline { 75 return t.block(C, nil) 76 } 77 78 // Start the timeout timer. 79 t.blockingTimer.Swap(ktime.Setting{ 80 Enabled: true, 81 Next: deadline, 82 }) 83 84 err := t.block(C, t.blockingTimerChan) 85 86 // Stop the timeout timer and drain the channel. 87 t.blockingTimer.Swap(ktime.Setting{}) 88 select { 89 case <-t.blockingTimerChan: 90 default: 91 } 92 93 return err 94 } 95 96 // BlockWithTimer blocks t until an event is received from C or tchan, or t is 97 // interrupted. It returns nil if an event is received from C, ETIMEDOUT if an 98 // event is received from tchan, and syserror.ErrInterrupted if t is 99 // interrupted. 100 // 101 // Most clients should use BlockWithDeadline or BlockWithTimeout instead. 102 // 103 // Preconditions: The caller must be running on the task goroutine. 104 func (t *Task) BlockWithTimer(C <-chan struct{}, tchan <-chan struct{}) error { 105 return t.block(C, tchan) 106 } 107 108 // Block blocks t until an event is received from C or t is interrupted. It 109 // returns nil if an event is received from C and syserror.ErrInterrupted if t 110 // is interrupted. 111 // 112 // Preconditions: The caller must be running on the task goroutine. 113 func (t *Task) Block(C <-chan struct{}) error { 114 return t.block(C, nil) 115 } 116 117 // block blocks a task on one of many events. 118 // N.B. defer is too expensive to be used here. 119 // 120 // Preconditions: The caller must be running on the task goroutine. 121 func (t *Task) block(C <-chan struct{}, timerChan <-chan struct{}) error { 122 // This function is very hot; skip this check outside of +race builds. 123 if sync.RaceEnabled { 124 t.assertTaskGoroutine() 125 } 126 127 // Fast path if the request is already done. 128 select { 129 case <-C: 130 return nil 131 default: 132 } 133 134 // Deactive our address space, we don't need it. 135 interrupt := t.SleepStart() 136 137 // If the request is not completed, but the timer has already expired, 138 // then ensure that we run through a scheduler cycle. This is because 139 // we may see applications relying on timer slack to yield the thread. 140 // For example, they may attempt to sleep for some number of nanoseconds, 141 // and expect that this will actually yield the CPU and sleep for at 142 // least microseconds, e.g.: 143 // https://github.com/LMAX-Exchange/disruptor/commit/6ca210f2bcd23f703c479804d583718e16f43c07 144 if len(timerChan) > 0 { 145 runtime.Gosched() 146 } 147 148 region := trace.StartRegion(t.traceContext, blockRegion) 149 select { 150 case <-C: 151 region.End() 152 t.SleepFinish(true) 153 // Woken by event. 154 return nil 155 156 case <-interrupt: 157 region.End() 158 t.SleepFinish(false) 159 // Return the indicated error on interrupt. 160 return syserror.ErrInterrupted 161 162 case <-timerChan: 163 region.End() 164 t.SleepFinish(true) 165 // We've timed out. 166 return linuxerr.ETIMEDOUT 167 } 168 } 169 170 // SleepStart implements context.ChannelSleeper.SleepStart. 171 func (t *Task) SleepStart() <-chan struct{} { 172 t.assertTaskGoroutine() 173 t.Deactivate() 174 t.accountTaskGoroutineEnter(TaskGoroutineBlockedInterruptible) 175 return t.interruptChan 176 } 177 178 // SleepFinish implements context.ChannelSleeper.SleepFinish. 179 func (t *Task) SleepFinish(success bool) { 180 if !success { 181 // Our caller received from t.interruptChan; we need to re-send to it 182 // to ensure that t.interrupted() is still true. 183 t.interruptSelf() 184 } 185 t.accountTaskGoroutineLeave(TaskGoroutineBlockedInterruptible) 186 t.Activate() 187 } 188 189 // Interrupted implements context.ChannelSleeper.Interrupted. 190 func (t *Task) Interrupted() bool { 191 if t.interrupted() { 192 return true 193 } 194 // Indicate that t's task goroutine is still responsive (i.e. reset the 195 // watchdog timer). 196 t.accountTaskGoroutineRunning() 197 return false 198 } 199 200 // UninterruptibleSleepStart implements context.Context.UninterruptibleSleepStart. 201 func (t *Task) UninterruptibleSleepStart(deactivate bool) { 202 t.assertTaskGoroutine() 203 if deactivate { 204 t.Deactivate() 205 } 206 t.accountTaskGoroutineEnter(TaskGoroutineBlockedUninterruptible) 207 } 208 209 // UninterruptibleSleepFinish implements context.Context.UninterruptibleSleepFinish. 210 func (t *Task) UninterruptibleSleepFinish(activate bool) { 211 t.accountTaskGoroutineLeave(TaskGoroutineBlockedUninterruptible) 212 if activate { 213 t.Activate() 214 } 215 } 216 217 // interrupted returns true if interrupt or interruptSelf has been called at 218 // least once since the last call to unsetInterrupted. 219 func (t *Task) interrupted() bool { 220 return len(t.interruptChan) != 0 221 } 222 223 // unsetInterrupted causes interrupted to return false until the next call to 224 // interrupt or interruptSelf. 225 func (t *Task) unsetInterrupted() { 226 select { 227 case <-t.interruptChan: 228 default: 229 } 230 } 231 232 // interrupt unblocks the task and interrupts it if it's currently running in 233 // userspace. 234 func (t *Task) interrupt() { 235 t.interruptSelf() 236 t.p.Interrupt() 237 } 238 239 // interruptSelf is like Interrupt, but can only be called by the task 240 // goroutine. 241 func (t *Task) interruptSelf() { 242 select { 243 case t.interruptChan <- struct{}{}: 244 default: 245 } 246 // platform.Context.Interrupt() is unnecessary since a task goroutine 247 // calling interruptSelf() cannot also be blocked in 248 // platform.Context.Switch(). 249 }