Skip to content

Commit 34805b4

Browse files
committed
Merge common code base for DBReferenceCollection
1 parent 769b982 commit 34805b4

File tree

3 files changed

+286
-4
lines changed

3 files changed

+286
-4
lines changed

src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,9 @@
519519
<Compile Include="$(CommonSourceRoot)System\Diagnostics\CodeAnalysis.cs">
520520
<Link>Common\System\Diagnostics\CodeAnalysis.cs</Link>
521521
</Compile>
522+
<Compile Include="$(CommonSourceRoot)\Microsoft\Data\ProviderBase\DbReferenceCollection.cs">
523+
<Link>Microsoft\Data\ProviderBase\DbReferenceCollection.cs</Link>
524+
</Compile>
522525
</ItemGroup>
523526
<ItemGroup Condition="'$(TargetGroup)' == 'netstandard' OR '$(TargetGroup)' == 'netcoreapp' OR '$(IsUAPAssembly)' == 'true'">
524527
<Compile Include="Microsoft.Data.SqlClient.TypeForwards.cs" />
@@ -616,9 +619,6 @@
616619
<Compile Include="$(CommonPath)\Microsoft\Data\ProviderBase\DbConnectionInternal.cs">
617620
<Link>Common\Microsoft\Data\ProviderBase\DbConnectionInternal.cs</Link>
618621
</Compile>
619-
<Compile Include="$(CommonPath)\Microsoft\Data\ProviderBase\DbReferenceCollection.cs">
620-
<Link>Common\Microsoft\Data\ProviderBase\DbReferenceCollection.cs</Link>
621-
</Compile>
622622
<Compile Include="$(CommonPath)\System\NotImplemented.cs" />
623623
<Compile Include="Interop\SNINativeMethodWrapper.Common.cs" />
624624
<Compile Include="Microsoft\Data\Common\DbConnectionOptions.cs" />

