diff --git a/common/turbotunnel/queuepacketconn.go b/common/turbotunnel/queuepacketconn.go index 14a98331f36fdedcc2d1386e18626148a3c4941d..e11f8d8ea1f1be0c1d5d7b0ef42fa720e7d62bf8 100644 --- a/common/turbotunnel/queuepacketconn.go +++ b/common/turbotunnel/queuepacketconn.go @@ -42,8 +42,9 @@ func NewQueuePacketConn(localAddr net.Addr, timeout time.Duration) *QueuePacketC } } -// QueueIncoming queues and incoming packet and its source address, to be -// returned in a future call to ReadFrom. +// QueueIncoming queues an incoming packet and its source address, to be +// returned in a future call to ReadFrom. This function takes ownership of p and +// the caller must not reuse it. func (c *QueuePacketConn) QueueIncoming(p []byte, addr net.Addr) { select { case <-c.closed: @@ -51,11 +52,8 @@ func (c *QueuePacketConn) QueueIncoming(p []byte, addr net.Addr) { return default: } - // Copy the slice so that the caller may reuse it. - buf := make([]byte, len(p)) - copy(buf, p) select { - case c.recvQueue <- taggedPacket{buf, addr}: + case c.recvQueue <- taggedPacket{p, addr}: default: // Drop the incoming packet if the receive queue is full. } @@ -84,22 +82,20 @@ func (c *QueuePacketConn) ReadFrom(p []byte) (int, net.Addr, error) { } // WriteTo queues an outgoing packet for the given address. The queue can later -// be retrieved using the OutgoingQueue method. +// be retrieved using the OutgoingQueue method. This function takes ownership of +// p and the caller must not reuse it. func (c *QueuePacketConn) WriteTo(p []byte, addr net.Addr) (int, error) { select { case <-c.closed: return 0, &net.OpError{Op: "write", Net: c.LocalAddr().Network(), Addr: c.LocalAddr(), Err: c.err.Load().(error)} default: } - // Copy the slice so that the caller may reuse it. - buf := make([]byte, len(p)) - copy(buf, p) select { - case c.clients.SendQueue(addr) <- buf: - return len(buf), nil + case c.clients.SendQueue(addr) <- p: + return len(p), nil default: // Drop the outgoing packet if the send queue is full. - return len(buf), nil + return len(p), nil } } diff --git a/common/turbotunnel/queuepacketconn_test.go b/common/turbotunnel/queuepacketconn_test.go new file mode 100644 index 0000000000000000000000000000000000000000..49ab6aaecdacd34baf83422cfd8d600721557d87 --- /dev/null +++ b/common/turbotunnel/queuepacketconn_test.go @@ -0,0 +1,43 @@ +package turbotunnel + +import ( + "testing" + "time" +) + +type emptyAddr struct{} + +func (_ emptyAddr) Network() string { return "empty" } +func (_ emptyAddr) String() string { return "empty" } + +// Run with -benchmem to see memory allocations. +func BenchmarkQueueIncoming(b *testing.B) { + conn := NewQueuePacketConn(emptyAddr{}, 1*time.Hour) + defer conn.Close() + + b.ResetTimer() + s := 500 + for i := 0; i < b.N; i++ { + // Use a variable for the length to stop the compiler from + // optimizing out the allocation. + p := make([]byte, s) + conn.QueueIncoming(p, emptyAddr{}) + } + b.StopTimer() +} + +// BenchmarkWriteTo benchmarks the QueuePacketConn.WriteTo function. +func BenchmarkWriteTo(b *testing.B) { + conn := NewQueuePacketConn(emptyAddr{}, 1*time.Hour) + defer conn.Close() + + b.ResetTimer() + s := 500 + for i := 0; i < b.N; i++ { + // Use a variable for the length to stop the compiler from + // optimizing out the allocation. + p := make([]byte, s) + conn.WriteTo(p, emptyAddr{}) + } + b.StopTimer() +}