github.com/biogo/biogo@v1.0.4/concurrent/promise.go (about)

     1  // Copyright ©2011-2012 The bíogo Authors. 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 concurrent
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"sync"
    11  )
    12  
    13  // Implementation of a promise multiple goroutine synchronisation and communication system
    14  // based on the approach used in Alice. Promises will safely allow multiple promisers to
    15  // interact with multiple promisees.
    16  //
    17  // New or non-error Broken Promises can be Fulfilled or Failed. Fulfilled or Failed Promises
    18  // can be Broken and any state of Promise can be Recovered if specified at creation.
    19  //
    20  // Promises can be mutable or not, recoverable or not and may relay internal error states
    21  // to other listeners.
    22  // Mutable promises may have their value state changed with subsequence Fulfill calls.
    23  // Recoverable promises may be recovered after a Fail call.
    24  // Promises created with relay set to true will relay an error generated by attempting to
    25  // fulfill an immutable fulfilled promise.
    26  type Promise struct {
    27  	message     chan Result
    28  	m           sync.Mutex
    29  	mutable     bool
    30  	recoverable bool
    31  	relay       bool
    32  }
    33  
    34  // Create a new promise
    35  func NewPromise(mutable, recoverable, relay bool) *Promise {
    36  	return &Promise{
    37  		message:     make(chan Result, 1),
    38  		mutable:     mutable,
    39  		recoverable: recoverable,
    40  		relay:       relay,
    41  	}
    42  
    43  }
    44  
    45  func (p *Promise) messageState() (message Result, set bool) {
    46  	select {
    47  	case message = <-p.message:
    48  		set = true
    49  	default:
    50  	}
    51  
    52  	return
    53  }
    54  
    55  // Fulfill a promise, allowing listeners to unblock.
    56  func (p *Promise) Fulfill(value interface{}) error {
    57  	p.m.Lock()
    58  	defer p.m.Unlock()
    59  
    60  	return p.fulfill(value)
    61  }
    62  
    63  func (p *Promise) fulfill(value interface{}) (err error) {
    64  	r, set := p.messageState()
    65  
    66  	if r.Err != nil {
    67  		err = fmt.Errorf("concurrent: attempt to fulfill failed promise: %v", r.Err)
    68  	} else {
    69  		if !set || p.mutable {
    70  			r.Value = value
    71  			err = nil
    72  		} else {
    73  			err = errors.New("concurrent: attempt to fulfill already set immutable promise")
    74  		}
    75  	}
    76  
    77  	if err != nil && p.relay {
    78  		if r.Err != nil {
    79  			err = fmt.Errorf("concurrent: promise already failed - cannot relay: %v", r.Err)
    80  		} else {
    81  			r.Err = err
    82  		}
    83  	}
    84  
    85  	p.message <- r
    86  
    87  	return
    88  }
    89  
    90  // Fail a promise allowing listeners to unblock, but sending an error state.
    91  func (p *Promise) Fail(value interface{}, err error) (ok bool) {
    92  	p.m.Lock()
    93  	defer p.m.Unlock()
    94  
    95  	return p.fail(value, err)
    96  }
    97  
    98  func (p *Promise) fail(value interface{}, err error) (f bool) {
    99  	r, _ := p.messageState()
   100  
   101  	if r.Err == nil && r.Value == nil {
   102  		if value != nil {
   103  			r.Value = value
   104  		}
   105  		r.Err = err
   106  		f = true
   107  	} else {
   108  		f = false
   109  	}
   110  
   111  	p.message <- r
   112  
   113  	return
   114  }
   115  
   116  // Recover a failed promise, setting the error state to nil. Promise must be recoverable.
   117  func (p *Promise) Recover(value interface{}) (ok bool) {
   118  	p.m.Lock()
   119  	defer p.m.Unlock()
   120  
   121  	r, _ := p.messageState()
   122  
   123  	if p.recoverable {
   124  		r.Err = nil
   125  		if value != nil {
   126  			p.fulfill(value)
   127  		}
   128  		ok = true
   129  	} else {
   130  		ok = false
   131  	}
   132  
   133  	return
   134  }
   135  
   136  // Break an already fulfilled or failed promise, blocking all listeners.
   137  func (p *Promise) Break() {
   138  	p.m.Lock()
   139  	defer p.m.Unlock()
   140  
   141  	p.messageState()
   142  }
   143  
   144  // Wait for a promise to be fulfilled, failed or recovered.
   145  func (p *Promise) Wait() <-chan Result {
   146  	r := <-p.message
   147  	p.message <- r
   148  	f := make(chan Result, 1)
   149  	f <- r
   150  	close(f)
   151  	return f
   152  }