github.com/koko1123/flow-go-1@v0.29.6/module/util/util.go (about)

     1  package util
     2  
     3  import (
     4  	"context"
     5  	"reflect"
     6  	"sync"
     7  
     8  	"github.com/koko1123/flow-go-1/module"
     9  )
    10  
    11  // AllReady calls Ready on all input components and returns a channel that is
    12  // closed when all input components are ready.
    13  func AllReady(components ...module.ReadyDoneAware) <-chan struct{} {
    14  	readyChans := make([]<-chan struct{}, len(components))
    15  
    16  	for i, c := range components {
    17  		readyChans[i] = c.Ready()
    18  	}
    19  
    20  	return AllClosed(readyChans...)
    21  }
    22  
    23  // AllDone calls Done on all input components and returns a channel that is
    24  // closed when all input components are done.
    25  func AllDone(components ...module.ReadyDoneAware) <-chan struct{} {
    26  	doneChans := make([]<-chan struct{}, len(components))
    27  
    28  	for i, c := range components {
    29  		doneChans[i] = c.Done()
    30  	}
    31  
    32  	return AllClosed(doneChans...)
    33  }
    34  
    35  // AllClosed returns a channel that is closed when all input channels are closed.
    36  func AllClosed(channels ...<-chan struct{}) <-chan struct{} {
    37  	done := make(chan struct{})
    38  	if len(channels) == 0 {
    39  		close(done)
    40  		return done
    41  	}
    42  
    43  	var wg sync.WaitGroup
    44  
    45  	for _, ch := range channels {
    46  		wg.Add(1)
    47  		go func(ch <-chan struct{}) {
    48  			<-ch
    49  			wg.Done()
    50  		}(ch)
    51  	}
    52  
    53  	go func() {
    54  		wg.Wait()
    55  		close(done)
    56  	}()
    57  
    58  	return done
    59  }
    60  
    61  // WaitClosed waits for either a signal/close on the channel or for the context to be cancelled
    62  // Returns nil if the channel was signalled/closed before returning, otherwise, it returns the context
    63  // error.
    64  //
    65  // This handles the corner case where the context is cancelled at the same time that the channel
    66  // is closed, and the Done case was selected.
    67  // This is intended for situations where ignoring a signal can cause safety issues.
    68  func WaitClosed(ctx context.Context, ch <-chan struct{}) error {
    69  	select {
    70  	case <-ctx.Done():
    71  		select {
    72  		case <-ch:
    73  			return nil
    74  		default:
    75  		}
    76  		return ctx.Err()
    77  	case <-ch:
    78  		return nil
    79  	}
    80  }
    81  
    82  // CheckClosed checks if the provided channel has a signal or was closed.
    83  // Returns true if the channel was signaled/closed, otherwise, returns false.
    84  //
    85  // This is intended to reduce boilerplate code when multiple channel checks are required because
    86  // missed signals could cause safety issues.
    87  func CheckClosed(done <-chan struct{}) bool {
    88  	select {
    89  	case <-done:
    90  		return true
    91  	default:
    92  		return false
    93  	}
    94  }
    95  
    96  // MergeChannels merges a list of channels into a single channel
    97  func MergeChannels(channels interface{}) interface{} {
    98  	sliceType := reflect.TypeOf(channels)
    99  	if sliceType.Kind() != reflect.Slice && sliceType.Kind() != reflect.Array {
   100  		panic("argument must be an array or slice")
   101  	}
   102  	chanType := sliceType.Elem()
   103  	if chanType.ChanDir() == reflect.SendDir {
   104  		panic("channels cannot be send-only")
   105  	}
   106  	c := reflect.ValueOf(channels)
   107  	var cases []reflect.SelectCase
   108  	for i := 0; i < c.Len(); i++ {
   109  		cases = append(cases, reflect.SelectCase{
   110  			Dir:  reflect.SelectRecv,
   111  			Chan: c.Index(i),
   112  		})
   113  	}
   114  	elemType := chanType.Elem()
   115  	out := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, elemType), 0)
   116  	go func() {
   117  		for len(cases) > 0 {
   118  			i, v, ok := reflect.Select(cases)
   119  			if !ok {
   120  				lastIndex := len(cases) - 1
   121  				cases[i], cases[lastIndex] = cases[lastIndex], cases[i]
   122  				cases = cases[:lastIndex]
   123  				continue
   124  			}
   125  			out.Send(v)
   126  		}
   127  		out.Close()
   128  	}()
   129  	return out.Convert(reflect.ChanOf(reflect.RecvDir, elemType)).Interface()
   130  }
   131  
   132  // WaitError waits for either an error on the error channel or the done channel to close
   133  // Returns an error if one is received on the error channel, otherwise it returns nil
   134  //
   135  // This handles a race condition where the done channel could have been closed as a result of an
   136  // irrecoverable error being thrown, so that when the scheduler yields control back to this
   137  // goroutine, both channels are available to read from. If the done case happens to be chosen
   138  // at random to proceed instead of the error case, then we would return without error which could
   139  // result in unsafe continuation.
   140  func WaitError(errChan <-chan error, done <-chan struct{}) error {
   141  	select {
   142  	case err := <-errChan:
   143  		return err
   144  	case <-done:
   145  		select {
   146  		case err := <-errChan:
   147  			return err
   148  		default:
   149  		}
   150  		return nil
   151  	}
   152  }
   153  
   154  // DetypeSlice converts a typed slice containing any kind of elements into an
   155  // untyped []any type, in effect removing the element type information from the slice.
   156  // It is useful for passing data into structpb.NewValue, which accepts []any but not
   157  // []T for any specific type T.
   158  func DetypeSlice[T any](typedSlice []T) []any {
   159  	untypedSlice := make([]any, len(typedSlice))
   160  	for i, t := range typedSlice {
   161  		untypedSlice[i] = t
   162  	}
   163  	return untypedSlice
   164  }