WalkAsync is missing a CancellationToken
mjanulaitis opened this issue · 5 comments
There is no way to cancel a walk because the call does not take a CancellationToken.
Issue #74 does not address the missing CancellationToken.
Async functions should always take and implement CancellationTokens. There is no way to cancel an async task externally. This must be implemented in the library.
https://devblogs.microsoft.com/premier-developer/recommended-patterns-for-cancellationtoken/
It makes no sense for #SNMP Library to support cancellation tokens when the underlying socket API doesn't, and no plan to address that.
"This must be implemented in the library" is not applicable.
The time out for the HasNext is not the issue. Certainly the timeout for one variable get can be the cancellation resolution. By adding a cancellation token to the call you can add the cancellation check between each HasNext call. Thus you can cancel then get a true cancellation within the timeout specified. For example:
public static int Walk(VersionCode version, IPEndPoint endpoint, OctetString community, ObjectIdentifier table, IList<Variable> list, int timeout, WalkMode mode, CancellationToken cancellationToken)
{
if (list == null)
{
throw new ArgumentNullException(nameof(list));
}
var result = 0;
var tableV = new Variable(table);
Variable seed;
var next = tableV;
var rowMask = string.Format(CultureInfo.InvariantCulture, "{0}.1.1.", table);
var subTreeMask = string.Format(CultureInfo.InvariantCulture, "{0}.", table);
do
{
cancellationToken.ThrowIfCancellationRequested();
seed = next;
if (seed == tableV)
{
continue;
}
if (mode == WalkMode.WithinSubtree && !seed.Id.ToString().StartsWith(subTreeMask, StringComparison.Ordinal))
{
// not in sub tree
break;
}
list.Add(seed);
if (seed.Id.ToString().StartsWith(rowMask, StringComparison.Ordinal))
{
result++;
}
}
while (HasNext(version, endpoint, community, seed, timeout, out next));
return result;
}
For those that are interested here is the code that can be copied into your SNMP controller enabling the walk to be canceled when for example your SNMP Browser view is closed:
public static int Walk(VersionCode version, IPEndPoint endpoint, OctetString community, ObjectIdentifier table, IList<Variable> list, int timeout, WalkMode mode, CancellationToken cancellationToken)
{
if (list == null)
{
throw new ArgumentNullException(nameof(list));
}
var result = 0;
var tableV = new Variable(table);
Variable seed;
var next = tableV;
var rowMask = string.Format(CultureInfo.InvariantCulture, "{0}.1.1.", table);
var subTreeMask = string.Format(CultureInfo.InvariantCulture, "{0}.", table);
do
{
cancellationToken.ThrowIfCancellationRequested();
seed = next;
if (seed == tableV)
{
continue;
}
if (mode == WalkMode.WithinSubtree && !seed.Id.ToString().StartsWith(subTreeMask, StringComparison.Ordinal))
{
// not in sub tree
break;
}
list.Add(seed);
if (seed.Id.ToString().StartsWith(rowMask, StringComparison.Ordinal))
{
result++;
}
}
while (HasNext(version, endpoint, community, seed, timeout, out next));
return result;
}
private static bool HasNext(VersionCode version, IPEndPoint endpoint, OctetString community, Variable seed, int timeout, out Variable next)
{
if (seed == null)
{
throw new ArgumentNullException(nameof(seed));
}
var variables = new List<Variable> { new Variable(seed.Id) };
var message = new GetNextRequestMessage(
Messenger.NextRequestId,
version,
community,
variables);
var response = message.GetResponse(timeout, endpoint);
var pdu = response.Pdu();
var errorFound = pdu.ErrorStatus.ToErrorCode() == ErrorCode.NoSuchName;
next = errorFound ? null : pdu.Variables[0];
return !errorFound;
}