| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- //------------------------------------------------------------------------------
- // <copyright file="DbReferenceCollection.cs" company="Microsoft">
- // Copyright (c) Microsoft Corporation. All rights reserved.
- // </copyright>
- // <owner current="true" primary="true">[....]</owner>
- // <owner current="true" primary="false">[....]</owner>
- //------------------------------------------------------------------------------
- namespace System.Data.ProviderBase {
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Threading;
- internal abstract class DbReferenceCollection {
- private struct CollectionEntry {
- private int _tag; // information about the reference
- private WeakReference _weak; // the reference itself.
- public void NewTarget(int tag, object target) {
- Debug.Assert(!HasTarget, "Entry already has a valid target");
- Debug.Assert(tag != 0, "Bad tag");
- Debug.Assert(target != null, "Invalid target");
- if (_weak == null) {
- _weak = new WeakReference(target, false);
- }
- else {
- _weak.Target = target;
- }
- _tag = tag;
- }
- public void RemoveTarget() {
- _tag = 0;
- }
- public bool HasTarget {
- get {
- return ((_tag != 0) && (_weak.IsAlive));
- }
- }
-
- public int Tag {
- get {
- return _tag;
- }
- }
- public object Target {
- get {
- return (_tag == 0 ? null : _weak.Target);
- }
- }
- }
- private const int LockPollTime = 100; // Time to wait (in ms) between attempting to get the _itemLock
- private const int DefaultCollectionSize = 20; // Default size for the collection, and the amount to grow everytime the collection is full
- private CollectionEntry[] _items; // The collection of items we are keeping track of
- private readonly object _itemLock; // Used to synchronize access to the _items collection
- 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)
- private int _lastItemIndex; // Location of the last item in _items
- private volatile bool _isNotifying; // Indicates that the collection is currently being notified (and, therefore, about to be cleared)
- protected DbReferenceCollection() {
- _items = new CollectionEntry[DefaultCollectionSize];
- _itemLock = new object();
- _optimisticCount = 0;
- _lastItemIndex = 0;
- }
- abstract public void Add(object value, int tag);
- protected void AddItem(object value, int tag) {
- Debug.Assert(null != value && 0 != tag, "AddItem with null value or 0 tag");
- bool itemAdded = false;
- lock (_itemLock) {
- // Try to find a free spot
- for (int i = 0; i <= _lastItemIndex; ++i) {
- if (_items[i].Tag == 0) {
- _items[i].NewTarget(tag, value);
- Debug.Assert(_items[i].HasTarget, "missing expected target");
- itemAdded = true;
- break;
- }
- }
- // No free spots, can we just add on to the end?
- if ((!itemAdded) && (_lastItemIndex + 1 < _items.Length)) {
- _lastItemIndex++;
- _items[_lastItemIndex].NewTarget(tag, value);
- itemAdded = true;
- }
- // If no free spots and no space at the end, try to find a dead item
- if (!itemAdded) {
- for (int i = 0; i <= _lastItemIndex; ++i) {
- if (!_items[i].HasTarget) {
- _items[i].NewTarget(tag, value);
- Debug.Assert(_items[i].HasTarget, "missing expected target");
- itemAdded = true;
- break;
- }
- }
- }
- // If nothing was free, then resize and add to the end
- if (!itemAdded) {
- Array.Resize<CollectionEntry>(ref _items, _items.Length * 2);
- _lastItemIndex++;
- _items[_lastItemIndex].NewTarget(tag, value);
- }
- _optimisticCount++;
- }
- }
- internal T FindItem<T>(int tag, Func<T, bool> filterMethod) where T : class {
- bool lockObtained = false;
- try {
- TryEnterItemLock(ref lockObtained);
- if (lockObtained) {
- if (_optimisticCount > 0) {
- // Loop through the items
- for (int counter = 0; counter <= _lastItemIndex; counter++) {
- // Check tag (should be easiest and quickest)
- if (_items[counter].Tag == tag) {
- // NOTE: Check if the returned value is null twice may seem wasteful, but this if for performance
- // Since checking for null twice is cheaper than calling both HasTarget and Target OR always attempting to typecast
- object value = _items[counter].Target;
- if (value != null) {
- // Make sure the item has the correct type and passes the filtering
- T tempItem = value as T;
- if ((tempItem != null) && (filterMethod(tempItem))) {
- return tempItem;
- }
- }
- }
- }
- }
- }
- }
- finally {
- ExitItemLockIfNeeded(lockObtained);
- }
- // If we got to here, then no item was found, so return null
- return null;
- }
- public void Notify(int message) {
- bool lockObtained = false;
- try {
- TryEnterItemLock(ref lockObtained);
- if (lockObtained) {
- try {
- _isNotifying = true;
- // Loop through each live item and notify it
- if (_optimisticCount > 0) {
- for (int index = 0; index <= _lastItemIndex; ++index) {
- object value = _items[index].Target; // checks tag & gets target
- if (null != value) {
- NotifyItem(message, _items[index].Tag, value);
- _items[index].RemoveTarget();
- }
- Debug.Assert(!_items[index].HasTarget, "Unexpected target after notifying");
- }
- _optimisticCount = 0;
- }
-
- // Shrink collection (if needed)
- if (_items.Length > 100) {
- _lastItemIndex = 0;
- _items = new CollectionEntry[DefaultCollectionSize];
- }
- }
- finally {
- _isNotifying = false;
- }
- }
- }
- finally {
- ExitItemLockIfNeeded(lockObtained);
- }
- }
- abstract protected void NotifyItem(int message, int tag, object value);
- abstract public void Remove(object value);
- protected void RemoveItem(object value) {
- Debug.Assert(null != value, "RemoveItem with null");
- bool lockObtained = false;
- try {
- TryEnterItemLock(ref lockObtained);
- if (lockObtained) {
- // Find the value, and then remove the target from our collection
- if (_optimisticCount > 0) {
- for (int index = 0; index <= _lastItemIndex; ++index) {
- if (value == _items[index].Target) { // checks tag & gets target
- _items[index].RemoveTarget();
- _optimisticCount--;
- break;
- }
- }
- }
- }
- }
- finally {
- ExitItemLockIfNeeded(lockObtained);
- }
- }
- // This is polling lock that will abandon getting the lock if _isNotifying is set to true
- private void TryEnterItemLock(ref bool lockObtained) {
- // Assume that we couldn't take the lock
- lockObtained = false;
- // Keep trying to take the lock until either we've taken it, or the collection is being notified
- while ((!_isNotifying) && (!lockObtained)) {
- Monitor.TryEnter(_itemLock, LockPollTime, ref lockObtained);
- }
- }
- private void ExitItemLockIfNeeded(bool lockObtained) {
- if (lockObtained) {
- Monitor.Exit(_itemLock);
- }
- }
- }
- }
|