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  }