11// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
22
33using System ;
4- using System . Collections . Generic ;
4+ using System . Collections . Concurrent ;
55using System . Diagnostics ;
66
77namespace Microsoft . VisualStudioTools . Project
88{
99 internal sealed class HierarchyIdMap
1010 {
11- private readonly List < WeakReference < HierarchyNode > > ids = new List < WeakReference < HierarchyNode > > ( ) ;
12- private readonly Stack < int > freedIds = new Stack < int > ( ) ;
13-
14- private readonly object theLock = new object ( ) ;
11+ private readonly ConcurrentDictionary < int , WeakReference < HierarchyNode > > nodes = new ConcurrentDictionary < int , WeakReference < HierarchyNode > > ( ) ;
12+ private readonly ConcurrentStack < int > freedIds = new ConcurrentStack < int > ( ) ;
1513
1614 /// <summary>
1715 /// Must be called from the UI thread
1816 /// </summary>
1917 public uint Add ( HierarchyNode node )
2018 {
19+ VisualStudio . Shell . ThreadHelper . ThrowIfNotOnUIThread ( ) ;
20+
2121#if DEBUG
22- foreach ( var reference in this . ids )
22+ foreach ( var reference in this . nodes . Values )
2323 {
2424 if ( reference != null )
2525 {
@@ -29,25 +29,15 @@ public uint Add(HierarchyNode node)
2929 }
3030 }
3131 }
32- #endif
3332
34- VisualStudio . Shell . ThreadHelper . ThrowIfNotOnUIThread ( ) ;
35-
36- lock ( this . theLock )
33+ #endif
34+ if ( ! this . freedIds . TryPop ( out var idx ) )
3735 {
38- if ( this . freedIds . Count > 0 )
39- {
40- var i = this . freedIds . Pop ( ) ;
41- this . ids [ i ] = new WeakReference < HierarchyNode > ( node ) ;
42- return ( uint ) i + 1 ;
43- }
44- else
45- {
46- this . ids . Add ( new WeakReference < HierarchyNode > ( node ) ) ;
47- // ids are 1 based
48- return ( uint ) this . ids . Count ;
49- }
36+ idx = this . nodes . Count ;
5037 }
38+ this . nodes [ idx ] = new WeakReference < HierarchyNode > ( node ) ;
39+
40+ return ( uint ) idx + 1 ;
5141 }
5242
5343 /// <summary>
@@ -57,33 +47,21 @@ public void Remove(HierarchyNode node)
5747 {
5848 VisualStudio . Shell . ThreadHelper . ThrowIfNotOnUIThread ( ) ;
5949
60- if ( node == null )
61- {
62- throw new ArgumentNullException ( nameof ( node ) ) ;
63- }
50+ Debug . Assert ( node != null , "Called with null node" ) ;
6451
65- lock ( this . theLock )
66- {
67- var i = ( int ) node . ID - 1 ;
68- if ( 0 > i || i >= this . ids . Count )
69- {
70- throw new InvalidOperationException ( $ "Invalid id. { i } ") ;
71- }
52+ var idx = ( int ) node . ID - 1 ;
7253
73- var weakRef = this . ids [ i ] ;
74- if ( weakRef == null )
75- {
76- throw new InvalidOperationException ( "Trying to retrieve a node before adding." ) ;
77- }
54+ var removeCheck = this . nodes . TryGetValue ( idx , out var weakRef ) ;
7855
79- if ( weakRef . TryGetTarget ( out var found ) && ! object . ReferenceEquals ( node , found ) )
80- {
81- throw new InvalidOperationException ( "The node has the wrong id." ) ;
82- }
56+ Debug . Assert ( removeCheck , "How did we get an id, which we haven't seen before" ) ;
57+ Debug . Assert ( weakRef != null , "Double delete is not expected." ) ;
8358
84- this . ids [ i ] = null ;
85- this . freedIds . Push ( i ) ;
86- }
59+ var checkReference = weakRef . TryGetTarget ( out var found ) && object . ReferenceEquals ( node , found ) ;
60+
61+ Debug . Assert ( checkReference , "The node has the wrong id." ) ;
62+
63+ this . nodes [ idx ] = null ;
64+ this . freedIds . Push ( idx ) ;
8765 }
8866
8967 /// <summary>
@@ -93,12 +71,15 @@ public HierarchyNode this[uint itemId]
9371 {
9472 get
9573 {
74+ VisualStudio . Shell . ThreadHelper . ThrowIfNotOnUIThread ( ) ;
75+
9676 var i = ( int ) itemId - 1 ;
97- if ( 0 <= i && i < this . ids . Count )
77+ if ( 0 <= i && i < this . nodes . Count )
9878 {
99- var reference = this . ids [ i ] ;
79+ var reference = this . nodes [ i ] ;
10080 if ( reference != null && reference . TryGetTarget ( out var node ) )
10181 {
82+ Debug . Assert ( node != null ) ;
10283 return node ;
10384 }
10485 }
0 commit comments