github.com/searKing/golang/go@v1.2.117/sync/conditional_variable.go (about)

     1  // Copyright 2021 The searKing Author. 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 sync
     6  
     7  import (
     8  	"context"
     9  	"errors"
    10  	"sync"
    11  	"time"
    12  )
    13  
    14  // ConditionVariable is an object able to block the calling thread until notified to resume.
    15  // see http://www.cplusplus.com/reference/condition_variable/condition_variable/
    16  type ConditionVariable struct {
    17  	subject Subject
    18  }
    19  
    20  // Wait atomically unlocks c.L and suspends execution
    21  // of the calling goroutine. After later resuming execution,
    22  // Wait locks c.L before returning. Unlike in other systems,
    23  // Wait cannot return unless awoken by Broadcast or Signal.
    24  //
    25  // Because c.L is not locked when Wait first resumes, the caller
    26  // typically cannot assume that the condition is true when
    27  // Wait returns. Instead, the caller should Wait in a loop:
    28  //
    29  //	c.L.Lock()
    30  //	for !condition() {
    31  //	    c.Wait()
    32  //	}
    33  //	... make use of condition ...
    34  //	c.L.Unlock()
    35  //
    36  // Wait wait until notified
    37  func (c *ConditionVariable) Wait(lck sync.Locker) {
    38  	_ = c.WaitContext(context.Background(), lck)
    39  }
    40  
    41  // WaitPred wait until notified
    42  // If pred is specified (2), the function only blocks if pred returns false,
    43  // and notifications can only unblock the thread when it becomes true
    44  // (which is specially useful to check against spurious wake-up calls). This version (2) behaves as if implemented as:
    45  // while (!pred()) wait(lck);
    46  func (c *ConditionVariable) WaitPred(lck sync.Locker, pred func() bool) {
    47  	_ = c.WaitPredContext(context.Background(), lck, pred)
    48  }
    49  
    50  // WaitFor The execution of the current goroutine (which shall have locked lck's mutex) is blocked during rel_time,
    51  // or until notified (if the latter happens first).
    52  // WaitFor wait for timeout or until notified
    53  // It behaves as if implemented as:
    54  // return wait_until (lck, chrono::steady_clock::now() + rel_time);
    55  func (c *ConditionVariable) WaitFor(lck sync.Locker, timeout time.Duration) error {
    56  	return c.WaitUntil(lck, time.Now().Add(timeout))
    57  }
    58  
    59  // WaitForPred wait for timeout or until notified
    60  // If pred is nil, do as pred returns false always,
    61  // If pred is specified, the function only blocks if pred returns false,
    62  // and notifications can only unblock the thread when it becomes true
    63  // (which is especially useful to check against spurious wake-up calls).
    64  // It behaves as if implemented as:
    65  // return wait_until (lck, chrono::steady_clock::now() + rel_time, std::move(pred));
    66  func (c *ConditionVariable) WaitForPred(lck sync.Locker, timeout time.Duration, pred func() bool) bool {
    67  	return c.WaitUntilPred(lck, time.Now().Add(timeout), pred)
    68  }
    69  
    70  // WaitUntil wait until notified or time point
    71  // The execution of the current thread (which shall have locked lck's mutex) is blocked either
    72  // until notified or until abs_time, whichever happens first.
    73  func (c *ConditionVariable) WaitUntil(lck sync.Locker, d time.Time) error {
    74  	ctx, cancelCtx := context.WithDeadline(context.Background(), d)
    75  	defer cancelCtx()
    76  	return c.WaitContext(ctx, lck)
    77  }
    78  
    79  // WaitUntilPred wait until notified or ctx done
    80  // The execution of the current thread (which shall have locked lck's mutex) is blocked either
    81  // until notified or until ctx, whichever happens first.
    82  // If pred is nil, do as pred returns false always,
    83  // If pred is specified, the function only blocks if pred returns false,
    84  // and notifications can only unblock the thread when it becomes true
    85  // (which is especially useful to check against spurious wake-up calls).
    86  // It behaves as if implemented as:
    87  // while (!pred())
    88  //
    89  //	if ( wait_until(lck,abs_time) == cv_status::timeout)
    90  //	  return pred();
    91  //
    92  // return true;
    93  func (c *ConditionVariable) WaitUntilPred(lck sync.Locker, d time.Time, pred func() bool) bool {
    94  	ctx, cancelCtx := context.WithDeadline(context.Background(), d)
    95  	defer cancelCtx()
    96  	return c.WaitPredContext(ctx, lck, pred)
    97  }
    98  
    99  // WaitContext wait until notified or time point
   100  // The execution of the current thread (which shall have locked lck's mutex) is blocked either
   101  // until notified or until ctx done, whichever happens first.
   102  func (c *ConditionVariable) WaitContext(ctx context.Context, lck sync.Locker) error {
   103  	eventC, cancel := c.subject.Subscribe()
   104  	defer cancel()
   105  	lck.Unlock()
   106  	defer lck.Lock()
   107  
   108  	select {
   109  	case <-eventC:
   110  		return nil
   111  	case <-ctx.Done():
   112  		return ctx.Err()
   113  	}
   114  }
   115  
   116  // WaitPredContext wait until notified or ctx done
   117  // The execution of the current thread (which shall have locked lck's mutex) is blocked either
   118  // until notified or until ctx, whichever happens first.
   119  // If pred is nil, do as pred returns false always,
   120  // If pred is specified, the function only blocks if pred returns false,
   121  // and notifications can only unblock the thread when it becomes true
   122  // (which is especially useful to check against spurious wake-up calls).
   123  // It behaves as if implemented as:
   124  // while (!pred())
   125  //
   126  //	if ( wait_until(ctx,lck) == cv_status::timeout)
   127  //	  return pred();
   128  //
   129  // return true;
   130  func (c *ConditionVariable) WaitPredContext(ctx context.Context, lck sync.Locker, pred func() bool) bool {
   131  	if pred == nil {
   132  		pred = func() bool { return false }
   133  	}
   134  	for !pred() {
   135  		if errors.Is(c.WaitContext(ctx, lck), context.DeadlineExceeded) {
   136  			return pred()
   137  		}
   138  	}
   139  	return true
   140  }
   141  
   142  // Signal wakes one goroutine waiting on c, if there is any.
   143  //
   144  // It is allowed but not required for the caller to hold c.L
   145  // during the call.
   146  func (c *ConditionVariable) Signal() {
   147  	go func() {
   148  		_ = c.subject.PublishSignal(context.Background(), nil)
   149  	}()
   150  }
   151  
   152  // Broadcast wakes all goroutines waiting on c.
   153  //
   154  // It is allowed but not required for the caller to hold c.L
   155  // during the call.
   156  func (c *ConditionVariable) Broadcast() {
   157  	go func() {
   158  		_ = c.subject.PublishBroadcast(context.Background(), nil)
   159  	}()
   160  }