You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
@ -0,0 +1,238 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user