github.com/aacfactory/fns@v1.2.86-0.20240310083819-80d667fc0a17/commons/futures/future.go (about)

     1  /*
     2   * Copyright 2023 Wang Min Xiang
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   * 	http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   *
    16   */
    17  
    18  package futures
    19  
    20  import (
    21  	"context"
    22  	"github.com/aacfactory/errors"
    23  	"github.com/aacfactory/fns/commons/objects"
    24  	"sync"
    25  )
    26  
    27  type Promise interface {
    28  	Succeed(v any)
    29  	Failed(err error)
    30  	Close()
    31  }
    32  
    33  type Result interface {
    34  	objects.Object
    35  }
    36  
    37  type Future interface {
    38  	Await(ctx context.Context) (result Result, err error)
    39  }
    40  
    41  var (
    42  	pool = sync.Pool{}
    43  )
    44  
    45  type sch struct {
    46  	locker sync.Locker
    47  	ch     chan value
    48  	closed bool
    49  }
    50  
    51  func (s *sch) send(v value) {
    52  	s.locker.Lock()
    53  	if s.closed {
    54  		s.locker.Unlock()
    55  		return
    56  	}
    57  	s.ch <- v
    58  	s.locker.Unlock()
    59  }
    60  
    61  func (s *sch) destroy() {
    62  	s.locker.Lock()
    63  	if !s.closed {
    64  		s.closed = true
    65  		close(s.ch)
    66  	}
    67  	s.locker.Unlock()
    68  }
    69  
    70  func (s *sch) get() <-chan value {
    71  	return s.ch
    72  }
    73  
    74  func New() (p Promise, f Future) {
    75  	var ch *sch
    76  	cached := pool.Get()
    77  	if cached == nil {
    78  		ch = &sch{
    79  			locker: new(sync.Mutex),
    80  			ch:     make(chan value, 1),
    81  			closed: false,
    82  		}
    83  	} else {
    84  		ch = cached.(*sch)
    85  	}
    86  	p = promise{
    87  		ch: ch,
    88  	}
    89  	f = future{
    90  		ch: ch,
    91  	}
    92  	return
    93  }
    94  
    95  func ReleaseUnused(p Promise) {
    96  	pool.Put(p.(promise).ch)
    97  }
    98  
    99  type value struct {
   100  	val any
   101  	err error
   102  }
   103  
   104  type promise struct {
   105  	ch *sch
   106  }
   107  
   108  func (p promise) Succeed(v any) {
   109  	p.ch.send(value{
   110  		val: v,
   111  	})
   112  }
   113  
   114  func (p promise) Failed(err error) {
   115  	if err == nil {
   116  		err = errors.Warning("fns: empty failed result").WithMeta("fns", "futures")
   117  	}
   118  	p.ch.send(value{
   119  		err: err,
   120  	})
   121  }
   122  
   123  func (p promise) Close() {
   124  	p.ch.destroy()
   125  }
   126  
   127  type future struct {
   128  	ch *sch
   129  }
   130  
   131  func (f future) Await(ctx context.Context) (r Result, err error) {
   132  	select {
   133  	case <-ctx.Done():
   134  		f.ch.destroy()
   135  		err = errors.Timeout("fns: get result value from future timeout").WithMeta("fns", "futures")
   136  		break
   137  	case data, ok := <-f.ch.get():
   138  		pool.Put(f.ch)
   139  		if !ok {
   140  			err = errors.Warning("fns: future was closed").WithMeta("fns", "futures")
   141  			break
   142  		}
   143  		if data.err != nil {
   144  			err = data.err
   145  			break
   146  		}
   147  		r = objects.New(data.val)
   148  		break
   149  	}
   150  	return
   151  }
   152  
   153  func Await(ctx context.Context, ff ...Future) (r []any) {
   154  	for _, f := range ff {
   155  		fr, fErr := f.Await(ctx)
   156  		if fErr != nil {
   157  			r = append(r, fErr)
   158  			continue
   159  		}
   160  		r = append(r, fr)
   161  	}
   162  	return
   163  }