Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Try using socket syscalls that accepts a single buffer to improve performance #36371

Merged
merged 7 commits into from
May 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Net.Sockets;
using System.Runtime.InteropServices;

internal static partial class Interop
{
internal static partial class Sys
{
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_Receive")]
internal static extern unsafe Error Receive(SafeHandle socket, byte* buffer, int bufferLen, SocketFlags flags, int* received);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Net.Sockets;
using System.Runtime.InteropServices;

internal static partial class Interop
{
internal static partial class Sys
{
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_Send")]
internal static extern unsafe Error Send(SafeHandle socket, byte* buffer, int bufferLen, SocketFlags flags, int* sent);
}
}
62 changes: 61 additions & 1 deletion src/libraries/Native/Unix/System.Native/pal_networking.c
Original file line number Diff line number Diff line change
Expand Up @@ -1347,6 +1347,34 @@ static int32_t ConvertSocketFlagsPlatformToPal(int platformFlags)
((platformFlags & MSG_CTRUNC) == 0 ? 0 : SocketFlags_MSG_CTRUNC);
}

int32_t SystemNative_Receive(intptr_t socket, void* buffer, int32_t bufferLen, int32_t flags, int32_t* received)
{
if (buffer == NULL || bufferLen < 0 || received == NULL)
{
return Error_EFAULT;
}

int fd = ToFileDescriptor(socket);

int socketFlags;
if (!ConvertSocketFlagsPalToPlatform(flags, &socketFlags))
{
return Error_ENOTSUP;
}

ssize_t res;
while ((res = recv(fd, buffer, (size_t)bufferLen, socketFlags)) < 0 && errno == EINTR);

if (res != -1)
{
*received = (int32_t)res;
return Error_SUCCESS;
}

*received = 0;
return SystemNative_ConvertErrorPlatformToPal(errno);
}

int32_t SystemNative_ReceiveMessage(intptr_t socket, MessageHeader* messageHeader, int32_t flags, int64_t* received)
{
if (messageHeader == NULL || received == NULL || messageHeader->SocketAddressLen < 0 ||
Expand Down Expand Up @@ -1390,6 +1418,38 @@ int32_t SystemNative_ReceiveMessage(intptr_t socket, MessageHeader* messageHeade
return SystemNative_ConvertErrorPlatformToPal(errno);
}

int32_t SystemNative_Send(intptr_t socket, void* buffer, int32_t bufferLen, int32_t flags, int32_t* sent)
{
if (buffer == NULL || bufferLen < 0 || sent == NULL)
{
return Error_EFAULT;
}

int fd = ToFileDescriptor(socket);

int socketFlags;
if (!ConvertSocketFlagsPalToPlatform(flags, &socketFlags))
{
return Error_ENOTSUP;
}

ssize_t res;
#if defined(__APPLE__) && __APPLE__
// possible OSX kernel bug: https://github.com/dotnet/runtime/issues/27221
while ((res = send(fd, buffer, (size_t)bufferLen, socketFlags)) < 0 && (errno == EINTR || errno == EPROTOTYPE));
#else
while ((res = send(fd, buffer, (size_t)bufferLen, socketFlags)) < 0 && errno == EINTR);
#endif
if (res != -1)
{
*sent = (int32_t)res;
return Error_SUCCESS;
}

*sent = 0;
return SystemNative_ConvertErrorPlatformToPal(errno);
}

int32_t SystemNative_SendMessage(intptr_t socket, MessageHeader* messageHeader, int32_t flags, int64_t* sent)
{
if (messageHeader == NULL || sent == NULL || messageHeader->SocketAddressLen < 0 ||
Expand All @@ -1411,7 +1471,7 @@ int32_t SystemNative_SendMessage(intptr_t socket, MessageHeader* messageHeader,

ssize_t res;
#if defined(__APPLE__) && __APPLE__
// possible OSX kernel bug: #31927
// possible OSX kernel bug: https://github.com/dotnet/runtime/issues/27221
while ((res = sendmsg(fd, &header, socketFlags)) < 0 && (errno == EINTR || errno == EPROTOTYPE));
#else
while ((res = sendmsg(fd, &header, socketFlags)) < 0 && errno == EINTR);
Expand Down
4 changes: 4 additions & 0 deletions src/libraries/Native/Unix/System.Native/pal_networking.h
Original file line number Diff line number Diff line change
Expand Up @@ -371,8 +371,12 @@ PALEXPORT int32_t SystemNative_SetReceiveTimeout(intptr_t socket, int32_t millis

PALEXPORT int32_t SystemNative_SetSendTimeout(intptr_t socket, int32_t millisecondsTimeout);

PALEXPORT int32_t SystemNative_Receive(intptr_t socket, void* buffer, int32_t bufferLen, int32_t flags, int32_t* received);

PALEXPORT int32_t SystemNative_ReceiveMessage(intptr_t socket, MessageHeader* messageHeader, int32_t flags, int64_t* received);

PALEXPORT int32_t SystemNative_Send(intptr_t socket, void* buffer, int32_t bufferLen, int32_t flags, int32_t* sent);

PALEXPORT int32_t SystemNative_SendMessage(intptr_t socket, MessageHeader* messageHeader, int32_t flags, int64_t* sent);

PALEXPORT int32_t SystemNative_Accept(intptr_t socket, uint8_t* socketAddress, int32_t* socketAddressLen, intptr_t* acceptedSocket);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,12 @@
Link="Common\Interop\Unix\System.Native\Interop.Poll.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.PlatformSocketSupport.cs"
Link="Common\Interop\Unix\System.Native\Interop.PlatformSocketSupport.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.Receive.cs"
Link="Common\Interop\Unix\System.Native\Interop.Receive.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.ReceiveMessage.cs"
Link="Common\Interop\Unix\System.Native\Interop.ReceiveMessage.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.Send.cs"
Link="Common\Interop\Unix\System.Native\Interop.Send.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.SendMessage.cs"
Link="Common\Interop\Unix\System.Native\Interop.SendMessage.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.SetSockOpt.cs"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1595,7 +1595,7 @@ public int Receive(IList<ArraySegment<byte>> buffers, SocketFlags socketFlags, o
if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"SRC:{LocalEndPoint} DST:{RemoteEndPoint}");

int bytesTransferred;
errorCode = SocketPal.Receive(_handle, buffers, ref socketFlags, out bytesTransferred);
errorCode = SocketPal.Receive(_handle, buffers, socketFlags, out bytesTransferred);
stephentoub marked this conversation as resolved.
Show resolved Hide resolved

#if TRACE_VERBOSE
if (NetEventSource.IsEnabled)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@ private abstract class ReceiveOperation : ReadOperation
private sealed class BufferMemoryReceiveOperation : ReceiveOperation
{
public Memory<byte> Buffer;
public bool SetReceivedFlags;

public BufferMemoryReceiveOperation(SocketAsyncContext context) : base(context) { }

Expand All @@ -479,7 +480,17 @@ protected override bool DoTryComplete(SocketAsyncContext context)
}
else
{
return SocketPal.TryCompleteReceiveFrom(context._socket, Buffer.Span, null, Flags, SocketAddress, ref SocketAddressLen, out BytesTransferred, out ReceivedFlags, out ErrorCode);
if (!SetReceivedFlags)
{
Debug.Assert(SocketAddress == null);

ReceivedFlags = SocketFlags.None;
return SocketPal.TryCompleteReceive(context._socket, Buffer.Span, Flags, out BytesTransferred, out ErrorCode);
}
else
{
return SocketPal.TryCompleteReceiveFrom(context._socket, Buffer.Span, null, Flags, SocketAddress, ref SocketAddressLen, out BytesTransferred, out ReceivedFlags, out ErrorCode);
}
}
}

Expand Down Expand Up @@ -1465,13 +1476,13 @@ public SocketError ConnectAsync(byte[] socketAddress, int socketAddressLen, Acti
return SocketError.IOPending;
}

public SocketError Receive(Memory<byte> buffer, ref SocketFlags flags, int timeout, out int bytesReceived)
public SocketError Receive(Memory<byte> buffer, SocketFlags flags, int timeout, out int bytesReceived)
{
int socketAddressLen = 0;
return ReceiveFrom(buffer, ref flags, null, ref socketAddressLen, timeout, out bytesReceived);
}

public SocketError Receive(Span<byte> buffer, ref SocketFlags flags, int timeout, out int bytesReceived)
public SocketError Receive(Span<byte> buffer, SocketFlags flags, int timeout, out int bytesReceived)
{
int socketAddressLen = 0;
return ReceiveFrom(buffer, ref flags, null, ref socketAddressLen, timeout, out bytesReceived);
Expand Down Expand Up @@ -1545,6 +1556,39 @@ public unsafe SocketError ReceiveFrom(Span<byte> buffer, ref SocketFlags flags,
}
}

public SocketError ReceiveAsync(Memory<byte> buffer, SocketFlags flags, out int bytesReceived, Action<int, byte[]?, int, SocketFlags, SocketError> callback, CancellationToken cancellationToken = default)
{
SetNonBlocking();

SocketError errorCode;
int observedSequenceNumber;
if (_receiveQueue.IsReady(this, out observedSequenceNumber) &&
SocketPal.TryCompleteReceive(_socket, buffer.Span, flags, out bytesReceived, out errorCode))
{
return errorCode;
}

BufferMemoryReceiveOperation operation = RentBufferMemoryReceiveOperation();
operation.SetReceivedFlags = false;
operation.Callback = callback;
operation.Buffer = buffer;
operation.Flags = flags;
operation.SocketAddress = null;
operation.SocketAddressLen = 0;

if (!_receiveQueue.StartAsyncOperation(this, operation, observedSequenceNumber, cancellationToken))
{
bytesReceived = operation.BytesTransferred;
errorCode = operation.ErrorCode;

ReturnOperation(operation);
return errorCode;
}

bytesReceived = 0;
return SocketError.IOPending;
}

public SocketError ReceiveFromAsync(Memory<byte> buffer, SocketFlags flags, byte[]? socketAddress, ref int socketAddressLen, out int bytesReceived, out SocketFlags receivedFlags, Action<int, byte[]?, int, SocketFlags, SocketError> callback, CancellationToken cancellationToken = default)
{
SetNonBlocking();
Expand All @@ -1558,6 +1602,7 @@ public SocketError ReceiveFromAsync(Memory<byte> buffer, SocketFlags flags, byt
}

BufferMemoryReceiveOperation operation = RentBufferMemoryReceiveOperation();
operation.SetReceivedFlags = true;
operation.Callback = callback;
operation.Buffer = buffer;
operation.Flags = flags;
Expand All @@ -1579,7 +1624,7 @@ public SocketError ReceiveFromAsync(Memory<byte> buffer, SocketFlags flags, byt
return SocketError.IOPending;
}

public SocketError Receive(IList<ArraySegment<byte>> buffers, ref SocketFlags flags, int timeout, out int bytesReceived)
public SocketError Receive(IList<ArraySegment<byte>> buffers, SocketFlags flags, int timeout, out int bytesReceived)
{
return ReceiveFrom(buffers, ref flags, null, 0, timeout, out bytesReceived);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,17 @@ internal unsafe SocketError DoOperationReceive(SafeSocketHandle handle, Cancella
SocketError errorCode;
if (_bufferList == null)
{
errorCode = handle.AsyncContext.ReceiveAsync(_buffer.Slice(_offset, _count), _socketFlags, out bytesReceived, out flags, TransferCompletionCallback, cancellationToken);
// TCP has no out-going receive flags. We can use different syscalls which give better performance.
bool noReceivedFlags = _currentSocket!.ProtocolType == ProtocolType.Tcp;
if (noReceivedFlags)
{
errorCode = handle.AsyncContext.ReceiveAsync(_buffer.Slice(_offset, _count), _socketFlags, out bytesReceived, TransferCompletionCallback, cancellationToken);
flags = SocketFlags.None;
}
else
{
errorCode = handle.AsyncContext.ReceiveAsync(_buffer.Slice(_offset, _count), _socketFlags, out bytesReceived, out flags, TransferCompletionCallback, cancellationToken);
}
}
else
{
Expand Down
Loading