github.com/haraldrudell/parl@v0.4.176/future.go (about) 1 /* 2 © 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package parl 7 8 import ( 9 "sync/atomic" 10 11 "github.com/haraldrudell/parl/perrors" 12 ) 13 14 // Future is a container for an awaitable calculation result 15 // - Future allows a thread to await a value calculated in parallel 16 // by other threads 17 // - unlike for a promise, consumer manages any thread, 18 // therefore producing debuggable code and meaningful stack traces 19 // - a promise launches the thread why there is no trace of what code 20 // created the promise or why 21 type Future[T any] struct { 22 // one-to-many wait mechanic based on channel 23 await Awaitable 24 // calculation outcome 25 result atomic.Pointer[TResult[T]] 26 } 27 28 // NewFuture returns an awaitable calculation 29 // - has an Awaitable and a thread-safe TResult container 30 // 31 // Usage: 32 // 33 // var calculation = NewFuture[someType]() 34 // go calculateThread(calculation) 35 // … 36 // var result, isValid = calculation.Result() 37 // 38 // func calculateThread(future *Future[someType]) { 39 // var err error 40 // var isPanic bool 41 // var value someType 42 // defer calculation.End(&value, &isPanic, &err) 43 // defer parl.RecoverErr(func() parl.DA { return parl.A() }, &err, &isPanic) 44 // 45 // value = … 46 func NewFuture[T any]() (calculation *Future[T]) { return &Future[T]{} } 47 48 // IsCompleted returns whether the calculation is complete. Thread-safe 49 func (f *Future[T]) IsCompleted() (isCompleted bool) { return f.await.IsClosed() } 50 51 // Ch returns an awaitable channel. Thread-safe 52 func (f *Future[T]) Ch() (ch AwaitableCh) { return f.await.Ch() } 53 54 // Result retrieves the calculation’s result 55 // - May block. Thread-safe 56 func (f *Future[T]) Result() (result T, hasValue bool) { 57 58 // blocks here 59 <-f.await.Ch() 60 61 if rp := f.result.Load(); rp != nil { 62 result = rp.Value 63 hasValue = rp.Err == nil 64 } 65 66 return 67 } 68 69 // TResult returns a pointer to the future’s result 70 // - nil if future has not resolved 71 // - thread-safe 72 func (f *Future[T]) TResult() (tResult *TResult[T]) { return f.result.Load() } 73 74 // End writes the result of the calculation, deferrable 75 // - value is considered valid if errp is nil or *errp is nil 76 // - End can make a goroutine channel-awaitable 77 // - End can only be invoked once or panic 78 // - any argument may be nil 79 // - thread-safe 80 func (f *Future[T]) End(value *T, isPanic *bool, errp *error) { 81 82 // create result to swap-in for atomic 83 var result = NewTResult3(value, isPanic, errp) 84 85 // check for multiple invocations 86 if !f.result.CompareAndSwap(nil, result) { 87 panic(perrors.NewPF("End invoked multiple times")) 88 } 89 90 // trigger awaitable 91 f.await.Close() 92 }