Skip to content

Commit

Permalink
Optimize the events handling for epoll
Browse files Browse the repository at this point in the history
  • Loading branch information
panjf2000 committed Apr 17, 2024
1 parent f934133 commit e299975
Show file tree
Hide file tree
Showing 5 changed files with 16 additions and 26 deletions.
20 changes: 8 additions & 12 deletions connection_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,16 @@

package gnet

import "github.com/panjf2000/gnet/v2/internal/netpoll"
import (
"golang.org/x/sys/unix"

func (c *conn) handleEvents(_ int, ev uint32) error {
// Don't change the ordering of processing EPOLLOUT | EPOLLRDHUP / EPOLLIN unless you're 100%
// sure what you're doing!
// Re-ordering can easily introduce bugs and bad side-effects, as I found out painfully in the past.
"github.com/panjf2000/gnet/v2/internal/netpoll"
)

// We should always check for the EPOLLOUT event first, as we must try to send the leftover data back to
// the peer when any error occurs on a connection.
//
// Either an EPOLLOUT or EPOLLERR event may be fired when a connection is refused.
// In either case write() should take care of it properly:
// 1) writing data back,
// 2) closing the connection.
func (c *conn) handleEvents(_ int, ev uint32) error {
if ev&netpoll.ErrEvents != 0 && ev&netpoll.InEvents == 0 && ev&netpoll.OutEvents == 0 {
return c.loop.close(c, unix.ECONNRESET)

Check warning on line 28 in connection_linux.go

View check run for this annotation

Codecov / codecov/patch

connection_linux.go#L28

Added line #L28 was not covered by tests
}
if ev&netpoll.OutEvents != 0 && !c.outboundBuffer.IsEmpty() {
if err := c.loop.write(c); err != nil {
return err
Expand Down
2 changes: 1 addition & 1 deletion internal/netpoll/epoll_default_poller.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ func (p *Poller) Polling(callback PollEventHandler) error {
}

const (
readEvents = unix.EPOLLPRI | unix.EPOLLIN
readEvents = unix.EPOLLIN | unix.EPOLLPRI | unix.EPOLLRDHUP
writeEvents = unix.EPOLLOUT
readWriteEvents = readEvents | writeEvents
)
Expand Down
2 changes: 1 addition & 1 deletion internal/netpoll/epoll_events.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const (
// reading/writing from/to a closed socket, etc.
ErrEvents = unix.EPOLLERR | unix.EPOLLHUP | unix.EPOLLRDHUP
// OutEvents combines EPOLLOUT event and some exceptional events.
OutEvents = ErrEvents | unix.EPOLLOUT
OutEvents = unix.EPOLLERR | unix.EPOLLHUP | unix.EPOLLOUT
// InEvents combines EPOLLIN/EPOLLPRI events and some exceptional events.
InEvents = ErrEvents | unix.EPOLLIN | unix.EPOLLPRI
)
Expand Down
2 changes: 1 addition & 1 deletion internal/netpoll/epoll_optimized_poller.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ func (p *Poller) Polling() error {
}

const (
readEvents = unix.EPOLLPRI | unix.EPOLLIN
readEvents = unix.EPOLLIN | unix.EPOLLPRI | unix.EPOLLRDHUP
writeEvents = unix.EPOLLOUT
readWriteEvents = readEvents | writeEvents
)
Expand Down
16 changes: 5 additions & 11 deletions reactor_default_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ package gnet
import (
"runtime"

"golang.org/x/sys/unix"

"github.com/panjf2000/gnet/v2/internal/netpoll"
"github.com/panjf2000/gnet/v2/pkg/errors"
)
Expand Down Expand Up @@ -96,17 +98,9 @@ func (el *eventloop) run() error {

err := el.poller.Polling(func(fd int, ev uint32) error {
if c := el.connections.getConn(fd); c != nil {
// Don't change the ordering of processing EPOLLOUT | EPOLLRDHUP / EPOLLIN unless you're 100%
// sure what you're doing!
// Re-ordering can easily introduce bugs and bad side-effects, as I found out painfully in the past.

// We should always check for the EPOLLOUT event first, as we must try to send the leftover data back to
// the peer when any error occurs on a connection.
//
// Either an EPOLLOUT or EPOLLERR event may be fired when a connection is refused.
// In either case write() should take care of it properly:
// 1) writing data back,
// 2) closing the connection.
if ev&netpoll.ErrEvents != 0 && ev&netpoll.InEvents == 0 && ev&netpoll.OutEvents == 0 {
return el.close(c, unix.ECONNRESET)

Check warning on line 102 in reactor_default_linux.go

View check run for this annotation

Codecov / codecov/patch

reactor_default_linux.go#L102

Added line #L102 was not covered by tests
}
if ev&netpoll.OutEvents != 0 && !c.outboundBuffer.IsEmpty() {
if err := el.write(c); err != nil {
return err
Expand Down

0 comments on commit e299975

Please sign in to comment.