github.com/status-im/status-go@v1.1.0/discovery/muxer.go (about)

     1  package discovery
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"sync"
     7  	"time"
     8  
     9  	"github.com/ethereum/go-ethereum/p2p/discv5"
    10  )
    11  
    12  // NewMultiplexer creates Multiplexer instance.
    13  func NewMultiplexer(discoveries []Discovery) Multiplexer {
    14  	return Multiplexer{discoveries}
    15  }
    16  
    17  // Multiplexer allows to use multiple discoveries behind single Discovery interface.
    18  type Multiplexer struct {
    19  	discoveries []Discovery
    20  }
    21  
    22  // Running should return true if at least one discovery is running
    23  func (m Multiplexer) Running() (rst bool) {
    24  	for i := range m.discoveries {
    25  		rst = rst || m.discoveries[i].Running()
    26  	}
    27  	return rst
    28  }
    29  
    30  // Start every discovery and stop every started in case if at least one fails.
    31  func (m Multiplexer) Start() (err error) {
    32  	started := []int{}
    33  	for i := range m.discoveries {
    34  		if err = m.discoveries[i].Start(); err != nil {
    35  			break
    36  		}
    37  		started = append(started, i)
    38  	}
    39  	if err != nil {
    40  		for _, i := range started {
    41  			_ = m.discoveries[i].Stop()
    42  		}
    43  	}
    44  	return err
    45  }
    46  
    47  // Stop every discovery.
    48  func (m Multiplexer) Stop() (err error) {
    49  	messages := []string{}
    50  	for i := range m.discoveries {
    51  		if err = m.discoveries[i].Stop(); err != nil {
    52  			messages = append(messages, err.Error())
    53  		}
    54  	}
    55  	if len(messages) != 0 {
    56  		return fmt.Errorf("failed to stop discoveries: %s", strings.Join(messages, "; "))
    57  	}
    58  	return nil
    59  }
    60  
    61  // Register passed topic and stop channel to every discovery and waits till it will return.
    62  func (m Multiplexer) Register(topic string, stop chan struct{}) error {
    63  	errors := make(chan error, len(m.discoveries))
    64  	for i := range m.discoveries {
    65  		i := i
    66  		go func() {
    67  			errors <- m.discoveries[i].Register(topic, stop)
    68  		}()
    69  	}
    70  	total := 0
    71  	messages := []string{}
    72  	for err := range errors {
    73  		total++
    74  		if err != nil {
    75  			messages = append(messages, err.Error())
    76  		}
    77  		if total == len(m.discoveries) {
    78  			break
    79  		}
    80  	}
    81  	if len(messages) != 0 {
    82  		return fmt.Errorf("failed to register %s: %s", topic, strings.Join(messages, "; "))
    83  	}
    84  	return nil
    85  }
    86  
    87  // Discover shares topic and channles for receiving results. And multiplexer periods that are sent to period channel.
    88  func (m Multiplexer) Discover(topic string, period <-chan time.Duration, found chan<- *discv5.Node, lookup chan<- bool) error {
    89  	var (
    90  		periods  = make([]chan time.Duration, len(m.discoveries))
    91  		messages = []string{}
    92  		wg       sync.WaitGroup
    93  		mu       sync.Mutex
    94  	)
    95  	wg.Add(len(m.discoveries) + 1)
    96  	for i := range m.discoveries {
    97  		i := i
    98  		periods[i] = make(chan time.Duration, 2)
    99  		go func() {
   100  			err := m.discoveries[i].Discover(topic, periods[i], found, lookup)
   101  			if err != nil {
   102  				mu.Lock()
   103  				messages = append(messages, err.Error())
   104  				mu.Unlock()
   105  			}
   106  			wg.Done()
   107  		}()
   108  	}
   109  	go func() {
   110  		for {
   111  			newPeriod, ok := <-period
   112  			for i := range periods {
   113  				if !ok {
   114  					close(periods[i])
   115  				} else {
   116  					periods[i] <- newPeriod
   117  				}
   118  			}
   119  			if !ok {
   120  				wg.Done()
   121  				return
   122  			}
   123  		}
   124  	}()
   125  	wg.Wait()
   126  	if len(messages) != 0 {
   127  		return fmt.Errorf("failed to discover topic %s: %s", topic, strings.Join(messages, "; "))
   128  	}
   129  	return nil
   130  }