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 }