From c8cba7e2513f3bc839441f59fabb9da5740c7401 Mon Sep 17 00:00:00 2001 From: Lex Li Date: Mon, 21 Nov 2016 12:52:38 +0000 Subject: [PATCH] Started to reuse SocketAsyncEventArgs instances. --- SharpSnmpLib/Messaging/Discoverer.cs | 21 +++--- SharpSnmpLib/Messaging/ListenerBinding.cs | 21 +++--- .../Messaging/SnmpMessageExtension.cs | 28 ++++---- .../Messaging/SocketAsyncEventArgsFactory.cs | 68 +++++++++++++++++++ SharpSnmpLib/Messaging/SocketAwaitable.cs | 34 +++++++++- SharpSnmpLib/Messaging/SocketExtension.cs | 2 + SharpSnmpLib/SharpSnmpLib.Android.csproj | 1 + SharpSnmpLib/SharpSnmpLib.Full.csproj | 1 + SharpSnmpLib/SharpSnmpLib.iOS.csproj | 1 + 9 files changed, 138 insertions(+), 39 deletions(-) create mode 100644 SharpSnmpLib/Messaging/SocketAsyncEventArgsFactory.cs diff --git a/SharpSnmpLib/Messaging/Discoverer.cs b/SharpSnmpLib/Messaging/Discoverer.cs index d68859a4..3eb53bc8 100644 --- a/SharpSnmpLib/Messaging/Discoverer.cs +++ b/SharpSnmpLib/Messaging/Discoverer.cs @@ -334,11 +334,11 @@ public async Task DiscoverAsync(VersionCode version, IPEndPoint broadcastAddress using (var udp = new Socket(addressFamily, SocketType.Dgram, ProtocolType.Udp)) { - using (var info = new SocketAsyncEventArgs()) + var info = SocketExtension.EventArgsFactory.Create(); + info.RemoteEndPoint = broadcastAddress; + info.SetBuffer(bytes, 0, bytes.Length); + using (var awaitable1 = new SocketAwaitable(info)) { - info.RemoteEndPoint = broadcastAddress; - info.SetBuffer(bytes, 0, bytes.Length); - var awaitable1 = new SocketAwaitable(info); await udp.SendToAsync(awaitable1); } @@ -370,13 +370,16 @@ private async Task ReceiveAsync(Socket socket) int count; var reply = new byte[_bufferSize]; - var args = new SocketAsyncEventArgs(); + var args = SocketExtension.EventArgsFactory.Create(); try { args.RemoteEndPoint = remote; args.SetBuffer(reply, 0, _bufferSize); - var awaitable = new SocketAwaitable(args); - count = await socket.ReceiveAsync(awaitable); + using (var awaitable = new SocketAwaitable(args)) + { + count = await socket.ReceiveAsync(awaitable); + } + await Task.Factory.StartNew(() => HandleMessage(reply, count, (IPEndPoint)remote)).ConfigureAwait(false); } catch (SocketException ex) @@ -392,10 +395,6 @@ private async Task ReceiveAsync(Socket socket) } } } - finally - { - args.Dispose(); - } } } } diff --git a/SharpSnmpLib/Messaging/ListenerBinding.cs b/SharpSnmpLib/Messaging/ListenerBinding.cs index 0899dd0e..804010cb 100644 --- a/SharpSnmpLib/Messaging/ListenerBinding.cs +++ b/SharpSnmpLib/Messaging/ListenerBinding.cs @@ -426,14 +426,16 @@ public async Task SendResponseAsync(ISnmpMessage response, EndPoint receiver) } var buffer = response.ToBytes(); - var info = new SocketAsyncEventArgs(); + var info = SocketExtension.EventArgsFactory.Create(); try { info.RemoteEndPoint = receiver; info.SetBuffer(buffer, 0, buffer.Length); - var awaitable1 = new SocketAwaitable(info); - await _socket.SendToAsync(awaitable1); + using (var awaitable1 = new SocketAwaitable(info)) + { + await _socket.SendToAsync(awaitable1); + } } catch (SocketException ex) { @@ -443,10 +445,6 @@ public async Task SendResponseAsync(ISnmpMessage response, EndPoint receiver) throw; } } - finally - { - info.Dispose(); - } } /// @@ -512,13 +510,16 @@ private async Task ReceiveAsync() int count; var reply = new byte[_bufferSize]; - var args = new SocketAsyncEventArgs(); + var args = SocketExtension.EventArgsFactory.Create(); try { args.RemoteEndPoint = remote; args.SetBuffer(reply, 0, _bufferSize); - var awaitable = new SocketAwaitable(args); - count = await _socket.ReceiveAsync(awaitable); + using (var awaitable = new SocketAwaitable(args)) + { + count = await _socket.ReceiveAsync(awaitable); + } + await Task.Factory.StartNew(() => HandleMessage(reply, count, (IPEndPoint)remote)); } catch (SocketException ex) diff --git a/SharpSnmpLib/Messaging/SnmpMessageExtension.cs b/SharpSnmpLib/Messaging/SnmpMessageExtension.cs index 66598de0..c420c618 100644 --- a/SharpSnmpLib/Messaging/SnmpMessageExtension.cs +++ b/SharpSnmpLib/Messaging/SnmpMessageExtension.cs @@ -551,11 +551,11 @@ public static async Task SendAsync(this ISnmpMessage message, EndPoint manager, } var bytes = message.ToBytes(); - using (var info = new SocketAsyncEventArgs()) + var info = SocketExtension.EventArgsFactory.Create(); + info.RemoteEndPoint = manager; + info.SetBuffer(bytes, 0, bytes.Length); + using (var awaitable1 = new SocketAwaitable(info)) { - info.RemoteEndPoint = manager; - info.SetBuffer(bytes, 0, bytes.Length); - var awaitable1 = new SocketAwaitable(info); await socket.SendToAsync(awaitable1); } } @@ -695,11 +695,11 @@ public static async Task GetResponseAsync(this ISnmpMessage reques var bufSize = udpSocket.ReceiveBufferSize = Messenger.MaxMessageSize; // Whatever you change, try to keep the Send and the Receive close to each other. - using (var info = new SocketAsyncEventArgs()) + var info = SocketExtension.EventArgsFactory.Create(); + info.RemoteEndPoint = receiver; + info.SetBuffer(bytes, 0, bytes.Length); + using (var awaitable1 = new SocketAwaitable(info)) { - info.RemoteEndPoint = receiver; - info.SetBuffer(bytes, 0, bytes.Length); - var awaitable1 = new SocketAwaitable(info); await udpSocket.SendToAsync(awaitable1); } @@ -707,14 +707,16 @@ public static async Task GetResponseAsync(this ISnmpMessage reques var reply = new byte[bufSize]; // IMPORTANT: follow http://blogs.msdn.com/b/pfxteam/archive/2011/12/15/10248293.aspx - var args = new SocketAsyncEventArgs(); + var args = SocketExtension.EventArgsFactory.Create(); EndPoint remote = new IPEndPoint(IPAddress.Any, 0); try { args.RemoteEndPoint = remote; args.SetBuffer(reply, 0, bufSize); - var awaitable = new SocketAwaitable(args); - count = await udpSocket.ReceiveAsync(awaitable); + using (var awaitable = new SocketAwaitable(args)) + { + count = await udpSocket.ReceiveAsync(awaitable); + } } catch (SocketException ex) { @@ -733,10 +735,6 @@ public static async Task GetResponseAsync(this ISnmpMessage reques throw; } - finally - { - args.Dispose(); - } // Passing 'count' is not necessary because ParseMessages should ignore it, but it offer extra safety (and would avoid an issue if parsing >1 response). var response = MessageFactory.ParseMessages(reply, 0, count, registry)[0]; diff --git a/SharpSnmpLib/Messaging/SocketAsyncEventArgsFactory.cs b/SharpSnmpLib/Messaging/SocketAsyncEventArgsFactory.cs new file mode 100644 index 00000000..cd5c77ed --- /dev/null +++ b/SharpSnmpLib/Messaging/SocketAsyncEventArgsFactory.cs @@ -0,0 +1,68 @@ +// SNMP application factory class. +// Copyright (C) 2009-2010 Lex Li +// +// 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. + +using System.Collections.Generic; +using System.Net.Sockets; + +namespace Lextm.SharpSnmpLib.Messaging +{ + /// + /// SNMP application factory, who holds all pipeline instances. + /// + public sealed class SocketAsyncEventArgsFactory + { + private readonly object _root = new object(); + private readonly Queue _queue = new Queue(); + + /// + /// Finds an available event args. + /// + /// + public SocketAsyncEventArgs Create() + { + SocketAsyncEventArgs result = null; + lock (_root) + { + if (_queue.Count > 0) + { + result = _queue.Dequeue(); + } + } + + if (result == null) + { + result = new SocketAsyncEventArgs(); + } + + return result; + } + + /// + /// Reuses the specified event args. + /// + /// The resource. + internal void Reuse(SocketAsyncEventArgs args) + { + lock (_root) + { + _queue.Enqueue(args); + } + } + } +} diff --git a/SharpSnmpLib/Messaging/SocketAwaitable.cs b/SharpSnmpLib/Messaging/SocketAwaitable.cs index e888958a..83261d5d 100644 --- a/SharpSnmpLib/Messaging/SocketAwaitable.cs +++ b/SharpSnmpLib/Messaging/SocketAwaitable.cs @@ -6,13 +6,14 @@ using System.Threading; using System.Threading.Tasks; - internal sealed class SocketAwaitable : INotifyCompletion + internal sealed class SocketAwaitable : INotifyCompletion, IDisposable { private readonly static Action SENTINEL = () => { }; internal bool m_wasCompleted; internal Action m_continuation; internal SocketAsyncEventArgs m_eventArgs; + private bool _disposed; public SocketAwaitable(SocketAsyncEventArgs eventArgs) { @@ -21,8 +22,35 @@ public SocketAwaitable(SocketAsyncEventArgs eventArgs) throw new ArgumentNullException(nameof(eventArgs)); } - this.m_eventArgs = eventArgs; - eventArgs.Completed += Completed; + m_eventArgs = eventArgs; + m_eventArgs.Completed += Completed; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + ~SocketAwaitable() + { + Dispose(false); + } + + private void Dispose(bool disposing) + { + if (_disposed) + { + return; + } + + _disposed = true; + if (disposing) + { + m_eventArgs.Completed -= Completed; + SocketExtension.EventArgsFactory.Reuse(m_eventArgs); + m_eventArgs = null; + } } private void Completed(object sender, SocketAsyncEventArgs e) diff --git a/SharpSnmpLib/Messaging/SocketExtension.cs b/SharpSnmpLib/Messaging/SocketExtension.cs index 5a81751f..53600c66 100644 --- a/SharpSnmpLib/Messaging/SocketExtension.cs +++ b/SharpSnmpLib/Messaging/SocketExtension.cs @@ -21,5 +21,7 @@ public static SocketAwaitable SendToAsync(this Socket socket, awaitable.m_wasCompleted = true; return awaitable; } + + internal static readonly SocketAsyncEventArgsFactory EventArgsFactory = new SocketAsyncEventArgsFactory(); } } diff --git a/SharpSnmpLib/SharpSnmpLib.Android.csproj b/SharpSnmpLib/SharpSnmpLib.Android.csproj index 61ace60e..633c0651 100644 --- a/SharpSnmpLib/SharpSnmpLib.Android.csproj +++ b/SharpSnmpLib/SharpSnmpLib.Android.csproj @@ -72,6 +72,7 @@ + diff --git a/SharpSnmpLib/SharpSnmpLib.Full.csproj b/SharpSnmpLib/SharpSnmpLib.Full.csproj index 4f2f2634..99d2c637 100644 --- a/SharpSnmpLib/SharpSnmpLib.Full.csproj +++ b/SharpSnmpLib/SharpSnmpLib.Full.csproj @@ -96,6 +96,7 @@ + diff --git a/SharpSnmpLib/SharpSnmpLib.iOS.csproj b/SharpSnmpLib/SharpSnmpLib.iOS.csproj index 8a7b7602..bb36bf17 100644 --- a/SharpSnmpLib/SharpSnmpLib.iOS.csproj +++ b/SharpSnmpLib/SharpSnmpLib.iOS.csproj @@ -60,6 +60,7 @@ +