github.com/getlantern/eventual@v1.0.0/eventual.go (about)

     1  // Package eventual provides values that eventually have a value.
     2  package eventual
     3  
     4  import (
     5  	"math"
     6  	"sync"
     7  	"sync/atomic"
     8  	"time"
     9  )
    10  
    11  const (
    12  	// Forever indicates that Get should wait forever
    13  	Forever = -1
    14  )
    15  
    16  // Value is an eventual value, meaning that callers wishing to access the value
    17  // block until the value is available.
    18  type Value interface {
    19  	// Set sets this Value to the given val.
    20  	Set(val interface{})
    21  
    22  	// Get waits up to timeout for the value to be set and returns it, or returns
    23  	// nil if it times out or Cancel() is called. valid will be false in latter
    24  	// case. If timeout is 0, Get won't wait. If timeout is -1, Get will wait
    25  	// forever.
    26  	Get(timeout time.Duration) (ret interface{}, valid bool)
    27  
    28  	// Cancel cancels this value, signaling any waiting calls to Get() that no
    29  	// value is coming. If no value was set before Cancel() was called, all future
    30  	// calls to Get() will return nil, false. Subsequent calls to Set after Cancel
    31  	// have no effect.
    32  	Cancel()
    33  }
    34  
    35  // Getter is a functional interface for the Value.Get function
    36  type Getter func(time.Duration) (interface{}, bool)
    37  
    38  type value struct {
    39  	state   atomic.Value
    40  	waiters []chan interface{}
    41  	mutex   sync.Mutex
    42  }
    43  
    44  type stateholder struct {
    45  	val      interface{}
    46  	set      bool
    47  	canceled bool
    48  }
    49  
    50  // NewValue creates a new Value.
    51  func NewValue() Value {
    52  	result := &value{waiters: make([]chan interface{}, 0)}
    53  	result.state.Store(&stateholder{})
    54  	return result
    55  }
    56  
    57  // DefaultGetter builds a Getter that always returns the supplied value.
    58  func DefaultGetter(val interface{}) Getter {
    59  	return func(time.Duration) (interface{}, bool) {
    60  		return val, true
    61  	}
    62  }
    63  
    64  // DefaultUnsetGetter builds a Getter that always !ok.
    65  func DefaultUnsetGetter() Getter {
    66  	return func(time.Duration) (interface{}, bool) {
    67  		return nil, false
    68  	}
    69  }
    70  
    71  func (v *value) Set(val interface{}) {
    72  	v.mutex.Lock()
    73  	defer v.mutex.Unlock()
    74  
    75  	state := v.getState()
    76  	settable := !state.canceled
    77  	if settable {
    78  		v.setState(&stateholder{
    79  			val:      val,
    80  			set:      true,
    81  			canceled: false,
    82  		})
    83  
    84  		if v.waiters != nil {
    85  			// Notify anyone waiting for value
    86  			for _, waiter := range v.waiters {
    87  				waiter <- val
    88  			}
    89  			// Clear waiters
    90  			v.waiters = nil
    91  		}
    92  	}
    93  }
    94  
    95  func (v *value) Cancel() {
    96  	v.mutex.Lock()
    97  	defer v.mutex.Unlock()
    98  
    99  	state := v.getState()
   100  	v.setState(&stateholder{
   101  		val:      state.val,
   102  		set:      state.set,
   103  		canceled: true,
   104  	})
   105  
   106  	if v.waiters != nil {
   107  		// Notify anyone waiting for value
   108  		for _, waiter := range v.waiters {
   109  			close(waiter)
   110  		}
   111  		// Clear waiters
   112  		v.waiters = nil
   113  	}
   114  }
   115  
   116  func (v *value) Get(timeout time.Duration) (ret interface{}, valid bool) {
   117  	state := v.getState()
   118  
   119  	// First check for existing value using atomic operations (for speed)
   120  	if state.set {
   121  		// Value found, use it
   122  		return state.val, true
   123  	} else if state.canceled {
   124  		// Value was canceled, return false
   125  		return nil, false
   126  	}
   127  
   128  	if timeout == 0 {
   129  		// Don't wait
   130  		return nil, false
   131  	}
   132  
   133  	// If we didn't find an existing value, try again but this time using locking
   134  	v.mutex.Lock()
   135  	state = v.getState()
   136  
   137  	if state.set {
   138  		// Value found, use it
   139  		v.mutex.Unlock()
   140  		return state.val, true
   141  	} else if state.canceled {
   142  		// Value was canceled, return false
   143  		v.mutex.Unlock()
   144  		return nil, false
   145  	}
   146  
   147  	if timeout == -1 {
   148  		// Wait essentially forever
   149  		timeout = time.Duration(math.MaxInt64)
   150  	}
   151  
   152  	// Value not found, register to be notified once value is set
   153  	valCh := make(chan interface{}, 1)
   154  	v.waiters = append(v.waiters, valCh)
   155  	v.mutex.Unlock()
   156  
   157  	// Wait up to timeout for value to get set
   158  	select {
   159  	case v, ok := <-valCh:
   160  		return v, ok
   161  	case <-time.After(timeout):
   162  		return nil, false
   163  	}
   164  }
   165  
   166  func (v *value) getState() *stateholder {
   167  	state := v.state.Load()
   168  	if state == nil {
   169  		return nil
   170  	}
   171  	return state.(*stateholder)
   172  }
   173  
   174  func (v *value) setState(state *stateholder) {
   175  	v.state.Store(state)
   176  }