98 lines
2.1 KiB
Go
98 lines
2.1 KiB
Go
// Copied from the go standard library.
|
|
//
|
|
// Copyright 2010 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE-BSD file.
|
|
|
|
package multiplex
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// pipeDeadline is an abstraction for handling timeouts.
|
|
type pipeDeadline struct {
|
|
mu sync.Mutex // Guards timer and cancel
|
|
timer *time.Timer
|
|
cancel chan struct{} // Must be non-nil
|
|
}
|
|
|
|
func makePipeDeadline() pipeDeadline {
|
|
return pipeDeadline{cancel: make(chan struct{})}
|
|
}
|
|
|
|
// set sets the point in time when the deadline will time out.
|
|
// A timeout event is signaled by closing the channel returned by waiter.
|
|
// Once a timeout has occurred, the deadline can be refreshed by specifying a
|
|
// t value in the future.
|
|
//
|
|
// A zero value for t prevents timeout.
|
|
func (d *pipeDeadline) set(t time.Time) {
|
|
d.mu.Lock()
|
|
defer d.mu.Unlock()
|
|
|
|
// deadline closed
|
|
if d.cancel == nil {
|
|
return
|
|
}
|
|
|
|
if d.timer != nil && !d.timer.Stop() {
|
|
<-d.cancel // Wait for the timer callback to finish and close cancel
|
|
}
|
|
d.timer = nil
|
|
|
|
// Time is zero, then there is no deadline.
|
|
closed := isClosedChan(d.cancel)
|
|
if t.IsZero() {
|
|
if closed {
|
|
d.cancel = make(chan struct{})
|
|
}
|
|
return
|
|
}
|
|
|
|
// Time in the future, setup a timer to cancel in the future.
|
|
if dur := time.Until(t); dur > 0 {
|
|
if closed {
|
|
d.cancel = make(chan struct{})
|
|
}
|
|
d.timer = time.AfterFunc(dur, func() {
|
|
close(d.cancel)
|
|
})
|
|
return
|
|
}
|
|
|
|
// Time in the past, so close immediately.
|
|
if !closed {
|
|
close(d.cancel)
|
|
}
|
|
}
|
|
|
|
// wait returns a channel that is closed when the deadline is exceeded.
|
|
func (d *pipeDeadline) wait() chan struct{} {
|
|
d.mu.Lock()
|
|
defer d.mu.Unlock()
|
|
return d.cancel
|
|
}
|
|
|
|
// close closes, the deadline. Any future calls to `set` will do nothing.
|
|
func (d *pipeDeadline) close() {
|
|
d.mu.Lock()
|
|
defer d.mu.Unlock()
|
|
|
|
if d.timer != nil && !d.timer.Stop() {
|
|
<-d.cancel // Wait for the timer callback to finish and close cancel
|
|
}
|
|
d.timer = nil
|
|
d.cancel = nil
|
|
}
|
|
|
|
func isClosedChan(c <-chan struct{}) bool {
|
|
select {
|
|
case <-c:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|