/// <summary> /// This is an important operation for long lasting allocations. /// For example the full sync tends to allocate the same peer for many minutes and we use this method to ensure that /// a newly arriving better peer can replace a currently selected one. /// Consider that there are some external changes (e.g. node stats values change based on the sync transfer rates) /// which may not be controlled from inside here, hence we decide to monitor the potential upgrades in a loop. /// </summary> /// <exception cref="InvalidOperationException">Thrown if an irreplaceable allocation is being replaced by this method (internal implementation error).</exception> private void UpgradeAllocations(string reason) { DropUselessPeers(); WakeUpPeerThatSleptEnough(); foreach ((SyncPeerAllocation allocation, _) in _replaceableAllocations) { lock (_isAllocatedChecks) { var unallocatedPeers = InitializedPeers.Where(p => p.CanBeAllocated(allocation.Contexts)); allocation.AllocateBestPeer(unallocatedPeers, _stats, _blockTree); } } }
public async Task <SyncPeerAllocation> Allocate(IPeerAllocationStrategy peerAllocationStrategy, AllocationContexts allocationContexts = AllocationContexts.All, int timeoutMilliseconds = 0) { int tryCount = 1; DateTime startTime = DateTime.UtcNow; SyncPeerAllocation allocation = new SyncPeerAllocation(peerAllocationStrategy, allocationContexts); while (true) { lock (_isAllocatedChecks) { allocation.AllocateBestPeer(InitializedPeers.Where(p => p.CanBeAllocated(allocationContexts)), _stats, _blockTree); if (allocation.HasPeer) { if (peerAllocationStrategy.CanBeReplaced) { _replaceableAllocations.TryAdd(allocation, null); } return(allocation); } } bool timeoutReached = timeoutMilliseconds == 0 || (DateTime.UtcNow - startTime).TotalMilliseconds > timeoutMilliseconds; if (timeoutReached) { return(SyncPeerAllocation.FailedAllocation); } int waitTime = 10 * tryCount++; if (!_signals.SafeWaitHandle.IsClosed) { await _signals.WaitOneAsync(waitTime, _refreshLoopCancellation.Token); if (!_signals.SafeWaitHandle.IsClosed) { _signals.Reset(); // without this we have no delay } } } }