github.com/jmigpin/editor@v1.6.0/util/syncutil/waitforset.go (about)

     1  package syncutil
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  	"time"
     7  )
     8  
     9  // Continously usable, instantiated once for many wait()/set() calls. Fails if wait() is not ready when set() is called.
    10  // Usage:
    11  // 	w:=NewWaitForSet()
    12  // 	w.Start(5*time.Second)
    13  // 	...
    14  // 	// sync/async call to w.Set()
    15  // 	...
    16  //	v,err := w.WaitForSet()
    17  //	if err!=nil {
    18  // 	}
    19  type WaitForSet struct {
    20  	d struct {
    21  		sync.Mutex
    22  		get struct {
    23  			timer   *time.Timer
    24  			waiting bool
    25  		}
    26  		set struct {
    27  			gotV bool
    28  			v    interface{}
    29  		}
    30  	}
    31  	cond *sync.Cond // signals from set() or timeout()
    32  }
    33  
    34  func NewWaitForSet() *WaitForSet {
    35  	w := &WaitForSet{}
    36  	w.cond = sync.NewCond(&w.d)
    37  	return w
    38  }
    39  
    40  //----------
    41  
    42  func (w *WaitForSet) Start(timeout time.Duration) {
    43  	w.d.Lock()
    44  	defer w.d.Unlock()
    45  	if w.d.get.timer != nil {
    46  		panic("waitforset: timer!=nil")
    47  	}
    48  	w.d.get.timer = time.AfterFunc(timeout, w.cond.Signal)
    49  }
    50  
    51  func (w *WaitForSet) WaitForSet() (interface{}, error) {
    52  	w.d.Lock()
    53  	defer w.d.Unlock()
    54  	defer w.clearTimer()
    55  	if w.d.get.timer == nil {
    56  		panic("waitforset: not started")
    57  	}
    58  	if w.d.get.waiting {
    59  		panic("waitforset: already waiting")
    60  	}
    61  	w.d.get.waiting = true
    62  	defer func() { w.d.get.waiting = false }()
    63  
    64  	// wait for signal if the value was not set yet
    65  	if !w.d.set.gotV {
    66  		w.cond.Wait() // wait for signal from set() or timeout start()
    67  	}
    68  
    69  	if w.d.set.gotV {
    70  		defer func() { w.d.set.gotV = false }() // reset for next run
    71  		return w.d.set.v, nil
    72  	}
    73  	return nil, fmt.Errorf("waitforset: timeout")
    74  }
    75  
    76  //----------
    77  
    78  // In case waitforset() is not going to be called.
    79  func (w *WaitForSet) Cancel() {
    80  	w.d.Lock()
    81  	defer w.d.Unlock()
    82  	w.clearTimer()
    83  }
    84  
    85  func (w *WaitForSet) clearTimer() {
    86  	w.d.get.timer.Stop()
    87  	w.d.get.timer = nil
    88  }
    89  
    90  //----------
    91  
    92  // Fails if not able to set while get() is ready.
    93  func (w *WaitForSet) Set(v interface{}) error {
    94  	w.d.Lock()
    95  	defer w.d.Unlock()
    96  	if w.d.get.timer == nil {
    97  		return fmt.Errorf("waitforset: not waiting for set")
    98  	}
    99  	w.d.set.gotV = true
   100  	w.d.set.v = v
   101  	w.cond.Signal()
   102  	return nil
   103  }