using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Windows.System;
namespace NotificationFlyoutSample
{
public static class DispatcherQueueExtensions
{
///
/// Indicates whether or not is available.
///
private static bool IsHasThreadAccessPropertyAvailable => true;
public static Task EnqueueAsync(this DispatcherQueue dispatcher, Action function, DispatcherQueuePriority priority = DispatcherQueuePriority.Normal)
{
// Run the function directly when we have thread access.
// Also reuse Task.CompletedTask in case of success,
// to skip an unnecessary heap allocation for every invocation.
if (IsHasThreadAccessPropertyAvailable && dispatcher.HasThreadAccess)
{
try
{
function();
return Task.CompletedTask;
}
catch (Exception e)
{
return Task.FromException(e);
}
}
static Task TryEnqueueAsync(DispatcherQueue dispatcher, Action function, DispatcherQueuePriority priority)
{
var taskCompletionSource = new TaskCompletionSource();
if (!dispatcher.TryEnqueue(priority, () =>
{
try
{
function();
taskCompletionSource.SetResult(null);
}
catch (Exception e)
{
taskCompletionSource.SetException(e);
}
}))
{
taskCompletionSource.SetException(GetEnqueueException("Failed to enqueue the operation"));
}
return taskCompletionSource.Task;
}
return TryEnqueueAsync(dispatcher, function, priority);
}
///
/// Invokes a given function on the target and returns a
/// that completes when the invocation of the function is completed.
///
/// The return type of to relay through the returned .
/// The target to invoke the code on.
/// The to invoke.
/// The priority level for the function to invoke.
/// A that completes when the invocation of is over.
/// If the current thread has access to , will be invoked directly.
public static Task EnqueueAsync(this DispatcherQueue dispatcher, Func function, DispatcherQueuePriority priority = DispatcherQueuePriority.Normal)
{
if (IsHasThreadAccessPropertyAvailable && dispatcher.HasThreadAccess)
{
try
{
return Task.FromResult(function());
}
catch (Exception e)
{
return Task.FromException(e);
}
}
static Task TryEnqueueAsync(DispatcherQueue dispatcher, Func function, DispatcherQueuePriority priority)
{
var taskCompletionSource = new TaskCompletionSource();
if (!dispatcher.TryEnqueue(priority, () =>
{
try
{
taskCompletionSource.SetResult(function());
}
catch (Exception e)
{
taskCompletionSource.SetException(e);
}
}))
{
taskCompletionSource.SetException(GetEnqueueException("Failed to enqueue the operation"));
}
return taskCompletionSource.Task;
}
return TryEnqueueAsync(dispatcher, function, priority);
}
///
/// Invokes a given function on the target and returns a
/// that acts as a proxy for the one returned by the given function.
///
/// The target to invoke the code on.
/// The to invoke.
/// The priority level for the function to invoke.
/// A that acts as a proxy for the one returned by .
/// If the current thread has access to , will be invoked directly.
public static Task EnqueueAsync(this DispatcherQueue dispatcher, Func function, DispatcherQueuePriority priority = DispatcherQueuePriority.Normal)
{
// If we have thread access, we can retrieve the task directly.
// We don't use ConfigureAwait(false) in this case, in order
// to let the caller continue its execution on the same thread
// after awaiting the task returned by this function.
if (IsHasThreadAccessPropertyAvailable && dispatcher.HasThreadAccess)
{
try
{
if (function() is Task awaitableResult)
{
return awaitableResult;
}
return Task.FromException(GetEnqueueException("The Task returned by function cannot be null."));
}
catch (Exception e)
{
return Task.FromException(e);
}
}
static Task TryEnqueueAsync(DispatcherQueue dispatcher, Func function, DispatcherQueuePriority priority)
{
var taskCompletionSource = new TaskCompletionSource();
if (!dispatcher.TryEnqueue(priority, async () =>
{
try
{
if (function() is Task awaitableResult)
{
await awaitableResult.ConfigureAwait(false);
taskCompletionSource.SetResult(null);
}
else
{
taskCompletionSource.SetException(GetEnqueueException("The Task returned by function cannot be null."));
}
}
catch (Exception e)
{
taskCompletionSource.SetException(e);
}
}))
{
taskCompletionSource.SetException(GetEnqueueException("Failed to enqueue the operation"));
}
return taskCompletionSource.Task;
}
return TryEnqueueAsync(dispatcher, function, priority);
}
///
/// Invokes a given function on the target and returns a
/// that acts as a proxy for the one returned by the given function.
///
/// The return type of to relay through the returned .
/// The target to invoke the code on.
/// The to invoke.
/// The priority level for the function to invoke.
/// A that relays the one returned by .
/// If the current thread has access to , will be invoked directly.
public static Task EnqueueAsync(this DispatcherQueue dispatcher, Func> function, DispatcherQueuePriority priority = DispatcherQueuePriority.Normal)
{
if (IsHasThreadAccessPropertyAvailable && dispatcher.HasThreadAccess)
{
try
{
if (function() is Task awaitableResult)
{
return awaitableResult;
}
return Task.FromException(GetEnqueueException("The Task returned by function cannot be null."));
}
catch (Exception e)
{
return Task.FromException(e);
}
}
static Task TryEnqueueAsync(DispatcherQueue dispatcher, Func> function, DispatcherQueuePriority priority)
{
var taskCompletionSource = new TaskCompletionSource();
if (!dispatcher.TryEnqueue(priority, async () =>
{
try
{
if (function() is Task awaitableResult)
{
var result = await awaitableResult.ConfigureAwait(false);
taskCompletionSource.SetResult(result);
}
else
{
taskCompletionSource.SetException(GetEnqueueException("The Task returned by function cannot be null."));
}
}
catch (Exception e)
{
taskCompletionSource.SetException(e);
}
}))
{
taskCompletionSource.SetException(GetEnqueueException("Failed to enqueue the operation"));
}
return taskCompletionSource.Task;
}
return TryEnqueueAsync(dispatcher, function, priority);
}
///
/// Creates an to return when an enqueue operation fails.
///
/// The message of the exception.
/// An with a specified message.
[MethodImpl(MethodImplOptions.NoInlining)]
private static InvalidOperationException GetEnqueueException(string message)
{
return new InvalidOperationException(message);
}
}
}