< Summary

Class:Itinero.Network.Search.Islands.RoutingNetworkIslandManager
Assembly:Itinero
File(s):/home/runner/work/routing2/routing2/src/Itinero/Network/Search/Islands/RoutingNetworkIslandManager.cs
Covered lines:122
Uncovered lines:14
Coverable lines:136
Total lines:269
Line coverage:89.7% (122 of 136)
Covered branches:24
Total branches:28
Branch coverage:85.7% (24 of 28)
Tag:267_28791001112

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
.ctor(...)100%1100%
GetBuildSerialiser(...)100%1100%
.ctor(...)100%1100%
IsEdgeOnIsland(...)100%1100%
IsEdgeOnIsland(...)62.5%881.25%
GetOrCreateDirectedGraph(...)100%2100%
get_MaxIslandSize()100%1100%
TryGetIslandsFor(...)100%10%
GetIslandsFor(...)100%2100%
IsMainN(...)100%10100%
BuildForTileAsync()100%4100%
Clone()50%276.92%

File(s)

/home/runner/work/routing2/routing2/src/Itinero/Network/Search/Islands/RoutingNetworkIslandManager.cs

#LineLine coverage
 1using System;
 2using System.Collections.Concurrent;
 3using System.Collections.Generic;
 4using System.Diagnostics.CodeAnalysis;
 5using System.Threading;
 6using System.Threading.Tasks;
 7using Itinero.Profiles;
 8
 9namespace Itinero.Network.Search.Islands;
 10
 11internal class RoutingNetworkIslandManager
 12{
 60813    private readonly Dictionary<(string profile, uint tile), Task> _tilesInProgress = new();
 60814    private readonly ReaderWriterLockSlim _tilesInProgressLock = new();
 15    private readonly Dictionary<string, Islands> _islands;
 60816    private readonly Dictionary<(string profile, IslandKind kind), IslandDirectedGraph> _directedGraphs = new();
 60817    private readonly ReaderWriterLockSlim _islandsLock = new();
 18
 19    // Per-profile semaphore that serialises IslandClassifier.BuildForTileAsync
 20    // calls against each other for the same profile. The shared Full+NonLocal
 21    // dgs are reset to their initial state at the end of each call (per spec);
 22    // running two classifications for the same profile concurrently would let
 23    // one wipe the other's working state mid-flight. Different profiles still
 24    // run in parallel — each has its own dg pair and its own semaphore.
 60825    private readonly ConcurrentDictionary<string, SemaphoreSlim> _buildSerialisers = new();
 26
 27    internal SemaphoreSlim GetBuildSerialiser(string profileName) =>
 6928        _buildSerialisers.GetOrAdd(profileName, _ => new SemaphoreSlim(1, 1));
 29
 35130    internal RoutingNetworkIslandManager(int maxIslandSize)
 35131    {
 35132        this.MaxIslandSize = maxIslandSize;
 35133        _islands = new();
 35134    }
 35
 25736    private RoutingNetworkIslandManager(int maxIslandSize, Dictionary<string, Islands> islands)
 25737    {
 25738        this.MaxIslandSize = maxIslandSize;
 25739        _islands = islands;
 25740    }
 41
 42    /// <summary>
 43    /// Checks if an edge is on an island using the directed graph.
 44    /// Returns true if island, false if not island, null if inconclusive.
 45    /// </summary>
 46    internal bool? IsEdgeOnIsland(Profile profile, EdgeId edgeId) =>
 747        this.IsEdgeOnIsland(profile.Name, edgeId);
 48
 49    internal bool? IsEdgeOnIsland(string profileName, EdgeId edgeId)
 750    {
 51        try
 752        {
 753            _islandsLock.EnterReadLock();
 54
 55            // Snapping is a Full-classification concern, so the existing
 56            // single-DG semantics route through the Full DG.
 757            if (!_directedGraphs.TryGetValue((profileName, IslandKind.Full), out var dg))
 558                return null;
 59
 260            if (!_islands.TryGetValue(profileName, out var profileIslands))
 061                return null;
 262            if (profileIslands.IsEdgeOnIsland(edgeId))
 063                return true;
 64
 265            if (dg.IsNotIsland(edgeId))
 066                return false;
 67
 268            return null;
 69        }
 70        finally
 771        {
 772            _islandsLock.ExitReadLock();
 773        }
 774    }
 75
 76    internal IslandDirectedGraph GetOrCreateDirectedGraph(Profile profile, IslandKind kind = IslandKind.Full)
 365977    {
 365978        var key = (profile.Name, kind);
 79        try
 365980        {
 365981            _islandsLock.EnterUpgradeableReadLock();
 82
 723983            if (_directedGraphs.TryGetValue(key, out var dg)) return dg;
 84
 85            try
 7986            {
 7987                _islandsLock.EnterWriteLock();
 88
 7989                dg = new IslandDirectedGraph();
 7990                _directedGraphs[key] = dg;
 7991                return dg;
 92            }
 93            finally
 7994            {
 7995                _islandsLock.ExitWriteLock();
 7996            }
 97        }
 98        finally
 365999        {
 3659100            _islandsLock.ExitUpgradeableReadLock();
 3659101        }
 3659102    }
 103
 2774104    internal int MaxIslandSize { get; }
 105
 106    internal bool TryGetIslandsFor(string profileName, out Islands islands)
 0107    {
 108        try
 0109        {
 0110            _islandsLock.EnterReadLock();
 111
 0112            return _islands.TryGetValue(profileName, out islands);
 113        }
 114        finally
 0115        {
 0116            _islandsLock.ExitReadLock();
 0117        }
 0118    }
 119
 120    internal Islands GetIslandsFor(Profile profile)
 2426121    {
 122        try
 2426123        {
 2426124            _islandsLock.EnterUpgradeableReadLock();
 125
 4808126            if (_islands.TryGetValue(profile.Name, out var islands)) return islands;
 127
 128            try
 44129            {
 44130                _islandsLock.EnterWriteLock();
 131
 44132                islands = new Islands();
 44133                _islands[profile.Name] = islands;
 44134                return islands;
 135            }
 136            finally
 44137            {
 44138                _islandsLock.ExitWriteLock();
 44139            }
 140        }
 141        finally
 2426142        {
 2426143            _islandsLock.ExitUpgradeableReadLock();
 2426144        }
 2426145    }
 146
 147    /// <summary>
 148    /// Returns whether the edge is in the profile's main-N component — the
 149    /// dominant SCC of the N-only subgraph, i.e. the "mainland" without
 150    /// L-edges.
 151    ///
 152    /// <list type="bullet">
 153    /// <item><c>true</c>: edge is in main-N. Default for any edge in a done tile
 154    /// that is neither L-tagged, on an island, nor a non-main-N pocket member.</item>
 155    /// <item><c>false</c>: edge is not in main-N. Either L-tagged (passed in via
 156    /// <paramref name="isLocalAccess"/>), on an island (unreachable in Full), or
 157    /// recorded as a local edge (non-main-N pocket).</item>
 158    /// <item><c>null</c>: classification has not yet produced a verdict for this
 159    /// tile.</item>
 160    /// </list>
 161    ///
 162    /// The L-tag check is tag-driven and resolved by the caller (typically via
 163    /// the cost function's <c>localAccess</c> field on the result of <c>Get</c>),
 164    /// then passed in. The manager itself does not consult any tag storage.
 165    /// </summary>
 166    internal bool? IsMainN(Profile profile, EdgeId edgeId, bool isLocalAccess)
 788377167    {
 168        // L-tagged edge — never main-N, no storage lookup needed.
 788379169        if (isLocalAccess) return false;
 170
 171        try
 788375172        {
 788375173            _islandsLock.EnterReadLock();
 174
 1568830175            if (!_islands.TryGetValue(profile.Name, out var islands)) return null;
 176
 177            // Unreachable in the Full classification → not in main-N.
 7921178            if (islands.IsEdgeOnIsland(edgeId)) return false;
 179
 180            // Non-main-N pocket → not in main-N.
 7920181            if (islands.IsEdgeLocal(edgeId)) return false;
 182
 183            // Tile finished classifying and the edge is in neither set → main-N.
 184            // Otherwise we don't yet know.
 7918185            return islands.GetTileDone(edgeId.TileId) ? true : null;
 186        }
 187        finally
 788375188        {
 788375189            _islandsLock.ExitReadLock();
 788375190        }
 788377191    }
 192
 193    internal async Task BuildForTileAsync(RoutingNetwork network, Profile profile, uint tileId,
 194        CancellationToken cancellationToken)
 7195    {
 196        // queue task, if not done yet.
 197        Task task;
 198        try
 7199        {
 7200            _tilesInProgressLock.EnterUpgradeableReadLock();
 201
 7202            if (!_tilesInProgress.TryGetValue((profile.Name, tileId), out task))
 7203            {
 204                try
 7205                {
 7206                    _tilesInProgressLock.EnterWriteLock();
 207
 7208                    task = IslandClassifier.BuildForTileAsync(network, profile, tileId, cancellationToken);
 7209                    _tilesInProgress[(profile.Name, tileId)] = task;
 7210                }
 211                finally
 7212                {
 7213                    _tilesInProgressLock.ExitWriteLock();
 7214                }
 7215            }
 7216        }
 217        finally
 7218        {
 7219            _tilesInProgressLock.ExitUpgradeableReadLock();
 7220        }
 221
 222        // await the task.
 7223        await task;
 224
 225        // remove from the queue.
 226        try
 7227        {
 7228            _tilesInProgressLock.EnterUpgradeableReadLock();
 229
 7230            if (_tilesInProgress.ContainsKey((profile.Name, tileId)))
 7231            {
 232                try
 7233                {
 7234                    _tilesInProgressLock.EnterWriteLock();
 235
 7236                    _tilesInProgress.Remove((profile.Name, tileId));
 7237                }
 238                finally
 7239                {
 7240                    _tilesInProgressLock.ExitWriteLock();
 7241                }
 7242            }
 7243        }
 244        finally
 7245        {
 7246            _tilesInProgressLock.ExitUpgradeableReadLock();
 7247        }
 7248    }
 249
 250    internal RoutingNetworkIslandManager Clone()
 257251    {
 252        try
 257253        {
 257254            _islandsLock.EnterReadLock();
 255
 257256            var islands = new Dictionary<string, Islands>();
 771257            foreach (var (profileName, profileIslands) in _islands)
 0258            {
 0259                islands[profileName] = profileIslands.Clone();
 0260            }
 261
 257262            return new RoutingNetworkIslandManager(this.MaxIslandSize, islands);
 263        }
 264        finally
 257265        {
 257266            _islandsLock.ExitReadLock();
 257267        }
 257268    }
 269}