github.com/wfusion/gofusion@v1.1.14/routine/future.go (about) 1 package routine 2 3 import ( 4 "errors" 5 "fmt" 6 "runtime/debug" 7 "sync/atomic" 8 "time" 9 "unsafe" 10 ) 11 12 type callbackType int 13 14 const ( 15 CallbackDone callbackType = 1 + iota 16 CallbackFail 17 CallbackAlways 18 CallbackCancel 19 ) 20 21 // pipe presents a promise that will be chain call 22 type pipe struct { 23 pipeDoneTask, pipeFailTask func(v any) *Future 24 pipePromise *promise 25 } 26 27 // getPipe returns piped Future task function and pipe promise by the status of current promise. 28 func (p *pipe) getPipe(isResolved bool) (func(v any) *Future, *promise) { 29 if isResolved { 30 return p.pipeDoneTask, p.pipePromise 31 } else { 32 return p.pipeFailTask, p.pipePromise 33 } 34 } 35 36 // Canceller is used to check if the future is cancelled 37 // It be usually passed to the future task function 38 // for future task function can check if the future is cancelled. 39 type Canceller interface { 40 IsCancelled() bool 41 Cancel() 42 } 43 44 // canceller provides an implement of Canceller interface. 45 // It will be passed to future task function as parameter 46 type canceller struct { 47 f *Future 48 } 49 50 // Cancel sets Future task to CANCELLED status 51 func (c *canceller) Cancel() { 52 _ = c.f.Cancel() 53 } 54 55 // IsCancelled returns true if Future task is cancelled, otherwise false. 56 func (c *canceller) IsCancelled() (r bool) { 57 return c.f.IsCancelled() 58 } 59 60 // futureVal stores the internal state of Future. 61 type futureVal struct { 62 dones, fails, always []func(v any) 63 cancels []func() 64 pipes []*pipe 65 r *Result 66 } 67 68 // Future provides a read-only view of promise, 69 // the value is set by using Resolve, Reject and Cancel methods of related promise 70 type Future struct { 71 Id int // ID can be used as identity of Future 72 AppName string 73 74 final chan struct{} 75 // val point to futureVal that stores status of future 76 // if we need to change the status of future, must copy a new futureVal and modify it, 77 // then use CAS to put the pointer of new futureVal 78 val unsafe.Pointer 79 } 80 81 // Canceller returns a canceller object related to future. 82 func (f *Future) Canceller() Canceller { 83 return &canceller{f} 84 } 85 86 // IsCancelled returns true if the promise is cancelled, otherwise false 87 func (f *Future) IsCancelled() bool { 88 val := f.loadVal() 89 90 if val != nil && val.r != nil && val.r.Typ == ResultCancelled { 91 return true 92 } else { 93 return false 94 } 95 } 96 97 // SetTimeout sets the future task will be cancelled 98 // if future is not complete before time out 99 func (f *Future) SetTimeout(timeout time.Duration) *Future { 100 if timeout <= 0 { 101 timeout = 10 * time.Nanosecond 102 } 103 104 go func() { 105 defer func() { 106 if e := recover(); e != nil { 107 err := newErrorWithStacks(e) 108 fmt.Println("error happens:\n ", err) 109 } 110 }() 111 112 timer := time.NewTimer(timeout) 113 defer timer.Stop() 114 <-timer.C 115 116 _ = f.Cancel() 117 }() 118 return f 119 } 120 121 // GetChan returns a channel than can be used to receive result of promise 122 func (f *Future) GetChan() <-chan *Result { 123 c := make(chan *Result, 1) 124 f.OnComplete(func(v any) { 125 c <- f.loadResult() 126 }).OnCancel(func() { 127 c <- f.loadResult() 128 }) 129 return c 130 } 131 132 // Get will block current goroutines until the Future is resolved/rejected/cancelled. 133 // If Future is resolved, value and nil will be returned 134 // If Future is rejected, nil and error will be returned. 135 // If Future is cancelled, nil and CANCELLED error will be returned. 136 func (f *Future) Get() (val any, err error) { 137 <-f.final 138 return getFutureReturnVal(f.loadResult()) 139 } 140 141 // GetOrTimeout is similar to Get(), but GetOrTimeout will not block after timeout. 142 // If GetOrTimeout returns with a timeout, timeout value will be true in return values. 143 // The unit of parameter is millisecond. 144 func (f *Future) GetOrTimeout(timeout time.Duration) (val any, timout bool, err error) { 145 if timeout <= 0 { 146 timeout = 10 * time.Nanosecond 147 } 148 149 timer := time.NewTimer(timeout) 150 defer timer.Stop() 151 152 select { 153 case <-timer.C: 154 _ = f.Cancel() 155 return nil, true, nil 156 case <-f.final: 157 r, err := getFutureReturnVal(f.loadResult()) 158 return r, false, err 159 } 160 } 161 162 // Cancel sets the status of promise to ResultCancelled. 163 // If promise is cancelled, Get() will return nil and CANCELLED error. 164 // All callback functions will be not called if promise is cancelled. 165 func (f *Future) Cancel() (e error) { 166 return f.setResult(&Result{ErrCancelled, ResultCancelled}) 167 } 168 169 // OnSuccess registers a callback function that will be called when promise is resolved. 170 // If promise is already resolved, the callback will immediately be called. 171 // The value of promise will be parameter of Done callback function. 172 func (f *Future) OnSuccess(callback func(v any)) *Future { 173 f.addCallback(callback, CallbackDone) 174 return f 175 } 176 177 // OnFailure registers a callback function that will be called when promise is rejected. 178 // If promise is already rejected, the callback will immediately be called. 179 // The error of promise will be parameter of Fail callback function. 180 func (f *Future) OnFailure(callback func(v any)) *Future { 181 f.addCallback(callback, CallbackFail) 182 return f 183 } 184 185 // OnComplete register a callback function that will be called when promise is rejected or resolved. 186 // If promise is already rejected or resolved, the callback will immediately be called. 187 // According to the status of promise, value or error will be parameter of Always callback function. 188 // Value is the parameter if promise is resolved, or error is the parameter if promise is rejected. 189 // Always callback will be not called if promise be called. 190 func (f *Future) OnComplete(callback func(v any)) *Future { 191 f.addCallback(callback, CallbackAlways) 192 return f 193 } 194 195 // OnCancel registers a callback function that will be called when promise is cancelled. 196 // If promise is already cancelled, the callback will immediately be called. 197 func (f *Future) OnCancel(callback func()) *Future { 198 f.addCallback(callback, CallbackCancel) 199 return f 200 } 201 202 // Pipe registers one or two functions that returns a Future, and returns a proxy of pipeline Future. 203 // First function will be called when Future is resolved, the returned Future will be as pipeline Future. 204 // Secondary function will be called when Future is rejected, the returned Future will be as pipeline Future. 205 func (f *Future) Pipe(callbacks ...any) (result *Future, ok bool) { 206 if len(callbacks) == 0 || 207 (len(callbacks) == 1 && callbacks[0] == nil) || 208 (len(callbacks) > 1 && callbacks[0] == nil && callbacks[1] == nil) { 209 result = f 210 return 211 } 212 213 // ensure all callback functions match the spec "func(v any) *Future" 214 cs := make([]func(v any) *Future, len(callbacks)) 215 for i, callback := range callbacks { 216 switch c := callback.(type) { 217 case func(v any) *Future: 218 cs[i] = c 219 case func() *Future: 220 cs[i] = func(v any) *Future { 221 return c() 222 } 223 case func(v any): 224 cs[i] = func(v any) *Future { 225 return start(func() { 226 c(v) 227 }, true) 228 } 229 case func(v any) (r any, err error): 230 cs[i] = func(v any) *Future { 231 return start(func() (r any, err error) { 232 r, err = c(v) 233 return 234 }, true) 235 } 236 case func(): 237 cs[i] = func(v any) *Future { 238 return start(func() { 239 c() 240 }, true) 241 } 242 case func() (r any, err error): 243 cs[i] = func(v any) *Future { 244 return start(func() (r any, err error) { 245 r, err = c() 246 return 247 }, true) 248 } 249 default: 250 ok = false 251 return 252 } 253 } 254 255 for { 256 v := f.loadVal() 257 r := v.r 258 if r != nil { 259 result = f 260 if r.Typ == ResultSuccess && cs[0] != nil { 261 result = cs[0](r.Result) 262 } else if r.Typ == ResultFailure && len(cs) > 1 && cs[1] != nil { 263 result = cs[1](r.Result) 264 } 265 } else { 266 newPipe := &pipe{} 267 newPipe.pipeDoneTask = cs[0] 268 if len(cs) > 1 { 269 newPipe.pipeFailTask = cs[1] 270 } 271 newPipe.pipePromise = NewPromise() 272 273 newVal := *v 274 newVal.pipes = append(newVal.pipes, newPipe) 275 276 // use CAS to ensure that the state of Future is not changed, 277 // if the state is changed, will retry CAS operation. 278 if atomic.CompareAndSwapPointer(&f.val, unsafe.Pointer(v), unsafe.Pointer(&newVal)) { 279 result = newPipe.pipePromise.Future 280 break 281 } 282 } 283 } 284 285 ok = true 286 return 287 } 288 289 // result uses Atomic load to return result of the Future 290 func (f *Future) loadResult() *Result { 291 val := f.loadVal() 292 return val.r 293 } 294 295 // val uses Atomic load to return state value of the Future 296 func (f *Future) loadVal() *futureVal { 297 r := atomic.LoadPointer(&f.val) 298 return (*futureVal)(r) 299 } 300 301 // setResult sets the value and final status of promise, it will only be executed for once 302 func (f *Future) setResult(r *Result) (e error) { 303 defer func() { 304 if err := getError(recover()); err != nil { 305 e = err 306 fmt.Printf("\nerror in setResult(): %s\n%s\n", err, debug.Stack()) 307 } 308 }() 309 310 e = errors.New("cannot resolve/reject/cancel more than once") 311 312 for { 313 v := f.loadVal() 314 if v.r != nil { 315 return 316 } 317 newVal := *v 318 newVal.r = r 319 320 // Use CAS operation to ensure that the state of promise isn't changed. 321 // If the state is changed, must get the latest state and try to call CAS again. 322 // No ABA issue in this case because address of all objects are different. 323 if atomic.CompareAndSwapPointer(&f.val, unsafe.Pointer(v), unsafe.Pointer(&newVal)) { 324 // Close chEnd then all Get() and GetOrTimeout() will be unblocked 325 close(f.final) 326 327 // call callback functions and start the promise pipeline 328 if len(v.dones) > 0 || len(v.fails) > 0 || len(v.always) > 0 || len(v.cancels) > 0 { 329 go func() { 330 defer func() { 331 if e := recover(); e != nil { 332 err := newErrorWithStacks(e) 333 fmt.Println("error happens:\n ", err) 334 } 335 }() 336 execCallback(r, v.dones, v.fails, v.always, v.cancels) 337 }() 338 } 339 340 // start the pipeline 341 if len(v.pipes) > 0 { 342 go func() { 343 defer func() { 344 if e := recover(); e != nil { 345 err := newErrorWithStacks(e) 346 fmt.Println("error happens:\n ", err) 347 } 348 }() 349 for _, pipe := range v.pipes { 350 pipeTask, pipePromise := pipe.getPipe(r.Typ == ResultSuccess) 351 startPipe(r, pipeTask, pipePromise) 352 } 353 }() 354 } 355 e = nil 356 break 357 } 358 } 359 return 360 } 361 362 // handleOneCallback registers a callback function 363 func (f *Future) addCallback(callback any, t callbackType) { 364 if callback == nil { 365 return 366 } 367 if (t == CallbackDone) || 368 (t == CallbackFail) || 369 (t == CallbackAlways) { 370 if _, ok := callback.(func(v any)); !ok { 371 panic(errors.New("callback function spec must be func(v any)")) 372 } 373 } else if t == CallbackCancel { 374 if _, ok := callback.(func()); !ok { 375 panic(errors.New("callback function spec must be func()")) 376 } 377 } 378 379 for { 380 v := f.loadVal() 381 r := v.r 382 if r == nil { 383 newVal := *v 384 switch t { 385 case CallbackDone: 386 newVal.dones = append(newVal.dones, callback.(func(v any))) 387 case CallbackFail: 388 newVal.fails = append(newVal.fails, callback.(func(v any))) 389 case CallbackAlways: 390 newVal.always = append(newVal.always, callback.(func(v any))) 391 case CallbackCancel: 392 newVal.cancels = append(newVal.cancels, callback.(func())) 393 } 394 395 // use CAS to ensure that the state of Future is not changed, 396 // if the state is changed, will retry CAS operation. 397 if atomic.CompareAndSwapPointer(&f.val, unsafe.Pointer(v), unsafe.Pointer(&newVal)) { 398 break 399 } 400 } else { 401 if (t == CallbackDone && r.Typ == ResultSuccess) || 402 (t == CallbackFail && r.Typ == ResultFailure) || 403 (t == CallbackAlways && r.Typ != ResultCancelled) { 404 callbackFunc := callback.(func(v any)) 405 callbackFunc(r.Result) 406 } else if t == CallbackCancel && r.Typ == ResultCancelled { 407 callbackFunc := callback.(func()) 408 callbackFunc() 409 } 410 break 411 } 412 } 413 }