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

Fix memory leak on TaskManager due to continuous growth of knownHashes (2.x) #865

Merged
merged 11 commits into from
Jun 26, 2019
21 changes: 20 additions & 1 deletion neo/IO/Caching/FIFOSet.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;

namespace Neo.IO.Caching
{
internal class FIFOSet<T> where T : IEquatable<T>
internal class FIFOSet<T> : IEnumerable<T> where T : IEquatable<T>
erikzhang marked this conversation as resolved.
Show resolved Hide resolved
{
private readonly int maxCapacity;
private readonly int removeCount;
Expand Down Expand Up @@ -37,5 +40,21 @@ public bool Add(T item)
dictionary.Add(item, null);
return true;
}

public IEnumerator<T> GetEnumerator()
{
var entries = dictionary.Values.Cast<T>().ToArray();
foreach (var entry in entries) yield return entry;
}

public void Remove(params UInt256[] hashes)
{
foreach (var hash in hashes)
{
dictionary.Remove(hash);
}
}

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}
12 changes: 10 additions & 2 deletions neo/Network/P2P/TaskManager.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Akka.Actor;
using Akka.Configuration;
using Neo.IO.Actors;
using Neo.IO.Caching;
using Neo.Ledger;
using Neo.Network.P2P.Payloads;
using System;
Expand All @@ -24,7 +25,14 @@ private class Timer { }

private readonly NeoSystem system;
private const int MaxConncurrentTasks = 3;
private readonly HashSet<UInt256> knownHashes = new HashSet<UInt256>();

/// <summary>
/// Max GetBlocks and Headers are limmited to 500 each
/// Blockchain.Singleton.MemPool.Capacity * 2 was the same value used in ProtocolHandler
/// </summary>
private static readonly int MaxCachedHashes = Blockchain.Singleton.MemPool.Capacity * 2;
private readonly FIFOSet<UInt256> knownHashes = new FIFOSet<UInt256>(MaxCachedHashes);

private readonly Dictionary<UInt256, int> globalTasks = new Dictionary<UInt256, int>();
private readonly Dictionary<IActorRef, TaskSession> sessions = new Dictionary<IActorRef, TaskSession>();
private readonly ICancelable timer = Context.System.Scheduler.ScheduleTellRepeatedlyCancelable(TimerInterval, TimerInterval, Context.Self, new Timer(), ActorRefs.NoSender);
Expand Down Expand Up @@ -115,7 +123,7 @@ private void OnRegister(VersionPayload version)

private void OnRestartTasks(InvPayload payload)
{
knownHashes.ExceptWith(payload.Hashes);
knownHashes.Remove(payload.Hashes);
foreach (UInt256 hash in payload.Hashes)
globalTasks.Remove(hash);
foreach (InvPayload group in InvPayload.CreateGroup(payload.Type, payload.Hashes))
Expand Down