github.com/rolandhe/saber@v0.0.4/gocc/condtimeout.go (about)

     1  // code from https://gist.github.com/zviadm/c234426882bfc8acba88f3503edaaa36#file-cond2-go
     2  
     3  package gocc
     4  
     5  import (
     6  	"sync"
     7  	"sync/atomic"
     8  	"time"
     9  	"unsafe"
    10  )
    11  
    12  // Condition 支持wait timeout的同步条件
    13  type Condition interface {
    14  	// Wait 同Cond Wait, 等待直到被唤醒
    15  	Wait()
    16  	// WaitWithTimeout , 带有超时的等待
    17  	WaitWithTimeout(t time.Duration)
    18  	// Broadcast 同Cond Broadcast
    19  	Broadcast()
    20  	// Signal 同Cond Signal
    21  	Signal()
    22  }
    23  
    24  // NewCondTimeout 构建支持timeout的cond
    25  func NewCondTimeout(l sync.Locker) Condition {
    26  	c := &condTimeout{locker: l}
    27  	n := make(chan struct{})
    28  	c.n = unsafe.Pointer(&n)
    29  	return c
    30  }
    31  
    32  // NewCondTimeoutWithName 构建Condition,可以指定名字, 日志输出是带有名字,方便排查问题
    33  func NewCondTimeoutWithName(l sync.Locker, name string) Condition {
    34  	c := &condTimeout{locker: l, name: name}
    35  	n := make(chan struct{})
    36  	c.n = unsafe.Pointer(&n)
    37  	return c
    38  }
    39  
    40  type condTimeout struct {
    41  	locker sync.Locker
    42  	n      unsafe.Pointer
    43  	name   string
    44  }
    45  
    46  // Wait Waits for Broadcast calls. Similar to regular sync.Cond, this unlocks the underlying
    47  // locker first, waits on changes and re-locks it before returning.
    48  func (c *condTimeout) Wait() {
    49  	n := c.NotifyChan()
    50  	c.locker.Unlock()
    51  	<-n
    52  	c.locker.Lock()
    53  }
    54  
    55  // WaitWithTimeout Same as Wait() call, but will only wait up to a given timeout.
    56  func (c *condTimeout) WaitWithTimeout(t time.Duration) {
    57  	n := c.NotifyChan()
    58  	c.locker.Unlock()
    59  
    60  	select {
    61  	case <-n:
    62  	case <-time.After(t):
    63  		CcLogger.Info("name:%s,wait with timeout\n", c.name)
    64  	}
    65  	c.locker.Lock()
    66  }
    67  
    68  // NotifyChan Returns a channel that can be used to wait for next Broadcast() call.
    69  func (c *condTimeout) NotifyChan() chan struct{} {
    70  	ptr := atomic.LoadPointer(&c.n)
    71  	return *((*chan struct{})(ptr))
    72  }
    73  
    74  // Broadcast call notifies everyone that something has changed.
    75  func (c *condTimeout) Broadcast() {
    76  	n := make(chan struct{})
    77  	ptrOld := atomic.SwapPointer(&c.n, unsafe.Pointer(&n))
    78  	close(*(*chan struct{})(ptrOld))
    79  }
    80  
    81  func (c *condTimeout) Signal() {
    82  	n := c.NotifyChan()
    83  	select {
    84  	case n <- struct{}{}:
    85  	default:
    86  	}
    87  }