diff --git a/MvvmCross/Base/IMvxMainThreadAsyncDispatcher.cs b/MvvmCross/Base/IMvxMainThreadAsyncDispatcher.cs new file mode 100644 index 000000000..0828d7bef --- /dev/null +++ b/MvvmCross/Base/IMvxMainThreadAsyncDispatcher.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MS-PL license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Threading.Tasks; + +namespace MvvmCross.Base +{ + // Note: The long term goal should be to deprecate IMvxMainThreadDispatcher + // As such, even though the implementation of this interface also implements + // IMvxMainThreadDispatcher, this interface should not inherit from IMvxMainThreadDispatcher + public interface IMvxMainThreadAsyncDispatcher + { + Task ExecuteOnMainThreadAsync(Action action, bool maskExceptions = true); + Task ExecuteOnMainThreadAsync(Func action, bool maskExceptions = true); + } +} diff --git a/MvvmCross/Base/IMvxMainThreadDispatcher.cs b/MvvmCross/Base/IMvxMainThreadDispatcher.cs index 69f38ed98..360414eaf 100644 --- a/MvvmCross/Base/IMvxMainThreadDispatcher.cs +++ b/MvvmCross/Base/IMvxMainThreadDispatcher.cs @@ -9,12 +9,7 @@ namespace MvvmCross.Base { public interface IMvxMainThreadDispatcher { + [Obsolete("Use IMvxMainThreadAsyncDispatcher.ExecuteOnMainThreadAsync instead")] bool RequestMainThreadAction(Action action, bool maskExceptions = true); } - - public interface IMvxMainThreadAsyncDispatcher - { - Task ExecuteOnMainThreadAsync(Action action, bool maskExceptions = true); - Task ExecuteOnMainThreadAsync(Func action, bool maskExceptions = true); - } } diff --git a/MvvmCross/Base/MvxMainThreadAsyncDispatcher.cs b/MvvmCross/Base/MvxMainThreadAsyncDispatcher.cs new file mode 100644 index 000000000..ccc45926d --- /dev/null +++ b/MvvmCross/Base/MvxMainThreadAsyncDispatcher.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MS-PL license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Threading.Tasks; + +namespace MvvmCross.Base +{ + public abstract class MvxMainThreadAsyncDispatcher : MvxMainThreadDispatcher, IMvxMainThreadAsyncDispatcher + { + public Task ExecuteOnMainThreadAsync(Action action, bool maskExceptions = true) + { + var asyncAction = new Func(() => + { + action(); + return Task.CompletedTask; + }); + return ExecuteOnMainThreadAsync(asyncAction, maskExceptions); + } + + public async Task ExecuteOnMainThreadAsync(Func action, bool maskExceptions = true) + { + var completion = new TaskCompletionSource(); + var syncAction = new Action(async () => + { + await action(); + completion.SetResult(true); + }); + RequestMainThreadAction(syncAction, maskExceptions); + + // If we're already on main thread, then the action will + // have already completed at this point, so can just return + if (completion.Task.IsCompleted) + return; + + // Make sure we don't introduce weird locking issues + // blocking on the completion source by jumping onto + // a new thread to wait + await Task.Run(async () => await completion.Task); + } + } +} diff --git a/MvvmCross/Base/MvxMainThreadDispatcher.cs b/MvvmCross/Base/MvxMainThreadDispatcher.cs index 4a409f794..fcf7be87b 100644 --- a/MvvmCross/Base/MvxMainThreadDispatcher.cs +++ b/MvvmCross/Base/MvxMainThreadDispatcher.cs @@ -4,12 +4,13 @@ using System; using System.Reflection; +using System.Threading.Tasks; using MvvmCross.Exceptions; using MvvmCross.Logging; namespace MvvmCross.Base { - public abstract class MvxMainThreadDispatcher : MvxSingleton + public abstract class MvxMainThreadDispatcher : MvxSingleton, IMvxMainThreadDispatcher { protected static void ExceptionMaskedAction(Action action, bool maskExceptions) { @@ -34,5 +35,7 @@ namespace MvvmCross.Base throw exception; } } + + public abstract bool RequestMainThreadAction(Action action, bool maskExceptions = true); } } diff --git a/MvvmCross/Core/MvxSetup.cs b/MvvmCross/Core/MvxSetup.cs index 514b8dee4..9a560ff50 100644 --- a/MvvmCross/Core/MvxSetup.cs +++ b/MvvmCross/Core/MvxSetup.cs @@ -339,6 +339,7 @@ namespace MvvmCross.Core { var dispatcher = CreateViewDispatcher(); Mvx.RegisterSingleton(dispatcher); + Mvx.RegisterSingleton(dispatcher); Mvx.RegisterSingleton(dispatcher); } diff --git a/MvvmCross/Platforms/Android/Views/MvxAndroidMainThreadDispatcher.cs b/MvvmCross/Platforms/Android/Views/MvxAndroidMainThreadDispatcher.cs index 3b7204ec6..6c060f575 100644 --- a/MvvmCross/Platforms/Android/Views/MvxAndroidMainThreadDispatcher.cs +++ b/MvvmCross/Platforms/Android/Views/MvxAndroidMainThreadDispatcher.cs @@ -10,9 +10,9 @@ using MvvmCross.Base; namespace MvvmCross.Platforms.Android.Views { - public class MvxAndroidMainThreadDispatcher : MvxMainThreadDispatcher + public class MvxAndroidMainThreadDispatcher : MvxMainThreadAsyncDispatcher { - public bool RequestMainThreadAction(Action action, bool maskExceptions = true) + public override bool RequestMainThreadAction(Action action, bool maskExceptions = true) { if (Application.SynchronizationContext == SynchronizationContext.Current) action(); diff --git a/MvvmCross/Platforms/Console/Views/MvxConsoleViewDispatcher.cs b/MvvmCross/Platforms/Console/Views/MvxConsoleViewDispatcher.cs index 969ed679e..e1c36c967 100644 --- a/MvvmCross/Platforms/Console/Views/MvxConsoleViewDispatcher.cs +++ b/MvvmCross/Platforms/Console/Views/MvxConsoleViewDispatcher.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MS-PL license. // See the LICENSE file in the project root for more information. @@ -10,10 +10,10 @@ using MvvmCross.Views; namespace MvvmCross.Platforms.Console.Views { public class MvxConsoleViewDispatcher - : MvxMainThreadDispatcher + : MvxMainThreadAsyncDispatcher , IMvxViewDispatcher { - public bool RequestMainThreadAction(Action action, bool maskExceptions = true) + public override bool RequestMainThreadAction(Action action, bool maskExceptions = true) { action(); return true; diff --git a/MvvmCross/Platforms/Ios/Views/MvxIosUIThreadDispatcher.cs b/MvvmCross/Platforms/Ios/Views/MvxIosUIThreadDispatcher.cs index 03151f0ae..263f011d1 100644 --- a/MvvmCross/Platforms/Ios/Views/MvxIosUIThreadDispatcher.cs +++ b/MvvmCross/Platforms/Ios/Views/MvxIosUIThreadDispatcher.cs @@ -11,7 +11,7 @@ using UIKit; namespace MvvmCross.Platforms.Ios.Views { public abstract class MvxIosUIThreadDispatcher - : MvxMainThreadDispatcher + : MvxMainThreadAsyncDispatcher { private readonly SynchronizationContext _uiSynchronizationContext; @@ -22,7 +22,7 @@ namespace MvvmCross.Platforms.Ios.Views throw new MvxException("SynchronizationContext must not be null - check to make sure Dispatcher is created on UI thread"); } - public bool RequestMainThreadAction(Action action, bool maskExceptions = true) + public override bool RequestMainThreadAction(Action action, bool maskExceptions = true) { if (_uiSynchronizationContext == SynchronizationContext.Current) action(); diff --git a/MvvmCross/Platforms/Mac/Views/MvxMacUIThreadDispatcher.cs b/MvvmCross/Platforms/Mac/Views/MvxMacUIThreadDispatcher.cs index b4dac0511..cee77dbde 100644 --- a/MvvmCross/Platforms/Mac/Views/MvxMacUIThreadDispatcher.cs +++ b/MvvmCross/Platforms/Mac/Views/MvxMacUIThreadDispatcher.cs @@ -12,7 +12,7 @@ using MvvmCross.Exceptions; namespace MvvmCross.Platforms.Mac.Views { public abstract class MvxMacUIThreadDispatcher - : MvxMainThreadDispatcher + : MvxMainThreadAsyncDispatcher { private readonly SynchronizationContext _uiSynchronizationContext; @@ -23,7 +23,7 @@ namespace MvvmCross.Platforms.Mac.Views throw new MvxException("SynchronizationContext must not be null - check to make sure Dispatcher is created on UI thread"); } - public bool RequestMainThreadAction(Action action, + public override bool RequestMainThreadAction(Action action, bool maskExceptions = true) { if (_uiSynchronizationContext == SynchronizationContext.Current) diff --git a/MvvmCross/Platforms/Tvos/Views/MvxTvosUIThreadDispatcher.cs b/MvvmCross/Platforms/Tvos/Views/MvxTvosUIThreadDispatcher.cs index c7c6158a2..83b051d2c 100644 --- a/MvvmCross/Platforms/Tvos/Views/MvxTvosUIThreadDispatcher.cs +++ b/MvvmCross/Platforms/Tvos/Views/MvxTvosUIThreadDispatcher.cs @@ -11,7 +11,7 @@ using UIKit; namespace MvvmCross.Platforms.Tvos.Views { public abstract class MvxTvosUIThreadDispatcher - : MvxMainThreadDispatcher + : MvxMainThreadAsyncDispatcher { private readonly SynchronizationContext _uiSynchronizationContext; @@ -22,7 +22,7 @@ namespace MvvmCross.Platforms.Tvos.Views throw new MvxException("SynchronizationContext must not be null - check to make sure Dispatcher is created on UI thread"); } - public bool RequestMainThreadAction(Action action, bool maskExceptions = true) + public override bool RequestMainThreadAction(Action action, bool maskExceptions = true) { if (_uiSynchronizationContext == SynchronizationContext.Current) action(); diff --git a/MvvmCross/Platforms/Uap/Views/MvxWindowsMainThreadDispatcher.cs b/MvvmCross/Platforms/Uap/Views/MvxWindowsMainThreadDispatcher.cs index f36c87969..0a7ba28f6 100644 --- a/MvvmCross/Platforms/Uap/Views/MvxWindowsMainThreadDispatcher.cs +++ b/MvvmCross/Platforms/Uap/Views/MvxWindowsMainThreadDispatcher.cs @@ -8,7 +8,7 @@ using MvvmCross.Base; namespace MvvmCross.Platforms.Uap.Views { - public class MvxWindowsMainThreadDispatcher : MvxMainThreadDispatcher + public class MvxWindowsMainThreadDispatcher : MvxMainThreadAsyncDispatcher { private readonly CoreDispatcher _uiDispatcher; @@ -17,7 +17,7 @@ namespace MvvmCross.Platforms.Uap.Views _uiDispatcher = uiDispatcher; } - public bool RequestMainThreadAction(Action action, bool maskExceptions = true) + public override bool RequestMainThreadAction(Action action, bool maskExceptions = true) { if (_uiDispatcher.HasThreadAccess) { diff --git a/MvvmCross/Platforms/Wpf/Views/MvxWpfUIThreadDispatcher.cs b/MvvmCross/Platforms/Wpf/Views/MvxWpfUIThreadDispatcher.cs index fe754885a..18bed094f 100644 --- a/MvvmCross/Platforms/Wpf/Views/MvxWpfUIThreadDispatcher.cs +++ b/MvvmCross/Platforms/Wpf/Views/MvxWpfUIThreadDispatcher.cs @@ -9,7 +9,7 @@ using MvvmCross.Base; namespace MvvmCross.Platforms.Wpf.Views { public class MvxWpfUIThreadDispatcher - : MvxMainThreadDispatcher + : MvxMainThreadAsyncDispatcher { private readonly Dispatcher _dispatcher; @@ -18,7 +18,7 @@ namespace MvvmCross.Platforms.Wpf.Views _dispatcher = dispatcher; } - public bool RequestMainThreadAction(Action action, bool maskExceptions = true) + public override bool RequestMainThreadAction(Action action, bool maskExceptions = true) { if (_dispatcher.CheckAccess()) { diff --git a/MvvmCross/Views/IMvxViewDispatcher.cs b/MvvmCross/Views/IMvxViewDispatcher.cs index e8e513dde..90f5c4abf 100644 --- a/MvvmCross/Views/IMvxViewDispatcher.cs +++ b/MvvmCross/Views/IMvxViewDispatcher.cs @@ -7,10 +7,10 @@ using MvvmCross.ViewModels; namespace MvvmCross.Views { - public interface IMvxViewDispatcher : IMvxMainThreadDispatcher + public interface IMvxViewDispatcher : IMvxMainThreadDispatcher, IMvxMainThreadAsyncDispatcher { bool ShowViewModel(MvxViewModelRequest request); bool ChangePresentation(MvxPresentationHint hint); } -} \ No newline at end of file +} diff --git a/UnitTests/MvvmCross.UnitTest/Mocks/Dispatchers/CallbackMockMainThreadDispatcher.cs b/UnitTests/MvvmCross.UnitTest/Mocks/Dispatchers/CallbackMockMainThreadDispatcher.cs index 016516fff..5e8f418a9 100644 --- a/UnitTests/MvvmCross.UnitTest/Mocks/Dispatchers/CallbackMockMainThreadDispatcher.cs +++ b/UnitTests/MvvmCross.UnitTest/Mocks/Dispatchers/CallbackMockMainThreadDispatcher.cs @@ -8,7 +8,7 @@ using MvvmCross.Base; namespace MvvmCross.UnitTest.Mocks.Dispatchers { public class CallbackMockMainThreadDispatcher - : MvxMainThreadDispatcher, IMvxMainThreadDispatcher + : MvxMainThreadAsyncDispatcher { private readonly Func _callback; @@ -17,7 +17,7 @@ namespace MvvmCross.UnitTest.Mocks.Dispatchers _callback = callback; } - public virtual bool RequestMainThreadAction(Action action, + public override bool RequestMainThreadAction(Action action, bool maskExceptions = true) { return _callback(action); diff --git a/UnitTests/MvvmCross.UnitTest/Mocks/Dispatchers/CountingMockMainThreadDispatcher.cs b/UnitTests/MvvmCross.UnitTest/Mocks/Dispatchers/CountingMockMainThreadDispatcher.cs index d700784b5..62cce3dc2 100644 --- a/UnitTests/MvvmCross.UnitTest/Mocks/Dispatchers/CountingMockMainThreadDispatcher.cs +++ b/UnitTests/MvvmCross.UnitTest/Mocks/Dispatchers/CountingMockMainThreadDispatcher.cs @@ -8,11 +8,11 @@ using MvvmCross.Base; namespace MvvmCross.UnitTest.Mocks.Dispatchers { public class CountingMockMainThreadDispatcher - : MvxMainThreadDispatcher, IMvxMainThreadDispatcher + : MvxMainThreadAsyncDispatcher { public int Count { get; set; } - public bool RequestMainThreadAction(Action action, bool maskExceptions = true) + public override bool RequestMainThreadAction(Action action, bool maskExceptions = true) { Count++; return true; diff --git a/UnitTests/MvvmCross.UnitTest/Mocks/Dispatchers/InlineMockMainThreadDispatcher.cs b/UnitTests/MvvmCross.UnitTest/Mocks/Dispatchers/InlineMockMainThreadDispatcher.cs index 8d8dc63be..803e3480c 100644 --- a/UnitTests/MvvmCross.UnitTest/Mocks/Dispatchers/InlineMockMainThreadDispatcher.cs +++ b/UnitTests/MvvmCross.UnitTest/Mocks/Dispatchers/InlineMockMainThreadDispatcher.cs @@ -8,9 +8,9 @@ using MvvmCross.Base; namespace MvvmCross.UnitTest.Mocks.Dispatchers { public class InlineMockMainThreadDispatcher - : MvxMainThreadDispatcher, IMvxMainThreadDispatcher + : MvxMainThreadAsyncDispatcher { - public virtual bool RequestMainThreadAction(Action action, + public override bool RequestMainThreadAction(Action action, bool maskExceptions = true) { action(); diff --git a/UnitTests/MvvmCross.UnitTest/Mocks/Dispatchers/NavigationMockDispatcher.cs b/UnitTests/MvvmCross.UnitTest/Mocks/Dispatchers/NavigationMockDispatcher.cs index 137a53c7d..ae6f18626 100644 --- a/UnitTests/MvvmCross.UnitTest/Mocks/Dispatchers/NavigationMockDispatcher.cs +++ b/UnitTests/MvvmCross.UnitTest/Mocks/Dispatchers/NavigationMockDispatcher.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using MvvmCross.Base; using MvvmCross.Logging; using MvvmCross.Tests; @@ -26,7 +27,7 @@ namespace MvvmCross.UnitTest.Mocks.Dispatchers return true; } - public virtual bool ShowViewModel(MvxViewModelRequest request) + public virtual Task ShowViewModel(MvxViewModelRequest request) { var debugString = $"ShowViewModel: '{request.ViewModelType.Name}' "; if (request.ParameterValues != null) @@ -36,13 +37,13 @@ namespace MvvmCross.UnitTest.Mocks.Dispatchers MvxTestLog.Instance.Log(MvxLogLevel.Debug, () => debugString); Requests.Add(request); - return true; + return Task.FromResult(true); } - public virtual bool ChangePresentation(MvxPresentationHint hint) + public virtual Task ChangePresentation(MvxPresentationHint hint) { Hints.Add(hint); - return true; + return Task.FromResult(true); } }