github.com/searKing/golang/go@v1.2.74/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  //  if ( wait_until(lck,abs_time) == cv_status::timeout)
    89  //    return pred();
    90  // return true;
    91  func (c *ConditionVariable) WaitUntilPred(lck sync.Locker, d time.Time, pred func() bool) bool {
    92  	ctx, cancelCtx := context.WithDeadline(context.Background(), d)
    93  	defer cancelCtx()
    94  	return c.WaitPredContext(ctx, lck, pred)
    95  }
    96  
    97  // WaitContext wait until notified or time point
    98  // The execution of the current thread (which shall have locked lck's mutex) is blocked either
    99  // until notified or until ctx done, whichever happens first.
   100  func (c *ConditionVariable) WaitContext(ctx context.Context, lck sync.Locker) error {
   101  	eventC, cancel := c.subject.Subscribe()
   102  	defer cancel()
   103  	lck.Unlock()
   104  	defer lck.Lock()
   105  
   106  	select {
   107  	case <-eventC:
   108  		return nil
   109  	case <-ctx.Done():
   110  		return ctx.Err()
   111  	}
   112  }
   113  
   114  // WaitPredContext wait until notified or ctx done
   115  // The execution of the current thread (which shall have locked lck's mutex) is blocked either
   116  // until notified or until ctx, whichever happens first.
   117  // If pred is nil, do as pred returns false always,
   118  // If pred is specified, the function only blocks if pred returns false,
   119  // and notifications can only unblock the thread when it becomes true
   120  // (which is especially useful to check against spurious wake-up calls).
   121  // It behaves as if implemented as:
   122  // while (!pred())
   123  //  if ( wait_until(ctx,lck) == cv_status::timeout)
   124  //    return pred();
   125  // return true;
   126  func (c *ConditionVariable) WaitPredContext(ctx context.Context, lck sync.Locker, pred func() bool) bool {
   127  	if pred == nil {
   128  		pred = func() bool { return false }
   129  	}
   130  	for !pred() {
   131  		if errors.Is(c.WaitContext(ctx, lck), context.DeadlineExceeded) {
   132  			return pred()
   133  		}
   134  	}
   135  	return true
   136  }
   137  
   138  // Signal wakes one goroutine waiting on c, if there is any.
   139  //
   140  // It is allowed but not required for the caller to hold c.L
   141  // during the call.
   142  func (c *ConditionVariable) Signal() {
   143  	go func() {
   144  		_ = c.subject.PublishSignal(context.Background(), nil)
   145  	}()
   146  }
   147  
   148  // Broadcast wakes all goroutines waiting on c.
   149  //
   150  // It is allowed but not required for the caller to hold c.L
   151  // during the call.
   152  func (c *ConditionVariable) Broadcast() {
   153  	go func() {
   154  		_ = c.subject.PublishBroadcast(context.Background(), nil)
   155  	}()
   156  }