Skip to content

Commit

Permalink
opt: support writev in eventloop.loopWrite()
Browse files Browse the repository at this point in the history
  • Loading branch information
panjf2000 committed Jul 7, 2021
1 parent b38ab75 commit f299a8e
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 20 deletions.
7 changes: 7 additions & 0 deletions errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,11 @@ var (
ErrUnsupportedLength = errors.New("unsupported lengthFieldLength. (expected: 1, 2, 3, 4, or 8)")
// ErrTooLessLength occurs when adjusted frame length is less than zero.
ErrTooLessLength = errors.New("adjusted frame length is less than zero")

// ================================================ internal errors ================================================

// ErrShortWritev occurs when internal/io.Writev fails to send all data.
ErrShortWritev = errors.New("short writev")
// ErrShortReadv occurs when internal/io.Writev fails to send all data.
ErrShortReadv = errors.New("short readv")
)
28 changes: 10 additions & 18 deletions eventloop_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,13 @@ import (
"time"
"unsafe"

"golang.org/x/sys/unix"

gerrors "github.com/panjf2000/gnet/errors"
"github.com/panjf2000/gnet/internal/io"
"github.com/panjf2000/gnet/internal/logging"
"github.com/panjf2000/gnet/internal/netpoll"
"github.com/panjf2000/gnet/internal/socket"
"golang.org/x/sys/unix"
)

type eventloop struct {
Expand Down Expand Up @@ -180,24 +182,14 @@ func (el *eventloop) loopWrite(c *conn) error {
el.eventHandler.PreWrite()

head, tail := c.outboundBuffer.LazyReadAll()
n, err := unix.Write(c.fd, head)
if err != nil {
if err == unix.EAGAIN {
return nil
}
return el.loopCloseConn(c, os.NewSyscallError("write", err))
}
n, err := io.Writev(c.fd, [][]byte{head, tail})
c.outboundBuffer.Shift(n)

if n == len(head) && tail != nil {
n, err = unix.Write(c.fd, tail)
if err != nil {
if err == unix.EAGAIN {
return nil
}
return el.loopCloseConn(c, os.NewSyscallError("write", err))
}
c.outboundBuffer.Shift(n)
switch err {
case nil, gerrors.ErrShortWritev: // do nothing, just go on
case unix.EAGAIN:
return nil
default:
return el.loopCloseConn(c, os.NewSyscallError("write", err))
}

// All data have been drained, it's no need to monitor the writable events,
Expand Down
5 changes: 3 additions & 2 deletions gnet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,12 @@ import (
"testing"
"time"

"go.uber.org/zap"
"go.uber.org/zap/zapcore"

"github.com/panjf2000/gnet/errors"
"github.com/panjf2000/gnet/pool/bytebuffer"
"github.com/panjf2000/gnet/pool/goroutine"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)

func TestCodecServe(t *testing.T) {
Expand Down
67 changes: 67 additions & 0 deletions internal/io/io_bsd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (c) 2021 Andy Pan
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

// +build freebsd dragonfly darwin

package io

import (
"golang.org/x/sys/unix"

"github.com/panjf2000/gnet/errors"
)

// Writev simply calls write() multiple times cuz writev() on BSD-like OS's is not yet implemented in Go.
func Writev(fd int, iov [][]byte) (int, error) {
var sum int
for i := range iov {
n, err := unix.Write(fd, iov[i])
if err != nil {
if sum == 0 {
sum = n
}
return sum, err
}
sum += n
if n < len(iov[i]) {
return sum, errors.ErrShortWritev
}
}
return sum, nil
}

// Readv simply calls read() multiple times cuz readv() on BSD-like OS's is not yet implemented in Go.
func Readv(fd int, iov [][]byte) (int, error) {
var sum int
for i := range iov {
n, err := unix.Read(fd, iov[i])
if err != nil {
if sum == 0 {
sum = n
}
return sum, err
}
sum += n
if n < len(iov[i]) {
return sum, errors.ErrShortReadv
}
}
return sum, nil
}
35 changes: 35 additions & 0 deletions internal/io/io_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright (c) 2021 Andy Pan
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

// +build linux

package io

import "golang.org/x/sys/unix"

// Writev calls writev() on Linux.
func Writev(fd int, iov [][]byte) (int, error) {
return unix.Writev(fd, iov)
}

// Readv calls readv() on Linux.
func Readv(fd int, iov [][]byte) (int, error) {
return unix.Readv(fd, iov)
}

0 comments on commit f299a8e

Please sign in to comment.