github.com/kaydxh/golang@v0.0.131/go/sync/cond.go (about)

     1  /*
     2   *Copyright (c) 2022, kaydxh
     3   *
     4   *Permission is hereby granted, free of charge, to any person obtaining a copy
     5   *of this software and associated documentation files (the "Software"), to deal
     6   *in the Software without restriction, including without limitation the rights
     7   *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     8   *copies of the Software, and to permit persons to whom the Software is
     9   *furnished to do so, subject to the following conditions:
    10   *
    11   *The above copyright notice and this permission notice shall be included in all
    12   *copies or substantial portions of the Software.
    13   *
    14   *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    15   *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    16   *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    17   *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    18   *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    19   *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    20   *SOFTWARE.
    21   */
    22  package sync
    23  
    24  import (
    25  	"fmt"
    26  	"sync"
    27  	"sync/atomic"
    28  	"time"
    29  	"unsafe"
    30  )
    31  
    32  // Cond 条件变量不保证顺序性,即signal只是通知wait去获取数据,wait拿到的数据是不是
    33  // signal当时给的数据,不能保证
    34  type Cond struct {
    35  	L       sync.Locker
    36  	ch      chan struct{}
    37  	checker copyChecker
    38  	noCopy  noCopy
    39  }
    40  
    41  func NewCond(l sync.Locker) *Cond {
    42  	c := &Cond{
    43  		L:  l,
    44  		ch: make(chan struct{}),
    45  	}
    46  	return c
    47  }
    48  
    49  func (c *Cond) wait() {
    50  	c.checker.check()
    51  	c.L.Unlock()
    52  	defer c.L.Lock()
    53  	<-c.ch
    54  
    55  }
    56  
    57  func (c *Cond) waitFor(timeout time.Duration) error {
    58  	c.L.Unlock()
    59  	defer c.L.Lock()
    60  
    61  	select {
    62  	case <-c.ch:
    63  		return nil
    64  	case <-time.After(timeout):
    65  		return fmt.Errorf("wait timeout: %v\n", timeout)
    66  	}
    67  
    68  }
    69  
    70  func (c *Cond) WaitForDo(timeout time.Duration, pred func() bool, do func() error) error {
    71  	c.checker.check()
    72  	c.L.Lock()
    73  	defer c.L.Unlock()
    74  
    75  	for !pred() {
    76  		start := time.Now()
    77  		err := c.waitFor(timeout)
    78  		if err != nil {
    79  			return err
    80  		}
    81  
    82  		timeout -= time.Now().Sub(start)
    83  	}
    84  	if do != nil {
    85  		do()
    86  	}
    87  	return nil
    88  }
    89  
    90  func (c *Cond) WaitUntil(pred func() bool) {
    91  	c.checker.check()
    92  	c.WaitUntilDo(pred, func() error { return nil })
    93  }
    94  
    95  func (c *Cond) WaitUntilDo(pred func() bool, do func() error) {
    96  	c.checker.check()
    97  	for {
    98  		err := c.WaitForDo(5*time.Second, pred, do)
    99  		if err == nil {
   100  			break
   101  		}
   102  	}
   103  
   104  }
   105  
   106  func (c *Cond) SignalDo(do func() error) {
   107  	c.L.Lock()
   108  	if do != nil {
   109  		do()
   110  	}
   111  	c.L.Unlock()
   112  
   113  	c.Signal()
   114  }
   115  
   116  func (c *Cond) BroadcastDo(do func() error) {
   117  	c.L.Lock()
   118  	if do != nil {
   119  		do()
   120  	}
   121  	c.L.Unlock()
   122  
   123  	c.Broadcast()
   124  }
   125  
   126  func (c *Cond) Signal() {
   127  	c.checker.check()
   128  	go func() {
   129  		select {
   130  		case c.ch <- struct{}{}:
   131  		default:
   132  		}
   133  	}()
   134  }
   135  
   136  func (c *Cond) Broadcast() {
   137  	c.checker.check()
   138  	go func() {
   139  		close(c.ch)
   140  		c.ch = make(chan struct{})
   141  	}()
   142  }
   143  
   144  // copyChecker holds back pointer to itself to detect object copying.
   145  type copyChecker uintptr
   146  
   147  func (c *copyChecker) check() {
   148  	if uintptr(*c) != uintptr(unsafe.Pointer(c)) &&
   149  		!atomic.CompareAndSwapUintptr((*uintptr)(c), 0, uintptr(unsafe.Pointer(c))) &&
   150  		uintptr(*c) != uintptr(unsafe.Pointer(c)) {
   151  		panic("sync.Cond is copied")
   152  	}
   153  }
   154  
   155  // noCopy may be embedded into structs which must not be copied
   156  // after the first use.
   157  //
   158  // See https://golang.org/issues/8005#issuecomment-190753527
   159  // for details.
   160  type noCopy struct{}
   161  
   162  // Lock is a no-op used by -copylocks checker from `go vet`.
   163  func (*noCopy) Lock()   {}
   164  func (*noCopy) Unlock() {}