-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add automatic Util.KeepRunning option
- Loading branch information
Showing
3 changed files
with
302 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,276 @@ | ||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information. | ||
// https://github.com/mono/rx/blob/master/Rx/NET/Source/System.Reactive.Core/Reactive/Disposables/CompositeDisposable.cs | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
|
||
namespace System.Reactive.Disposables | ||
{ | ||
/// <summary> | ||
/// Represents a group of disposable resources that are disposed together. | ||
/// </summary> | ||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix", | ||
Justification = "Backward compat + ideally want to get rid of the ICollection nature of the type.")] | ||
public sealed class CompositeDisposable : ICollection<IDisposable> | ||
{ | ||
private readonly object _gate = new object(); | ||
|
||
private bool _disposed; | ||
private List<IDisposable> _disposables; | ||
private int _count; | ||
private const int SHRINK_THRESHOLD = 64; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="T:System.Reactive.Disposables.CompositeDisposable"/> class with no disposables contained by it initially. | ||
/// </summary> | ||
public CompositeDisposable() | ||
{ | ||
_disposables = new List<IDisposable>(); | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="T:System.Reactive.Disposables.CompositeDisposable"/> class with the specified number of disposables. | ||
/// </summary> | ||
/// <param name="capacity">The number of disposables that the new CompositeDisposable can initially store.</param> | ||
/// <exception cref="ArgumentOutOfRangeException"><paramref name="capacity"/> is less than zero.</exception> | ||
public CompositeDisposable(int capacity) | ||
{ | ||
if (capacity < 0) | ||
throw new ArgumentOutOfRangeException("capacity"); | ||
|
||
_disposables = new List<IDisposable>(capacity); | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="T:System.Reactive.Disposables.CompositeDisposable"/> class from a group of disposables. | ||
/// </summary> | ||
/// <param name="disposables">Disposables that will be disposed together.</param> | ||
/// <exception cref="ArgumentNullException"><paramref name="disposables"/> is null.</exception> | ||
public CompositeDisposable(params IDisposable[] disposables) | ||
{ | ||
if (disposables == null) | ||
throw new ArgumentNullException("disposables"); | ||
|
||
_disposables = new List<IDisposable>(disposables); | ||
_count = _disposables.Count; | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="T:System.Reactive.Disposables.CompositeDisposable"/> class from a group of disposables. | ||
/// </summary> | ||
/// <param name="disposables">Disposables that will be disposed together.</param> | ||
/// <exception cref="ArgumentNullException"><paramref name="disposables"/> is null.</exception> | ||
public CompositeDisposable(IEnumerable<IDisposable> disposables) | ||
{ | ||
if (disposables == null) | ||
throw new ArgumentNullException("disposables"); | ||
|
||
_disposables = new List<IDisposable>(disposables); | ||
_count = _disposables.Count; | ||
} | ||
|
||
/// <summary> | ||
/// Gets the number of disposables contained in the CompositeDisposable. | ||
/// </summary> | ||
public int Count | ||
{ | ||
get { return _count; } | ||
} | ||
|
||
/// <summary> | ||
/// Adds a disposable to the CompositeDisposable or disposes the disposable if the CompositeDisposable is disposed. | ||
/// </summary> | ||
/// <param name="item">Disposable to add.</param> | ||
/// <exception cref="ArgumentNullException"><paramref name="item"/> is null.</exception> | ||
public void Add(IDisposable item) | ||
{ | ||
if (item == null) | ||
throw new ArgumentNullException("item"); | ||
|
||
var shouldDispose = false; | ||
lock (_gate) | ||
{ | ||
shouldDispose = _disposed; | ||
if (!_disposed) | ||
{ | ||
_disposables.Add(item); | ||
_count++; | ||
} | ||
} | ||
|
||
if (shouldDispose) | ||
item.Dispose(); | ||
} | ||
|
||
/// <summary> | ||
/// Removes and disposes the first occurrence of a disposable from the CompositeDisposable. | ||
/// </summary> | ||
/// <param name="item">Disposable to remove.</param> | ||
/// <returns>true if found; false otherwise.</returns> | ||
/// <exception cref="ArgumentNullException"><paramref name="item"/> is null.</exception> | ||
public bool Remove(IDisposable item) | ||
{ | ||
if (item == null) | ||
throw new ArgumentNullException("item"); | ||
|
||
var shouldDispose = false; | ||
|
||
lock (_gate) | ||
{ | ||
if (!_disposed) | ||
{ | ||
// | ||
// List<T> doesn't shrink the size of the underlying array but does collapse the array | ||
// by copying the tail one position to the left of the removal index. We don't need | ||
// index-based lookup but only ordering for sequential disposal. So, instead of spending | ||
// cycles on the Array.Copy imposed by Remove, we use a null sentinel value. We also | ||
// do manual Swiss cheese detection to shrink the list if there's a lot of holes in it. | ||
// | ||
var i = _disposables.IndexOf(item); | ||
if (i >= 0) | ||
{ | ||
shouldDispose = true; | ||
_disposables[i] = null; | ||
_count--; | ||
|
||
if (_disposables.Capacity > SHRINK_THRESHOLD && _count < _disposables.Capacity / 2) | ||
{ | ||
var old = _disposables; | ||
_disposables = new List<IDisposable>(_disposables.Capacity / 2); | ||
|
||
foreach (var d in old) | ||
if (d != null) | ||
_disposables.Add(d); | ||
} | ||
} | ||
} | ||
} | ||
|
||
if (shouldDispose) | ||
item.Dispose(); | ||
|
||
return shouldDispose; | ||
} | ||
|
||
/// <summary> | ||
/// Disposes all disposables in the group and removes them from the group. | ||
/// </summary> | ||
public void Dispose() | ||
{ | ||
var currentDisposables = default(IDisposable[]); | ||
lock (_gate) | ||
{ | ||
if (!_disposed) | ||
{ | ||
_disposed = true; | ||
currentDisposables = _disposables.ToArray(); | ||
_disposables.Clear(); | ||
_count = 0; | ||
} | ||
} | ||
|
||
if (currentDisposables != null) | ||
{ | ||
foreach (var d in currentDisposables) | ||
if (d != null) | ||
d.Dispose(); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Removes and disposes all disposables from the CompositeDisposable, but does not dispose the CompositeDisposable. | ||
/// </summary> | ||
public void Clear() | ||
{ | ||
var currentDisposables = default(IDisposable[]); | ||
lock (_gate) | ||
{ | ||
currentDisposables = _disposables.ToArray(); | ||
_disposables.Clear(); | ||
_count = 0; | ||
} | ||
|
||
foreach (var d in currentDisposables) | ||
if (d != null) | ||
d.Dispose(); | ||
} | ||
|
||
/// <summary> | ||
/// Determines whether the CompositeDisposable contains a specific disposable. | ||
/// </summary> | ||
/// <param name="item">Disposable to search for.</param> | ||
/// <returns>true if the disposable was found; otherwise, false.</returns> | ||
/// <exception cref="ArgumentNullException"><paramref name="item"/> is null.</exception> | ||
public bool Contains(IDisposable item) | ||
{ | ||
if (item == null) | ||
throw new ArgumentNullException("item"); | ||
|
||
lock (_gate) | ||
{ | ||
return _disposables.Contains(item); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Copies the disposables contained in the CompositeDisposable to an array, starting at a particular array index. | ||
/// </summary> | ||
/// <param name="array">Array to copy the contained disposables to.</param> | ||
/// <param name="arrayIndex">Target index at which to copy the first disposable of the group.</param> | ||
/// <exception cref="ArgumentNullException"><paramref name="array"/> is null.</exception> | ||
/// <exception cref="ArgumentOutOfRangeException"><paramref name="arrayIndex"/> is less than zero. -or - <paramref name="arrayIndex"/> is larger than or equal to the array length.</exception> | ||
public void CopyTo(IDisposable[] array, int arrayIndex) | ||
{ | ||
if (array == null) | ||
throw new ArgumentNullException("array"); | ||
if (arrayIndex < 0 || arrayIndex >= array.Length) | ||
throw new ArgumentOutOfRangeException("arrayIndex"); | ||
|
||
lock (_gate) | ||
{ | ||
Array.Copy(_disposables.Where(d => d != null).ToArray(), 0, array, arrayIndex, | ||
array.Length - arrayIndex); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Always returns false. | ||
/// </summary> | ||
public bool IsReadOnly | ||
{ | ||
get { return false; } | ||
} | ||
|
||
/// <summary> | ||
/// Returns an enumerator that iterates through the CompositeDisposable. | ||
/// </summary> | ||
/// <returns>An enumerator to iterate over the disposables.</returns> | ||
public IEnumerator<IDisposable> GetEnumerator() | ||
{ | ||
var res = default(IEnumerable<IDisposable>); | ||
|
||
lock (_gate) | ||
{ | ||
res = _disposables.Where(d => d != null).ToList(); | ||
} | ||
|
||
return res.GetEnumerator(); | ||
} | ||
|
||
/// <summary> | ||
/// Returns an enumerator that iterates through the CompositeDisposable. | ||
/// </summary> | ||
/// <returns>An enumerator to iterate over the disposables.</returns> | ||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() | ||
{ | ||
return GetEnumerator(); | ||
} | ||
|
||
/// <summary> | ||
/// Gets a value that indicates whether the object is disposed. | ||
/// </summary> | ||
public bool IsDisposed | ||
{ | ||
get { return _disposed; } | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace LINQPad.DumpEditable.Models | ||
{ | ||
public class DumpEditableOptions | ||
{ | ||
public static DumpEditableOptions Defaults => new DumpEditableOptions(); | ||
public bool AutomaticallyKeepQueryRunning { get; set; } = true; | ||
} | ||
} |