DbReferenceCollection.cs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. //------------------------------------------------------------------------------
  2. // <copyright file="DbReferenceCollection.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">[....]</owner>
  6. // <owner current="true" primary="false">[....]</owner>
  7. //------------------------------------------------------------------------------
  8. namespace System.Data.ProviderBase {
  9. using System;
  10. using System.Collections;
  11. using System.Collections.Generic;
  12. using System.Diagnostics;
  13. using System.Threading;
  14. internal abstract class DbReferenceCollection {
  15. private struct CollectionEntry {
  16. private int _tag; // information about the reference
  17. private WeakReference _weak; // the reference itself.
  18. public void NewTarget(int tag, object target) {
  19. Debug.Assert(!HasTarget, "Entry already has a valid target");
  20. Debug.Assert(tag != 0, "Bad tag");
  21. Debug.Assert(target != null, "Invalid target");
  22. if (_weak == null) {
  23. _weak = new WeakReference(target, false);
  24. }
  25. else {
  26. _weak.Target = target;
  27. }
  28. _tag = tag;
  29. }
  30. public void RemoveTarget() {
  31. _tag = 0;
  32. }
  33. public bool HasTarget {
  34. get {
  35. return ((_tag != 0) && (_weak.IsAlive));
  36. }
  37. }
  38. public int Tag {
  39. get {
  40. return _tag;
  41. }
  42. }
  43. public object Target {
  44. get {
  45. return (_tag == 0 ? null : _weak.Target);
  46. }
  47. }
  48. }
  49. private const int LockPollTime = 100; // Time to wait (in ms) between attempting to get the _itemLock
  50. private const int DefaultCollectionSize = 20; // Default size for the collection, and the amount to grow everytime the collection is full
  51. private CollectionEntry[] _items; // The collection of items we are keeping track of
  52. private readonly object _itemLock; // Used to synchronize access to the _items collection
  53. private int _optimisticCount; // (#ItemsAdded - #ItemsRemoved) - This estimates the number of items that we *should* have (but doesn't take into account item targets being GC'd)
  54. private int _lastItemIndex; // Location of the last item in _items
  55. private volatile bool _isNotifying; // Indicates that the collection is currently being notified (and, therefore, about to be cleared)
  56. protected DbReferenceCollection() {
  57. _items = new CollectionEntry[DefaultCollectionSize];
  58. _itemLock = new object();
  59. _optimisticCount = 0;
  60. _lastItemIndex = 0;
  61. }
  62. abstract public void Add(object value, int tag);
  63. protected void AddItem(object value, int tag) {
  64. Debug.Assert(null != value && 0 != tag, "AddItem with null value or 0 tag");
  65. bool itemAdded = false;
  66. lock (_itemLock) {
  67. // Try to find a free spot
  68. for (int i = 0; i <= _lastItemIndex; ++i) {
  69. if (_items[i].Tag == 0) {
  70. _items[i].NewTarget(tag, value);
  71. Debug.Assert(_items[i].HasTarget, "missing expected target");
  72. itemAdded = true;
  73. break;
  74. }
  75. }
  76. // No free spots, can we just add on to the end?
  77. if ((!itemAdded) && (_lastItemIndex + 1 < _items.Length)) {
  78. _lastItemIndex++;
  79. _items[_lastItemIndex].NewTarget(tag, value);
  80. itemAdded = true;
  81. }
  82. // If no free spots and no space at the end, try to find a dead item
  83. if (!itemAdded) {
  84. for (int i = 0; i <= _lastItemIndex; ++i) {
  85. if (!_items[i].HasTarget) {
  86. _items[i].NewTarget(tag, value);
  87. Debug.Assert(_items[i].HasTarget, "missing expected target");
  88. itemAdded = true;
  89. break;
  90. }
  91. }
  92. }
  93. // If nothing was free, then resize and add to the end
  94. if (!itemAdded) {
  95. Array.Resize<CollectionEntry>(ref _items, _items.Length * 2);
  96. _lastItemIndex++;
  97. _items[_lastItemIndex].NewTarget(tag, value);
  98. }
  99. _optimisticCount++;
  100. }
  101. }
  102. internal T FindItem<T>(int tag, Func<T, bool> filterMethod) where T : class {
  103. bool lockObtained = false;
  104. try {
  105. TryEnterItemLock(ref lockObtained);
  106. if (lockObtained) {
  107. if (_optimisticCount > 0) {
  108. // Loop through the items
  109. for (int counter = 0; counter <= _lastItemIndex; counter++) {
  110. // Check tag (should be easiest and quickest)
  111. if (_items[counter].Tag == tag) {
  112. // NOTE: Check if the returned value is null twice may seem wasteful, but this if for performance
  113. // Since checking for null twice is cheaper than calling both HasTarget and Target OR always attempting to typecast
  114. object value = _items[counter].Target;
  115. if (value != null) {
  116. // Make sure the item has the correct type and passes the filtering
  117. T tempItem = value as T;
  118. if ((tempItem != null) && (filterMethod(tempItem))) {
  119. return tempItem;
  120. }
  121. }
  122. }
  123. }
  124. }
  125. }
  126. }
  127. finally {
  128. ExitItemLockIfNeeded(lockObtained);
  129. }
  130. // If we got to here, then no item was found, so return null
  131. return null;
  132. }
  133. public void Notify(int message) {
  134. bool lockObtained = false;
  135. try {
  136. TryEnterItemLock(ref lockObtained);
  137. if (lockObtained) {
  138. try {
  139. _isNotifying = true;
  140. // Loop through each live item and notify it
  141. if (_optimisticCount > 0) {
  142. for (int index = 0; index <= _lastItemIndex; ++index) {
  143. object value = _items[index].Target; // checks tag & gets target
  144. if (null != value) {
  145. NotifyItem(message, _items[index].Tag, value);
  146. _items[index].RemoveTarget();
  147. }
  148. Debug.Assert(!_items[index].HasTarget, "Unexpected target after notifying");
  149. }
  150. _optimisticCount = 0;
  151. }
  152. // Shrink collection (if needed)
  153. if (_items.Length > 100) {
  154. _lastItemIndex = 0;
  155. _items = new CollectionEntry[DefaultCollectionSize];
  156. }
  157. }
  158. finally {
  159. _isNotifying = false;
  160. }
  161. }
  162. }
  163. finally {
  164. ExitItemLockIfNeeded(lockObtained);
  165. }
  166. }
  167. abstract protected void NotifyItem(int message, int tag, object value);
  168. abstract public void Remove(object value);
  169. protected void RemoveItem(object value) {
  170. Debug.Assert(null != value, "RemoveItem with null");
  171. bool lockObtained = false;
  172. try {
  173. TryEnterItemLock(ref lockObtained);
  174. if (lockObtained) {
  175. // Find the value, and then remove the target from our collection
  176. if (_optimisticCount > 0) {
  177. for (int index = 0; index <= _lastItemIndex; ++index) {
  178. if (value == _items[index].Target) { // checks tag & gets target
  179. _items[index].RemoveTarget();
  180. _optimisticCount--;
  181. break;
  182. }
  183. }
  184. }
  185. }
  186. }
  187. finally {
  188. ExitItemLockIfNeeded(lockObtained);
  189. }
  190. }
  191. // This is polling lock that will abandon getting the lock if _isNotifying is set to true
  192. private void TryEnterItemLock(ref bool lockObtained) {
  193. // Assume that we couldn't take the lock
  194. lockObtained = false;
  195. // Keep trying to take the lock until either we've taken it, or the collection is being notified
  196. while ((!_isNotifying) && (!lockObtained)) {
  197. Monitor.TryEnter(_itemLock, LockPollTime, ref lockObtained);
  198. }
  199. }
  200. private void ExitItemLockIfNeeded(bool lockObtained) {
  201. if (lockObtained) {
  202. Monitor.Exit(_itemLock);
  203. }
  204. }
  205. }
  206. }