fix: clean shutdown without connection errors
- Use read deadline + context check instead of forcibly closing connections - ReadBypassPending now takes context and exits cleanly on cancellation - Remove forced conn.Close() loop from Kill() - context cancellation suffices
This commit is contained in:
parent
9057d36f05
commit
85435447bf
|
|
@ -135,7 +135,7 @@ func (s *Session) runBypassLoop(notifyFn BypassNotifyFunc) {
|
|||
}
|
||||
}
|
||||
|
||||
req, err := conn.ReadBypassPending()
|
||||
req, err := conn.ReadBypassPending(s.Ctx)
|
||||
conn.Close()
|
||||
if err != nil {
|
||||
select {
|
||||
|
|
|
|||
|
|
@ -198,8 +198,18 @@ func (s *Server) Start(conn net.Conn) {
|
|||
}()
|
||||
|
||||
for {
|
||||
// Set read deadline so we can check for shutdown periodically
|
||||
conn.SetReadDeadline(time.Now().Add(1 * time.Second))
|
||||
fc, err := plan9.ReadFcall(conn)
|
||||
if err != nil {
|
||||
// Check if shutdown was requested
|
||||
if connCtx.Err() != nil {
|
||||
break
|
||||
}
|
||||
// Timeout - loop and check context again
|
||||
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
|
||||
continue
|
||||
}
|
||||
if err != io.EOF {
|
||||
s.log.Error("read: %v", err)
|
||||
}
|
||||
|
|
@ -704,11 +714,6 @@ func remove(root *fs.Tree, cs *connState, fc *plan9.Fcall) *plan9.Fcall {
|
|||
|
||||
func (s *Server) Kill() {
|
||||
s.shutdownCancel()
|
||||
s.connMu.Lock()
|
||||
for conn := range s.activeConns {
|
||||
conn.Close()
|
||||
}
|
||||
s.connMu.Unlock()
|
||||
session.Shutdown()
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
|
|
|
|||
|
|
@ -551,14 +551,25 @@ type BypassRequest struct {
|
|||
|
||||
// ReadBypassPending blocks until a bypass request is available.
|
||||
// This should be called in a loop by the approval handler.
|
||||
func (c *Conn) ReadBypassPending() (*BypassRequest, error) {
|
||||
// Returns nil, ctx.Err() if context is cancelled.
|
||||
func (c *Conn) ReadBypassPending(ctx context.Context) (*BypassRequest, error) {
|
||||
fid, err := c.fsys.Open("bypass/pending", plan9.OREAD)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open bypass/pending: %w", err)
|
||||
}
|
||||
defer fid.Close()
|
||||
|
||||
// Blocking read - will return when a request is available
|
||||
// Watch for context cancellation to unblock the read
|
||||
done := make(chan struct{})
|
||||
defer close(done)
|
||||
go func() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
fid.Close()
|
||||
case <-done:
|
||||
}
|
||||
}()
|
||||
|
||||
// Blocking read - will return when a request is available or fid is closed
|
||||
var result []byte
|
||||
buf := make([]byte, 8192)
|
||||
for {
|
||||
|
|
@ -570,9 +581,14 @@ func (c *Conn) ReadBypassPending() (*BypassRequest, error) {
|
|||
break
|
||||
}
|
||||
if err != nil {
|
||||
fid.Close()
|
||||
if ctx.Err() != nil {
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
return nil, fmt.Errorf("read bypass/pending: %w", err)
|
||||
}
|
||||
}
|
||||
fid.Close()
|
||||
|
||||
var req BypassRequest
|
||||
if err := json.Unmarshal(result, &req); err != nil {
|
||||
|
|
|
|||
Loading…
Reference in New Issue