Skip to content

Commit aaa23be

Browse files
authored
[client] Prevent to block channel writing (#3474)
The "runningChan" provides feedback to the UI or any client about whether the service is up and running. If the client exits earlier than when the service successfully starts, then this channel causes a block. - Added timeout for reading the channel to ensure we don't cause blocks for too long for the caller - Modified channel writing operations to be non-blocking
1 parent 6bef474 commit aaa23be

File tree

3 files changed

+23
-25
lines changed

3 files changed

+23
-25
lines changed

client/embed/embed.go

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,11 @@ func (c *Client) Start(startCtx context.Context) error {
134134

135135
// either startup error (permanent backoff err) or nil err (successful engine up)
136136
// TODO: make after-startup backoff err available
137-
run := make(chan error, 1)
137+
run := make(chan struct{}, 1)
138+
clientErr := make(chan error, 1)
138139
go func() {
139140
if err := client.Run(run); err != nil {
140-
run <- err
141+
clientErr <- err
141142
}
142143
}()
143144

@@ -147,13 +148,9 @@ func (c *Client) Start(startCtx context.Context) error {
147148
return fmt.Errorf("stop error after context done. Stop error: %w. Context done: %w", stopErr, startCtx.Err())
148149
}
149150
return startCtx.Err()
150-
case err := <-run:
151-
if err != nil {
152-
if stopErr := client.Stop(); stopErr != nil {
153-
return fmt.Errorf("stop error after failed to startup. Stop error: %w. Start error: %w", stopErr, err)
154-
}
155-
return fmt.Errorf("startup: %w", err)
156-
}
151+
case err := <-clientErr:
152+
return fmt.Errorf("startup: %w", err)
153+
case <-run:
157154
}
158155

159156
c.connect = client

client/internal/connect.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func NewConnectClient(
6161
}
6262

6363
// Run with main logic.
64-
func (c *ConnectClient) Run(runningChan chan error) error {
64+
func (c *ConnectClient) Run(runningChan chan struct{}) error {
6565
return c.run(MobileDependency{}, runningChan)
6666
}
6767

@@ -102,7 +102,7 @@ func (c *ConnectClient) RunOniOS(
102102
return c.run(mobileDependency, nil)
103103
}
104104

105-
func (c *ConnectClient) run(mobileDependency MobileDependency, runningChan chan error) error {
105+
func (c *ConnectClient) run(mobileDependency MobileDependency, runningChan chan struct{}) error {
106106
defer func() {
107107
if r := recover(); r != nil {
108108
rec := c.statusRecorder
@@ -159,7 +159,6 @@ func (c *ConnectClient) run(mobileDependency MobileDependency, runningChan chan
159159
}
160160

161161
defer c.statusRecorder.ClientStop()
162-
runningChanOpen := true
163162
operation := func() error {
164163
// if context cancelled we not start new backoff cycle
165164
if c.isContextCancelled() {
@@ -282,10 +281,11 @@ func (c *ConnectClient) run(mobileDependency MobileDependency, runningChan chan
282281
log.Infof("Netbird engine started, the IP is: %s", peerConfig.GetAddress())
283282
state.Set(StatusConnected)
284283

285-
if runningChan != nil && runningChanOpen {
286-
runningChan <- nil
287-
close(runningChan)
288-
runningChanOpen = false
284+
if runningChan != nil {
285+
select {
286+
case runningChan <- struct{}{}:
287+
default:
288+
}
289289
}
290290

291291
<-engineCtx.Done()

client/server/server.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ func (s *Server) Start() error {
160160
// mechanism to keep the client connected even when the connection is lost.
161161
// we cancel retry if the client receive a stop or down command, or if disable auto connect is configured.
162162
func (s *Server) connectWithRetryRuns(ctx context.Context, config *internal.Config, statusRecorder *peer.Status,
163-
runningChan chan error,
163+
runningChan chan struct{},
164164
) {
165165
backOff := getConnectWithBackoff(ctx)
166166
retryStarted := false
@@ -628,20 +628,21 @@ func (s *Server) Up(callerCtx context.Context, _ *proto.UpRequest) (*proto.UpRes
628628
s.statusRecorder.UpdateManagementAddress(s.config.ManagementURL.String())
629629
s.statusRecorder.UpdateRosenpass(s.config.RosenpassEnabled, s.config.RosenpassPermissive)
630630

631-
runningChan := make(chan error)
632-
go s.connectWithRetryRuns(ctx, s.config, s.statusRecorder, runningChan)
631+
timeoutCtx, cancel := context.WithTimeout(callerCtx, 10*time.Second)
632+
defer cancel()
633633

634+
runningChan := make(chan struct{}, 1) // buffered channel to do not lose the signal
635+
go s.connectWithRetryRuns(ctx, s.config, s.statusRecorder, runningChan)
634636
for {
635637
select {
636-
case err := <-runningChan:
637-
if err != nil {
638-
log.Debugf("waiting for engine to become ready failed: %s", err)
639-
} else {
640-
return &proto.UpResponse{}, nil
641-
}
638+
case <-runningChan:
639+
return &proto.UpResponse{}, nil
642640
case <-callerCtx.Done():
643641
log.Debug("context done, stopping the wait for engine to become ready")
644642
return nil, callerCtx.Err()
643+
case <-timeoutCtx.Done():
644+
log.Debug("up is timed out, stopping the wait for engine to become ready")
645+
return nil, timeoutCtx.Err()
645646
}
646647
}
647648
}

0 commit comments

Comments
 (0)