// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-#if NET_4_0 || MOBILE
+#if NET_4_0
using System.Collections.Generic;
using System.Collections.Concurrent;
int currId = int.MinValue;
ConcurrentDictionary<CancellationTokenRegistration, Action> callbacks;
+ CancellationTokenRegistration[] linkedTokens;
ManualResetEvent handle;
canceled = true;
handle.Set ();
+ if (linkedTokens != null)
+ UnregisterLinkedTokens ();
List<Exception> exceptions = null;
try {
Action cb;
- for (int id = int.MinValue + 1; id <= currId; id++) {
+ for (int id = currId; id != int.MinValue; id--) {
if (!callbacks.TryRemove (new CancellationTokenRegistration (id, this), out cb))
continue;
if (cb == null)
throw new AggregateException (exceptions);
}
+ /* This is the callback registered on linked tokens
+ * so that they don't throw an ODE if the callback
+ * is called concurrently with a Dispose
+ */
+ void SafeLinkedCancel ()
+ {
+ try {
+ Cancel ();
+ } catch (ObjectDisposedException) {}
+ }
+
#if NET_4_5
public void CancelAfter (TimeSpan delay)
{
throw new ArgumentException ("Empty tokens array");
CancellationTokenSource src = new CancellationTokenSource ();
- Action action = src.Cancel;
+ Action action = src.SafeLinkedCancel;
+ var registrations = new List<CancellationTokenRegistration> (tokens.Length);
foreach (CancellationToken token in tokens) {
if (token.CanBeCanceled)
- token.Register (action);
+ registrations.Add (token.Register (action));
}
+ src.linkedTokens = registrations.ToArray ();
return src;
}
if (!canceled) {
Thread.MemoryBarrier ();
+ UnregisterLinkedTokens ();
callbacks = null;
}
#if NET_4_5
handle.Dispose ();
}
}
+
+ void UnregisterLinkedTokens ()
+ {
+ var registrations = Interlocked.Exchange (ref linkedTokens, null);
+ if (registrations == null)
+ return;
+ foreach (var linked in registrations)
+ linked.Dispose ();
+ }
internal CancellationTokenRegistration Register (Action callback, bool useSynchronizationContext)
{