src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,9 @@
610610
<Compile Include="$(CommonSourceRoot)Resources\ResDescriptionAttribute.cs">
611611
<Link>Resources\ResDescriptionAttribute.cs</Link>
612612
</Compile>
613+
<Compile Include="$(CommonSourceRoot)\Microsoft\Data\ProviderBase\DbReferenceCollection.cs">
614+
<Link>Microsoft\Data\ProviderBase\DbReferenceCollection.cs</Link>
615+
</Compile>
613616
</ItemGroup>
614617
<ItemGroup>
615618
<Compile Include="Common\src\Microsoft\Data\Common\NameValuePermission.cs" />
@@ -631,7 +634,6 @@
631634
<Compile Include="Microsoft\Data\ProviderBase\DbConnectionPool.cs" />
632635
<Compile Include="Microsoft\Data\ProviderBase\DbConnectionPoolCounters.cs" />
633636
<Compile Include="Microsoft\Data\ProviderBase\DbConnectionPoolIdentity.cs" />
634-
<Compile Include="Microsoft\Data\ProviderBase\DbReferenceCollection.cs" />
635637
<Compile Include="Microsoft\Data\RelationshipConverter.cs" />
636638
<Compile Include="Microsoft\Data\SqlClient\AlwaysEncryptedKeyConverter.Cng.cs" />
637639
<Compile Include="Microsoft\Data\SqlClient\assemblycache.cs" />
Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
6+
using System;
7+
using System.Diagnostics;
8+
using System.Threading;
9+
10+
namespace Microsoft.Data.ProviderBase
11+
{
12+
internal abstract class DbReferenceCollection
13+
{
14+
#region Constants
15+
// Time to wait (in ms) between attempting to get the _itemLock
16+
private const int LockPollTime = 100;
17+
18+
// Default size for the collection, and the amount to grow every time the collection is full
19+
private const int DefaultCollectionSize = 20;
20+
#endregion
21+
22+
#region Fields
23+
// The collection of items we are keeping track of
24+
private CollectionEntry[] _items;
25+
26+
// Used to synchronize access to the _items collection
27+
private readonly object _itemLock;
28+
29+
// (#ItemsAdded - #ItemsRemoved) - This estimates the number of items that we should have
30+
// (but doesn't take into account item targets being GC'd)
31+
private int _estimatedCount;
32+
33+
// Location of the last item in _items
34+
private int _lastItemIndex;
35+
36+
// Indicates that the collection is currently being notified (and, therefore, about to be cleared)
37+
private volatile bool _isNotifying;
38+
#endregion
39+
40+
private struct CollectionEntry
41+
{
42+
private int _refInfo; // information about the reference
43+
private WeakReference<object> _weakReference; // the reference itself.
44+
45+
public void SetTNewTarget(int refInfo, object target)
46+
{
47+
Debug.Assert(!TryGetTarget(out object _), "Entry already has a valid target");
48+
Debug.Assert(refInfo != 0, "Bad reference info");
49+
Debug.Assert(target != null, "Invalid target");
50+
51+
if (_weakReference == null)
52+
{
53+
_weakReference = new WeakReference<object>(target, false);
54+
}
55+
else
56+
{
57+
_weakReference.SetTarget(target);
58+
}
59+
_refInfo = refInfo;
60+
}
61+
62+
public void RemoveTarget()
63+
{
64+
_refInfo = 0;
65+
_weakReference.SetTarget(null);
66+
}
67+
68+
public readonly int RefInfo => _refInfo;
69+
70+
public readonly bool TryGetTarget(out object target)
71+
{
72+
target = null;
73+
return _refInfo != 0 && _weakReference.TryGetTarget(out target);
74+
}
75+
}
76+
77+
protected DbReferenceCollection()
78+
{
79+
_items = new CollectionEntry[DefaultCollectionSize];
80+
_itemLock = new object();
81+
_estimatedCount = 0;
82+
_lastItemIndex = 0;
83+
}
84+
85+
abstract public void Add(object value, int refInfo);
86+
87+
protected void AddItem(object value, int refInfo)
88+
{
89+
Debug.Assert(value != null && 0 != refInfo, "AddItem with null value or 0 reference info");
90+
bool itemAdded = false;
91+
92+
lock (_itemLock)
93+
{
94+
// Try to find a free spot
95+
for (int i = 0; i <= _lastItemIndex; ++i)
96+
{
97+
if (_items[i].RefInfo == 0)
98+
{
99+
_items[i].SetTNewTarget(refInfo, value);
100+
Debug.Assert(_items[i].TryGetTarget(out object _), "missing expected target");
101+
itemAdded = true;
102+
break;
103+
}
104+
}
105+
106+
// No free spots, can we just add on to the end?
107+
if ((!itemAdded) && (_lastItemIndex + 1 < _items.Length))
108+
{
109+
_lastItemIndex++;
110+
_items[_lastItemIndex].SetTNewTarget(refInfo, value);
111+
itemAdded = true;
112+
}
113+
114+
// If no free spots and no space at the end, try to find a dead item
115+
if (!itemAdded)
116+
{
117+
for (int i = 0; i <= _lastItemIndex; ++i)
118+
{
119+
if (!_items[i].TryGetTarget(out object _))
120+
{
121+
_items[i].SetTNewTarget(refInfo, value);
122+
Debug.Assert(_items[i].TryGetTarget(out object _), "missing expected target");
123+
itemAdded = true;
124+
break;
125+
}
126+
}
127+
}
128+
129+
// If nothing was free, then resize and add to the end
130+
if (!itemAdded)
131+
{
132+
Array.Resize<CollectionEntry>(ref _items, _items.Length * 2);
133+
_lastItemIndex++;
134+
_items[_lastItemIndex].SetTNewTarget(refInfo, value);
135+
}
136+
137+
_estimatedCount++;
138+
}
139+
}
140+
141+
internal T FindItem<T>(int refInfo, Func<T, bool> filterMethod) where T : class
142+
{
143+
bool lockObtained = false;
144+
try
145+
{
146+
TryEnterItemLock(ref lockObtained);
147+
if (lockObtained)
148+
{
149+
if (_estimatedCount > 0)
150+
{
151+
for (int counter = 0; counter <= _lastItemIndex; counter++)
152+
{
153+
// Check reference info (should be easiest and quickest)
154+
if (_items[counter].RefInfo == refInfo)
155+
{
156+
if (_items[counter].TryGetTarget(out object value))
157+
{
158+
// Make sure the item has the correct type and passes the filtering
159+
if (value is T tempItem && filterMethod(tempItem))
160+
{
161+
return tempItem;
162+
}
163+
}
164+
}
165+
}
166+
}
167+
}
168+
}
169+
finally
170+
{
171+
ExitItemLockIfNeeded(lockObtained);
172+
}
173+
174+
// If we got to here, then no item was found, so return null
175+
return null;
176+
}
177+
178+
public void Notify(int message)
179+
{
180+
bool lockObtained = false;
181+
try
182+
{
183+
TryEnterItemLock(ref lockObtained);
184+
if (lockObtained)
185+
{
186+
try
187+
{
188+
_isNotifying = true;
189+
190+
// Loop through each live item and notify it
191+
if (_estimatedCount > 0)
192+
{
193+
for (int index = 0; index <= _lastItemIndex; ++index)
194+
{
195+
if (_items[index].TryGetTarget(out object value))
196+
{
197+
NotifyItem(message, _items[index].RefInfo, value);
198+
_items[index].RemoveTarget();
199+
}
200+
Debug.Assert(!_items[index].TryGetTarget(out object _), "Unexpected target after notifying");
201+
}
202+
_estimatedCount = 0;
203+
}
204+
205+
// Shrink collection (if needed)
206+
if (_items.Length > 100)
207+
{
208+
_lastItemIndex = 0;
209+
_items = new CollectionEntry[DefaultCollectionSize];
210+
}
211+
}
212+
finally
213+
{
214+
_isNotifying = false;
215+
}
216+
}
217+
}
218+
finally
219+
{
220+
ExitItemLockIfNeeded(lockObtained);
221+
}
222+
}
223+
224+
abstract protected void NotifyItem(int message, int refInfo, object value);
225+
226+
abstract public void Remove(object value);
227+
228+
protected void RemoveItem(object value)
229+
{
230+
Debug.Assert(null != value, "RemoveItem with null");
231+
232+
bool lockObtained = false;
233+
try
234+
{
235+
TryEnterItemLock(ref lockObtained);
236+
237+
if (lockObtained)
238+
{
239+
// Find the value, and then remove the target from our collection
240+
if (_estimatedCount > 0)
241+
{
242+
for (int index = 0; index <= _lastItemIndex; ++index)
243+
{
244+
if (_items[index].TryGetTarget(out object target) && value == target)
245+
{
246+
_items[index].RemoveTarget();
247+
_estimatedCount--;
248+
break;
249+
}
250+
}
251+
}
252+
}
253+
}
254+
finally
255+
{
256+
ExitItemLockIfNeeded(lockObtained);
257+
}
258+
}
259+
260+
// This is polling lock that will abandon getting the lock if _isNotifying is set to true
261+
private void TryEnterItemLock(ref bool lockObtained)
262+
{
263+
// Assume that we couldn't take the lock
264+
lockObtained = false;
265+
// Keep trying to take the lock until either we've taken it, or the collection is being notified
266+
while ((!_isNotifying) && (!lockObtained))
267+
{
268+
Monitor.TryEnter(_itemLock, LockPollTime, ref lockObtained);
269+
}
270+
}
271+
272+
private void ExitItemLockIfNeeded(bool lockObtained)
273+
{
274+
if (lockObtained)
275+
{
276+
Monitor.Exit(_itemLock);
277+
}
278+
}
279+
}
280+
}

0 commit comments

Comments
 (0)