Skip to content

Commit d71d15d

Browse files
authored
Merge pull request #1945 from dgageot/board/docker-agent-issues-cb91df7b
Fix listener resource leaks in serve commands
2 parents a3f97d5 + cb7b342 commit d71d15d

4 files changed

Lines changed: 19 additions & 11 deletions

File tree

cmd/root/a2a.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,11 @@ func (f *a2aFlags) runA2ACommand(cmd *cobra.Command, args []string) error {
4242
out := cli.NewPrinter(cmd.OutOrStdout())
4343
agentFilename := args[0]
4444

45-
ln, err := listenAndCloseOnCancel(ctx, f.listenAddr)
45+
ln, cleanup, err := newListener(ctx, f.listenAddr)
4646
if err != nil {
4747
return err
4848
}
49+
defer cleanup()
4950

5051
out.Println("Listening on", ln.Addr().String())
5152
return a2a.Run(ctx, agentFilename, f.agentName, &f.runConfig, ln)

cmd/root/api.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,11 @@ func (f *apiFlags) runAPICommand(cmd *cobra.Command, args []string) error {
8686
return fmt.Errorf("--pull-interval flag can only be used with OCI references, not local files")
8787
}
8888

89-
ln, err := listenAndCloseOnCancel(ctx, f.listenAddr)
89+
ln, lnCleanup, err := newListener(ctx, f.listenAddr)
9090
if err != nil {
9191
return err
9292
}
93+
defer lnCleanup()
9394

9495
out.Println("Listening on", ln.Addr().String())
9596

cmd/root/flags.go

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -129,16 +129,21 @@ func parseModelShorthand(s string) *latest.ModelConfig {
129129
return nil
130130
}
131131

132-
// listenAndCloseOnCancel starts a listener and spawns a goroutine
133-
// that closes it when the context is cancelled.
134-
func listenAndCloseOnCancel(ctx context.Context, addr string) (net.Listener, error) {
132+
// newListener creates a TCP listener and returns a cleanup function that
133+
// must be deferred by the caller. The cleanup function closes the listener.
134+
// The listener is also closed if the context is cancelled, which unblocks
135+
// any in-progress Serve call.
136+
func newListener(ctx context.Context, addr string) (net.Listener, func(), error) {
135137
ln, err := server.Listen(ctx, addr)
136138
if err != nil {
137-
return nil, fmt.Errorf("failed to listen on %s: %w", addr, err)
139+
return nil, nil, fmt.Errorf("failed to listen on %s: %w", addr, err)
138140
}
139-
go func() {
140-
<-ctx.Done()
141+
stop := context.AfterFunc(ctx, func() {
141142
_ = ln.Close()
142-
}()
143-
return ln, nil
143+
})
144+
cleanup := func() {
145+
stop()
146+
_ = ln.Close()
147+
}
148+
return ln, cleanup, nil
144149
}

cmd/root/mcp.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,11 @@ func (f *mcpFlags) runMCPCommand(cmd *cobra.Command, args []string) error {
4848
return mcp.StartMCPServer(ctx, agentFilename, f.agentName, &f.runConfig)
4949
}
5050

51-
ln, err := listenAndCloseOnCancel(ctx, f.listenAddr)
51+
ln, cleanup, err := newListener(ctx, f.listenAddr)
5252
if err != nil {
5353
return err
5454
}
55+
defer cleanup()
5556

5657
return mcp.StartHTTPServer(ctx, agentFilename, f.agentName, &f.runConfig, ln)
5758
}

0 commit comments

Comments
 (0)