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 }