Skip to content

Instantly share code, notes, and snippets.

@MareMare
Last active November 24, 2016 01:09
Show Gist options
  • Save MareMare/1d0876da3f50abdeb0f5720a512962a2 to your computer and use it in GitHub Desktop.
Save MareMare/1d0876da3f50abdeb0f5720a512962a2 to your computer and use it in GitHub Desktop.
#Snmp(v.9.0.3) GetAsync With Cancellation.
using System;
using System.Collections.Generic;
using System.Net;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Lextm.SharpSnmpLib;
using Lextm.SharpSnmpLib.Messaging;
namespace SandboxForGetAsync
{
internal static class Program
{
internal static int Main(string[] args)
{
try
{
return Program.InternalMainAsync(args).Result;
}
catch (Exception ex)
{
Console.Error.WriteLine(ex);
return Marshal.GetHRForException(ex);
}
finally
{
Console.WriteLine("hit any key to exit...");
// shapshot(#2) here on dotMemory GUI.
Console.ReadLine();
}
}
private static async Task<int> InternalMainAsync(string[] args)
{
Console.WriteLine("hit any key to start test...");
// shapshot(#1) here on dotMemory GUI.
Console.ReadLine();
await TestMemory();
return 0;
}
private static async Task TestMemory()
{
// Running this code on 10.161.227.175
// The Snmp Agent on 10.161.227.134, but an agent is not listening...
var message = new GetRequestMessage(0x4bed, VersionCode.V1, new OctetString("public"), new List<Variable> { new Variable(new ObjectIdentifier("1.3.6.1.2.1.1.1.0")) });
for (int i = 0; i < 100; i++)
{
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(0.5))) // timeout 500 msec.
{
try
{
var response = await message.GetResponseAsync(new IPEndPoint(IPAddress.Parse("10.161.227.134"), 161))
.WithCancellation(cts.Token)
.ConfigureAwait(false);
}
catch (Exception ex)
{
Console.WriteLine("{0}", ex.Message);
}
}
}
message = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
#region Task Extension Methods
private static async Task<T> WithCancellation<T>(this Task<T> task, CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<bool>();
using (cancellationToken.Register(state => ((TaskCompletionSource<bool>)state).TrySetResult(true), tcs))
{
var completedTask = await Task.WhenAny(task, tcs.Task).ConfigureAwait(false);
if (task != completedTask)
{
throw new OperationCanceledException(cancellationToken);
}
}
return await task.ConfigureAwait(false);
}
#endregion
}
}

ShapSnmpLib(9.0.3) Result of observing memory test of GetResponseAsync method with dotMemory

The SocketAwaitable class seems to be leaking resources in #SNMP v 9.0.3. I think it is probably caused by strong reference.

So I prepared details that seems to be memory leak. I ran it in the situation where the following timeout occurs:

  • The SNMP agent exists on host B, but it is not running.
  • Running my test code (Console App) on host A.

My test code snippet is below:

private static async Task TestMemory()
{
    // Running this code on host A
    // The Snmp Agent on host B, but an agent is not listening...
    var message = new GetRequestMessage(0x4bed, VersionCode.V1, new OctetString("public"), new List<Variable> { new Variable(new ObjectIdentifier("1.3.6.1.2.1.1.1.0")) });
    for (int i = 0; i < 100; i++)
    {
        using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(0.5))) // timeout 500 msec.
        {
            try
            {
                var response = await message.GetResponseAsync(new IPEndPoint(IPAddress.Parse("IP OF HOST B"), 161))
                    .WithCancellation(cts.Token)
                    .ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                Console.WriteLine("{0}", ex.Message);
            }
        }
    }

    message = null;
    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();
}
private static async Task<T> WithCancellation<T>(this Task<T> task, CancellationToken cancellationToken)
{
    var tcs = new TaskCompletionSource<bool>();
    using (cancellationToken.Register(state => ((TaskCompletionSource<bool>)state).TrySetResult(true), tcs))
    {
        var completedTask = await Task.WhenAny(task, tcs.Task).ConfigureAwait(false);
        if (task != completedTask)
        {
            throw new OperationCanceledException(cancellationToken);
        }
    }

    return await task.ConfigureAwait(false);
}

The snapshot with dotMemory is below:

  1. Compare snapshots image1

  2. Object Set(SocketAwaitable) image2

I think it is leaking with SocketAwaitable and SocketAsyncEventArgs.

It seems to leak in the following cases:

  • When the SNMP Agent on the remote host does not respond (e.g. snmpd.exe is not listening):
    1. CancellationTokenSource raised an OperationCanceledException exception.
    2. The socket in the GetResponseAsync(this ISnmpMessage, IPEndPoint) method is not disposed.

Perhaps it may be necessary to overload the GetResponseAsync method to receive CancellationToken.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment