github.com/kelleygo/clashcore@v1.0.2/common/net/deadline/pipe.go (about)

     1  // Copyright 2010 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  package deadline
     6  
     7  import (
     8  	"sync"
     9  	"time"
    10  )
    11  
    12  // pipeDeadline is an abstraction for handling timeouts.
    13  type pipeDeadline struct {
    14  	mu     sync.Mutex // Guards timer and cancel
    15  	timer  *time.Timer
    16  	cancel chan struct{} // Must be non-nil
    17  }
    18  
    19  func makePipeDeadline() pipeDeadline {
    20  	return pipeDeadline{cancel: make(chan struct{})}
    21  }
    22  
    23  // set sets the point in time when the deadline will time out.
    24  // A timeout event is signaled by closing the channel returned by waiter.
    25  // Once a timeout has occurred, the deadline can be refreshed by specifying a
    26  // t value in the future.
    27  //
    28  // A zero value for t prevents timeout.
    29  func (d *pipeDeadline) set(t time.Time) {
    30  	d.mu.Lock()
    31  	defer d.mu.Unlock()
    32  
    33  	if d.timer != nil && !d.timer.Stop() {
    34  		<-d.cancel // Wait for the timer callback to finish and close cancel
    35  	}
    36  	d.timer = nil
    37  
    38  	// Time is zero, then there is no deadline.
    39  	closed := isClosedChan(d.cancel)
    40  	if t.IsZero() {
    41  		if closed {
    42  			d.cancel = make(chan struct{})
    43  		}
    44  		return
    45  	}
    46  
    47  	// Time in the future, setup a timer to cancel in the future.
    48  	if dur := time.Until(t); dur > 0 {
    49  		if closed {
    50  			d.cancel = make(chan struct{})
    51  		}
    52  		d.timer = time.AfterFunc(dur, func() {
    53  			close(d.cancel)
    54  		})
    55  		return
    56  	}
    57  
    58  	// Time in the past, so close immediately.
    59  	if !closed {
    60  		close(d.cancel)
    61  	}
    62  }
    63  
    64  // wait returns a channel that is closed when the deadline is exceeded.
    65  func (d *pipeDeadline) wait() chan struct{} {
    66  	d.mu.Lock()
    67  	defer d.mu.Unlock()
    68  	return d.cancel
    69  }
    70  
    71  func isClosedChan(c <-chan struct{}) bool {
    72  	select {
    73  	case <-c:
    74  		return true
    75  	default:
    76  		return false
    77  	}
    78  }
    79  
    80  func makeFilledChan() chan struct{} {
    81  	ch := make(chan struct{}, 1)
    82  	ch <- struct{}{}
    83  	return ch
    84  }