Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 9c334ed

Browse files
cmaglieumbynos
andauthoredAug 10, 2022
Fix gRPC BoardList* methods concurrency issues (#1804)
* Improved streaming of pluggable-discoveries events (WIP) Now the DiscoveryManager is able to start the discoveries and add/remove them in a thread-safe way. Also the watchers may connect and disconnect seamlessly at any time, the incoming events from the discovery are broadcasted correctly to each active watcher. This refactoring dramatically simplifies the DiscoveryManager design. * Added discovery id in discovery.Event struct * Cache active ports and transmit them when a new watcher connects * Correctly handle discovery cleanup * Fixed wrong test * Correctly handle discovery cleanup and re-add * Added some doc comments in the source code * Move Unlock under defer * Factored subrotuine into a function it will be useful in the next commits. * Do not cache ports in the DiscoveryClient there is already a cache in the DiscoveryManager there is no need to duplicate it. * Discovery: eventChan must be protected by mutex when doing START_SYNC otherwise the discovery may send some events before the eventChan is setup (and those events will be lost) * Increased error level for logging watchers that lags * Updated discvoery_client to the latest API * Report discovery start errors * Update arduino/discovery/discovery_client/main.go Co-authored-by: Umberto Baldi <[email protected]> Co-authored-by: Umberto Baldi <[email protected]>
1 parent 312cfdb commit 9c334ed

File tree

10 files changed

+289
-404
lines changed

10 files changed

+289
-404
lines changed
 

‎arduino/cores/packagemanager/package_manager_test.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -329,16 +329,14 @@ func TestPackageManagerClear(t *testing.T) {
329329
packageManager := packagemanager.NewPackageManager(customHardware, customHardware, customHardware, customHardware, "test")
330330
packageManager.LoadHardwareFromDirectory(customHardware)
331331

332-
// Creates another PackageManager but don't load the hardware
333-
emptyPackageManager := packagemanager.NewPackageManager(customHardware, customHardware, customHardware, customHardware, "test")
332+
// Check that the hardware is loaded
333+
require.NotEmpty(t, packageManager.Packages)
334334

335-
// Verifies they're not equal
336-
require.NotEqual(t, packageManager, emptyPackageManager)
337-
338-
// Clear the first PackageManager that contains loaded hardware
335+
// Clear the package manager
339336
packageManager.Clear()
340-
// Verifies both PackageManagers are now equal
341-
require.Equal(t, packageManager, emptyPackageManager)
337+
338+
// Check that the hardware is cleared
339+
require.Empty(t, packageManager.Packages)
342340
}
343341

344342
func TestFindToolsRequiredFromPlatformRelease(t *testing.T) {

‎arduino/discovery/discovery.go

Lines changed: 18 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ type PluggableDiscovery struct {
5757
incomingMessagesError error
5858
state int
5959
eventChan chan<- *Event
60-
cachedPorts map[string]*Port
6160
}
6261

6362
type discoveryMessage struct {
@@ -121,8 +120,9 @@ func (p *Port) String() string {
121120

122121
// Event is a pluggable discovery event
123122
type Event struct {
124-
Type string
125-
Port *Port
123+
Type string
124+
Port *Port
125+
DiscoveryID string
126126
}
127127

128128
// New create and connect to the given pluggable discovery
@@ -131,7 +131,6 @@ func New(id string, args ...string) *PluggableDiscovery {
131131
id: id,
132132
processArgs: args,
133133
state: Dead,
134-
cachedPorts: map[string]*Port{},
135134
}
136135
}
137136

@@ -176,9 +175,8 @@ func (disc *PluggableDiscovery) jsonDecodeLoop(in io.Reader, outChan chan<- *dis
176175
return
177176
}
178177
disc.statusMutex.Lock()
179-
disc.cachedPorts[msg.Port.Address+"|"+msg.Port.Protocol] = msg.Port
180178
if disc.eventChan != nil {
181-
disc.eventChan <- &Event{"add", msg.Port}
179+
disc.eventChan <- &Event{"add", msg.Port, disc.GetID()}
182180
}
183181
disc.statusMutex.Unlock()
184182
} else if msg.EventType == "remove" {
@@ -187,9 +185,8 @@ func (disc *PluggableDiscovery) jsonDecodeLoop(in io.Reader, outChan chan<- *dis
187185
return
188186
}
189187
disc.statusMutex.Lock()
190-
delete(disc.cachedPorts, msg.Port.Address+"|"+msg.Port.Protocol)
191188
if disc.eventChan != nil {
192-
disc.eventChan <- &Event{"remove", msg.Port}
189+
disc.eventChan <- &Event{"remove", msg.Port, disc.GetID()}
193190
}
194191
disc.statusMutex.Unlock()
195192
} else {
@@ -276,10 +273,7 @@ func (disc *PluggableDiscovery) killProcess() error {
276273
}
277274
disc.statusMutex.Lock()
278275
defer disc.statusMutex.Unlock()
279-
if disc.eventChan != nil {
280-
close(disc.eventChan)
281-
disc.eventChan = nil
282-
}
276+
disc.stopSync()
283277
disc.state = Dead
284278
logrus.Infof("killed discovery %s process", disc.id)
285279
return nil
@@ -366,13 +360,17 @@ func (disc *PluggableDiscovery) Stop() error {
366360
}
367361
disc.statusMutex.Lock()
368362
defer disc.statusMutex.Unlock()
369-
disc.cachedPorts = map[string]*Port{}
363+
disc.stopSync()
364+
disc.state = Idling
365+
return nil
366+
}
367+
368+
func (disc *PluggableDiscovery) stopSync() {
370369
if disc.eventChan != nil {
370+
disc.eventChan <- &Event{"stop", nil, disc.GetID()}
371371
close(disc.eventChan)
372372
disc.eventChan = nil
373373
}
374-
disc.state = Idling
375-
return nil
376374
}
377375

378376
// Quit terminates the discovery. No more commands can be accepted by the discovery.
@@ -409,6 +407,9 @@ func (disc *PluggableDiscovery) List() ([]*Port, error) {
409407
// The event channel must be consumed as quickly as possible since it may block the
410408
// discovery if it becomes full. The channel size is configurable.
411409
func (disc *PluggableDiscovery) StartSync(size int) (<-chan *Event, error) {
410+
disc.statusMutex.Lock()
411+
defer disc.statusMutex.Unlock()
412+
412413
if err := disc.sendCommand("START_SYNC\n"); err != nil {
413414
return nil, err
414415
}
@@ -423,29 +424,10 @@ func (disc *PluggableDiscovery) StartSync(size int) (<-chan *Event, error) {
423424
return nil, errors.Errorf(tr("communication out of sync, expected '%[1]s', received '%[2]s'"), "OK", msg.Message)
424425
}
425426

426-
disc.statusMutex.Lock()
427-
defer disc.statusMutex.Unlock()
428427
disc.state = Syncing
429-
disc.cachedPorts = map[string]*Port{}
430-
if disc.eventChan != nil {
431-
// In case there is already an existing event channel in use we close it
432-
// before creating a new one.
433-
close(disc.eventChan)
434-
}
428+
// In case there is already an existing event channel in use we close it before creating a new one.
429+
disc.stopSync()
435430
c := make(chan *Event, size)
436431
disc.eventChan = c
437432
return c, nil
438433
}
439-
440-
// ListCachedPorts returns a list of the available ports. The list is a cache of all the
441-
// add/remove events happened from the StartSync call and it will not consume any
442-
// resource from the underliying discovery.
443-
func (disc *PluggableDiscovery) ListCachedPorts() []*Port {
444-
disc.statusMutex.Lock()
445-
defer disc.statusMutex.Unlock()
446-
res := []*Port{}
447-
for _, port := range disc.cachedPorts {
448-
res = append(res, port)
449-
}
450-
return res
451-
}

‎arduino/discovery/discovery_client/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ replace github.com/arduino/arduino-cli => ../../..
77
require (
88
github.com/arduino/arduino-cli v0.0.0-00010101000000-000000000000
99
github.com/gizak/termui/v3 v3.1.0
10+
github.com/sirupsen/logrus v1.4.2
1011
)
1112

1213
require (
@@ -20,7 +21,6 @@ require (
2021
github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d // indirect
2122
github.com/pkg/errors v0.9.1 // indirect
2223
github.com/rivo/uniseg v0.2.0 // indirect
23-
github.com/sirupsen/logrus v1.4.2 // indirect
2424
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125 // indirect
2525
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
2626
golang.org/x/text v0.3.6 // indirect

‎arduino/discovery/discovery_client/main.go

Lines changed: 33 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -21,36 +21,28 @@ import (
2121
"log"
2222
"os"
2323
"sort"
24-
"time"
2524

2625
"github.com/arduino/arduino-cli/arduino/discovery"
26+
"github.com/arduino/arduino-cli/arduino/discovery/discoverymanager"
2727
ui "github.com/gizak/termui/v3"
2828
"github.com/gizak/termui/v3/widgets"
29+
"github.com/sirupsen/logrus"
2930
)
3031

3132
func main() {
32-
discoveries := []*discovery.PluggableDiscovery{}
33-
discEvent := make(chan *discovery.Event)
33+
logrus.SetLevel(logrus.ErrorLevel)
34+
dm := discoverymanager.New()
3435
for _, discCmd := range os.Args[1:] {
35-
disc := discovery.New("", discCmd)
36-
if err := disc.Run(); err != nil {
37-
log.Fatal("Error starting discovery:", err)
38-
}
39-
if err := disc.Start(); err != nil {
40-
log.Fatal("Error starting discovery:", err)
41-
}
42-
eventChan, err := disc.StartSync(10)
43-
if err != nil {
44-
log.Fatal("Error starting discovery:", err)
45-
}
46-
go func() {
47-
for msg := range eventChan {
48-
discEvent <- msg
49-
}
50-
}()
51-
discoveries = append(discoveries, disc)
36+
disc := discovery.New(discCmd, discCmd)
37+
dm.Add(disc)
5238
}
39+
dm.Start()
5340

41+
activePorts := map[string]*discovery.Port{}
42+
watcher, err := dm.Watch()
43+
if err != nil {
44+
log.Fatalf("failed to start discoveries: %v", err)
45+
}
5446
if err := ui.Init(); err != nil {
5547
log.Fatalf("failed to initialize termui: %v", err)
5648
}
@@ -66,15 +58,20 @@ func main() {
6658
updateList := func() {
6759
rows := []string{}
6860
rows = append(rows, "Available ports list:")
69-
for _, disc := range discoveries {
70-
for i, port := range disc.ListCachedPorts() {
71-
rows = append(rows, fmt.Sprintf(" [%04d] Address: %s", i, port.AddressLabel))
72-
rows = append(rows, fmt.Sprintf(" Protocol: %s", port.ProtocolLabel))
73-
keys := port.Properties.Keys()
74-
sort.Strings(keys)
75-
for _, k := range keys {
76-
rows = append(rows, fmt.Sprintf(" %s=%s", k, port.Properties.Get(k)))
77-
}
61+
62+
ids := sort.StringSlice{}
63+
for id := range activePorts {
64+
ids = append(ids, id)
65+
}
66+
ids.Sort()
67+
for _, id := range ids {
68+
port := activePorts[id]
69+
rows = append(rows, fmt.Sprintf("> Address: %s", port.AddressLabel))
70+
rows = append(rows, fmt.Sprintf(" Protocol: %s", port.ProtocolLabel))
71+
keys := port.Properties.Keys()
72+
sort.Strings(keys)
73+
for _, k := range keys {
74+
rows = append(rows, fmt.Sprintf(" %s=%s", k, port.Properties.Get(k)))
7875
}
7976
}
8077
l.Rows = rows
@@ -123,20 +120,16 @@ out:
123120
previousKey = e.ID
124121
}
125122

126-
case <-discEvent:
123+
case ev := <-watcher.Feed():
124+
if ev.Type == "add" {
125+
activePorts[ev.Port.Address+"|"+ev.Port.Protocol] = ev.Port
126+
}
127+
if ev.Type == "remove" {
128+
delete(activePorts, ev.Port.Address+"|"+ev.Port.Protocol)
129+
}
127130
updateList()
128131
}
129132

130133
ui.Render(l)
131134
}
132-
133-
for _, disc := range discoveries {
134-
disc.Quit()
135-
fmt.Println("Discovery QUITed")
136-
for disc.State() == discovery.Alive {
137-
time.Sleep(time.Millisecond)
138-
}
139-
fmt.Println("Discovery correctly terminated")
140-
}
141-
142135
}

‎arduino/discovery/discoverymanager/discoverymanager.go

Lines changed: 176 additions & 191 deletions
Large diffs are not rendered by default.

‎cli/arguments/completion.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ func GetInstallableLibs() []string {
178178
func GetConnectedBoards() []string {
179179
inst := instance.CreateAndInit()
180180

181-
list, _ := board.List(&rpc.BoardListRequest{
181+
list, _, _ := board.List(&rpc.BoardListRequest{
182182
Instance: inst,
183183
})
184184
var res []string

‎cli/arguments/port.go

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -106,31 +106,16 @@ func (p *Port) GetPort(instance *rpc.Instance, sk *sketch.Sketch) (*discovery.Po
106106
return nil, errors.New("invalid instance")
107107
}
108108
dm := pm.DiscoveryManager()
109-
if errs := dm.RunAll(); len(errs) == len(dm.IDs()) {
110-
// All discoveries failed to run, we can't do anything
111-
return nil, fmt.Errorf("%v", errs)
112-
} else if len(errs) > 0 {
113-
// If only some discoveries failed to run just tell the user and go on
114-
for _, err := range errs {
115-
feedback.Error(err)
116-
}
117-
}
118-
eventChan, errs := dm.StartSyncAll()
119-
if len(errs) > 0 {
120-
return nil, fmt.Errorf("%v", errs)
109+
watcher, err := dm.Watch()
110+
if err != nil {
111+
return nil, err
121112
}
122-
123-
defer func() {
124-
// Quit all discoveries at the end.
125-
if errs := dm.QuitAll(); len(errs) > 0 {
126-
logrus.Errorf("quitting discoveries when getting port metadata: %v", errs)
127-
}
128-
}()
113+
defer watcher.Close()
129114

130115
deadline := time.After(p.timeout.Get())
131116
for {
132117
select {
133-
case portEvent := <-eventChan:
118+
case portEvent := <-watcher.Feed():
134119
if portEvent.Type != "add" {
135120
continue
136121
}
@@ -161,7 +146,7 @@ func (p *Port) GetSearchTimeout() time.Duration {
161146
// discovered Port object together with the FQBN. If the port does not match
162147
// exactly 1 board,
163148
func (p *Port) DetectFQBN(inst *rpc.Instance) (string, *rpc.Port) {
164-
detectedPorts, err := board.List(&rpc.BoardListRequest{
149+
detectedPorts, _, err := board.List(&rpc.BoardListRequest{
165150
Instance: inst,
166151
Timeout: p.timeout.Get().Milliseconds(),
167152
})

‎cli/board/list.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,22 +64,26 @@ func runListCommand(cmd *cobra.Command, args []string) {
6464
os.Exit(0)
6565
}
6666

67-
ports, err := board.List(&rpc.BoardListRequest{
67+
ports, discvoeryErrors, err := board.List(&rpc.BoardListRequest{
6868
Instance: inst,
6969
Timeout: timeoutArg.Get().Milliseconds(),
7070
})
7171
if err != nil {
7272
feedback.Errorf(tr("Error detecting boards: %v"), err)
7373
}
74+
for _, err := range discvoeryErrors {
75+
feedback.Errorf(tr("Error starting discovery: %v"), err)
76+
}
7477
feedback.PrintResult(result{ports})
7578
}
7679

7780
func watchList(cmd *cobra.Command, inst *rpc.Instance) {
78-
eventsChan, err := board.Watch(inst.Id, nil)
81+
eventsChan, closeCB, err := board.Watch(inst.Id)
7982
if err != nil {
8083
feedback.Errorf(tr("Error detecting boards: %v"), err)
8184
os.Exit(errorcodes.ErrNetwork)
8285
}
86+
defer closeCB()
8387

8488
// This is done to avoid printing the header each time a new event is received
8589
if feedback.GetFormat() == feedback.Text {

‎commands/board/list.go

Lines changed: 32 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package board
1717

1818
import (
19+
"context"
1920
"encoding/json"
2021
"fmt"
2122
"io/ioutil"
@@ -176,32 +177,21 @@ func identify(pm *packagemanager.PackageManager, port *discovery.Port) ([]*rpc.B
176177
// List returns a list of boards found by the loaded discoveries.
177178
// In case of errors partial results from discoveries that didn't fail
178179
// are returned.
179-
func List(req *rpc.BoardListRequest) (r []*rpc.DetectedPort, e error) {
180+
func List(req *rpc.BoardListRequest) (r []*rpc.DetectedPort, discoveryStartErrors []error, e error) {
180181
pm := commands.GetPackageManager(req.GetInstance().Id)
181182
if pm == nil {
182-
return nil, &arduino.InvalidInstanceError{}
183+
return nil, nil, &arduino.InvalidInstanceError{}
183184
}
184185

185186
dm := pm.DiscoveryManager()
186-
if errs := dm.RunAll(); len(errs) > 0 {
187-
return nil, &arduino.UnavailableError{Message: tr("Error starting board discoveries"), Cause: fmt.Errorf("%v", errs)}
188-
}
189-
if errs := dm.StartAll(); len(errs) > 0 {
190-
return nil, &arduino.UnavailableError{Message: tr("Error starting board discoveries"), Cause: fmt.Errorf("%v", errs)}
191-
}
192-
defer func() {
193-
if errs := dm.StopAll(); len(errs) > 0 {
194-
logrus.Error(errs)
195-
}
196-
}()
187+
discoveryStartErrors = dm.Start()
197188
time.Sleep(time.Duration(req.GetTimeout()) * time.Millisecond)
198189

199190
retVal := []*rpc.DetectedPort{}
200-
ports, errs := pm.DiscoveryManager().List()
201-
for _, port := range ports {
191+
for _, port := range dm.List() {
202192
boards, err := identify(pm, port)
203193
if err != nil {
204-
return nil, err
194+
return nil, discoveryStartErrors, err
205195
}
206196

207197
// boards slice can be empty at this point if neither the cores nor the
@@ -212,92 +202,49 @@ func List(req *rpc.BoardListRequest) (r []*rpc.DetectedPort, e error) {
212202
}
213203
retVal = append(retVal, b)
214204
}
215-
if len(errs) > 0 {
216-
return retVal, &arduino.UnavailableError{Message: tr("Error getting board list"), Cause: fmt.Errorf("%v", errs)}
217-
}
218-
return retVal, nil
205+
return retVal, discoveryStartErrors, nil
219206
}
220207

221208
// Watch returns a channel that receives boards connection and disconnection events.
222-
// The discovery process can be interrupted by sending a message to the interrupt channel.
223-
func Watch(instanceID int32, interrupt <-chan bool) (<-chan *rpc.BoardListWatchResponse, error) {
209+
// It also returns a callback function that must be used to stop and dispose the watch.
210+
func Watch(instanceID int32) (<-chan *rpc.BoardListWatchResponse, func(), error) {
224211
pm := commands.GetPackageManager(instanceID)
225212
dm := pm.DiscoveryManager()
226213

227-
runErrs := dm.RunAll()
228-
if len(runErrs) == len(dm.IDs()) {
229-
// All discoveries failed to run, we can't do anything
230-
return nil, &arduino.UnavailableError{Message: tr("Error starting board discoveries"), Cause: fmt.Errorf("%v", runErrs)}
214+
watcher, err := dm.Watch()
215+
if err != nil {
216+
return nil, nil, err
231217
}
232218

233-
eventsChan, errs := dm.StartSyncAll()
234-
if len(runErrs) > 0 {
235-
errs = append(runErrs, errs...)
236-
}
219+
ctx, cancel := context.WithCancel(context.Background())
220+
go func() {
221+
<-ctx.Done()
222+
watcher.Close()
223+
}()
237224

238225
outChan := make(chan *rpc.BoardListWatchResponse)
239-
240226
go func() {
241227
defer close(outChan)
242-
for _, err := range errs {
243-
outChan <- &rpc.BoardListWatchResponse{
244-
EventType: "error",
245-
Error: err.Error(),
228+
for event := range watcher.Feed() {
229+
port := &rpc.DetectedPort{
230+
Port: event.Port.ToRPC(),
246231
}
247-
}
248-
for {
249-
select {
250-
case event := <-eventsChan:
251-
if event.Type == "quit" {
252-
// The discovery manager has closed its event channel because it's
253-
// quitting all the discovery processes that are running, this
254-
// means that the events channel we're listening from won't receive any
255-
// more events.
256-
// Handling this case is necessary when the board watcher is running and
257-
// the instance being used is reinitialized since that quits all the
258-
// discovery processes and reset the discovery manager. That would leave
259-
// this goroutine listening forever on a "dead" channel and might even
260-
// cause panics.
261-
// This message avoid all this issues.
262-
// It will be the client's task restarting the board watcher if necessary,
263-
// this host won't attempt restarting it.
264-
outChan <- &rpc.BoardListWatchResponse{
265-
EventType: event.Type,
266-
}
267-
return
268-
}
269-
270-
port := &rpc.DetectedPort{
271-
Port: event.Port.ToRPC(),
272-
}
273232

274-
boardsError := ""
275-
if event.Type == "add" {
276-
boards, err := identify(pm, event.Port)
277-
if err != nil {
278-
boardsError = err.Error()
279-
}
280-
port.MatchingBoards = boards
281-
}
282-
outChan <- &rpc.BoardListWatchResponse{
283-
EventType: event.Type,
284-
Port: port,
285-
Error: boardsError,
286-
}
287-
case <-interrupt:
288-
for _, err := range dm.StopAll() {
289-
// Discoveries that return errors have their process
290-
// closed and are removed from the list of discoveries
291-
// in the manager
292-
outChan <- &rpc.BoardListWatchResponse{
293-
EventType: "error",
294-
Error: tr("stopping discoveries: %s", err),
295-
}
233+
boardsError := ""
234+
if event.Type == "add" {
235+
boards, err := identify(pm, event.Port)
236+
if err != nil {
237+
boardsError = err.Error()
296238
}
297-
return
239+
port.MatchingBoards = boards
240+
}
241+
outChan <- &rpc.BoardListWatchResponse{
242+
EventType: event.Type,
243+
Port: port,
244+
Error: boardsError,
298245
}
299246
}
300247
}()
301248

302-
return outChan, nil
249+
return outChan, cancel, nil
303250
}

‎commands/daemon/daemon.go

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,7 @@ import (
3636
"github.com/arduino/arduino-cli/i18n"
3737
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
3838
"github.com/sirupsen/logrus"
39-
"google.golang.org/grpc/codes"
4039
"google.golang.org/grpc/metadata"
41-
"google.golang.org/grpc/status"
4240
)
4341

4442
// ArduinoCoreServerImpl FIXMEDOC
@@ -69,7 +67,7 @@ func (s *ArduinoCoreServerImpl) BoardDetails(ctx context.Context, req *rpc.Board
6967

7068
// BoardList FIXMEDOC
7169
func (s *ArduinoCoreServerImpl) BoardList(ctx context.Context, req *rpc.BoardListRequest) (*rpc.BoardListResponse, error) {
72-
ports, err := board.List(req)
70+
ports, _, err := board.List(req)
7371
if err != nil {
7472
return nil, convertErrorToRPCStatus(err)
7573
}
@@ -109,42 +107,35 @@ func (s *ArduinoCoreServerImpl) BoardListWatch(stream rpc.ArduinoCoreService_Boa
109107
return err
110108
}
111109

112-
interrupt := make(chan bool, 1)
110+
eventsChan, closeWatcher, err := board.Watch(msg.Instance.Id)
111+
if err != nil {
112+
return convertErrorToRPCStatus(err)
113+
}
114+
113115
go func() {
114-
defer close(interrupt)
116+
defer closeWatcher()
115117
for {
116118
msg, err := stream.Recv()
117119
// Handle client closing the stream and eventual errors
118120
if err == io.EOF {
119121
logrus.Info("boards watcher stream closed")
120-
interrupt <- true
121-
return
122-
} else if st, ok := status.FromError(err); ok && st.Code() == codes.Canceled {
123-
logrus.Info("boards watcher interrupted by host")
124122
return
125-
} else if err != nil {
123+
}
124+
if err != nil {
126125
logrus.Infof("interrupting boards watcher: %v", err)
127-
interrupt <- true
128126
return
129127
}
130128

131129
// Message received, does the client want to interrupt?
132130
if msg != nil && msg.Interrupt {
133131
logrus.Info("boards watcher interrupted by client")
134-
interrupt <- msg.Interrupt
135132
return
136133
}
137134
}
138135
}()
139136

140-
eventsChan, err := board.Watch(msg.Instance.Id, interrupt)
141-
if err != nil {
142-
return convertErrorToRPCStatus(err)
143-
}
144-
145137
for event := range eventsChan {
146-
err = stream.Send(event)
147-
if err != nil {
138+
if err := stream.Send(event); err != nil {
148139
logrus.Infof("sending board watch message: %v", err)
149140
}
150141
}

0 commit comments

Comments
 (0)
Please sign in to comment.