diff --git a/connection_onevent.go b/connection_onevent.go index f8351f32..8f134306 100644 --- a/connection_onevent.go +++ b/connection_onevent.go @@ -177,6 +177,18 @@ func (c *connection) onProcess(isProcessable func(c *connection) bool, process f } // add new task var task = func() { + panicked := true + defer func() { + // cannot use recover() here, since we don't want to break the panic stack + if panicked { + c.unlock(processing) + if c.IsActive() { + c.Close() + } else { + c.closeCallback(false) + } + } + }() START: // `process` must be executed at least once if `isProcessable` in order to cover the `send & close by peer` case. // Then the loop processing must ensure that the connection `IsActive`. @@ -189,6 +201,7 @@ func (c *connection) onProcess(isProcessable func(c *connection) bool, process f // Handling callback if connection has been closed. if !c.IsActive() { c.closeCallback(false) + panicked = false return } c.unlock(processing) @@ -197,6 +210,7 @@ func (c *connection) onProcess(isProcessable func(c *connection) bool, process f goto START } // task exits + panicked = false return } diff --git a/netpoll_test.go b/netpoll_test.go index cedf6226..b97bf176 100644 --- a/netpoll_test.go +++ b/netpoll_test.go @@ -287,6 +287,37 @@ func TestServerReadAndClose(t *testing.T) { MustNil(t, err) } +func TestServerPanicAndClose(t *testing.T) { + var network, address = "tcp", ":18888" + var sendMsg = []byte("hello") + var paniced int32 + var loop = newTestEventLoop(network, address, + func(ctx context.Context, connection Connection) error { + _, err := connection.Reader().Next(len(sendMsg)) + MustNil(t, err) + atomic.StoreInt32(&paniced, 1) + panic("test") + }, + ) + + var conn, err = DialConnection(network, address, time.Second) + MustNil(t, err) + _, err = conn.Writer().WriteBinary(sendMsg) + MustNil(t, err) + err = conn.Writer().Flush() + MustNil(t, err) + + for atomic.LoadInt32(&paniced) == 0 { + runtime.Gosched() // wait for poller close connection + } + for conn.IsActive() { + runtime.Gosched() // wait for poller close connection + } + + err = loop.Shutdown(context.Background()) + MustNil(t, err) +} + func TestClientWriteAndClose(t *testing.T) { var ( network, address = "tcp", ":18889"