diff --git a/Source/Csla.Shared/Csla.Shared.projitems b/Source/Csla.Shared/Csla.Shared.projitems index 40595e8c88..6b4ee25034 100644 --- a/Source/Csla.Shared/Csla.Shared.projitems +++ b/Source/Csla.Shared/Csla.Shared.projitems @@ -328,7 +328,7 @@ - + diff --git a/Source/Csla.Shared/DataPortalClient/HttpProxy.cs b/Source/Csla.Shared/DataPortalClient/HttpProxy.cs index 79e5bdfb7d..7e05cd2cc8 100644 --- a/Source/Csla.Shared/DataPortalClient/HttpProxy.cs +++ b/Source/Csla.Shared/DataPortalClient/HttpProxy.cs @@ -9,6 +9,7 @@ using Csla.Core; using Csla.Serialization.Mobile; using Csla.Server; +using Csla.Threading; using System; using System.Collections.Generic; using System.Linq; @@ -395,11 +396,11 @@ public async Task Delete(Type objectType, object criteria, Dat private async Task CallDataPortalServer(HttpClient client, byte[] serialized, string operation, string routingToken, bool isSync) { + var task = CallDataPortalServer(client, serialized, operation, routingToken); if (isSync) - serialized = Threading.SyncTask.Run(new Func>( - async () => await CallDataPortalServer(client, serialized, operation, routingToken))); + serialized = task.RunWithContext(_client.Timeout); else - serialized = await CallDataPortalServer(client, serialized, operation, routingToken); + serialized = await task; return serialized; } diff --git a/Source/Csla.Shared/Threading/ContextParams.cs b/Source/Csla.Shared/Threading/ContextParams.cs index e1c9731808..757d320479 100644 --- a/Source/Csla.Shared/Threading/ContextParams.cs +++ b/Source/Csla.Shared/Threading/ContextParams.cs @@ -40,13 +40,8 @@ internal void SetThreadContext() { Csla.ApplicationContext.User = User; Csla.ApplicationContext.SetContext(ClientContext, GlobalContext); -#if NETFX_CORE - // do nothing because we can't set the context on a non-UI thread - // in WinRT or UWP -#else Thread.CurrentThread.CurrentUICulture = UICulture; Thread.CurrentThread.CurrentCulture = Culture; -#endif } } } \ No newline at end of file diff --git a/Source/Csla.Shared/Threading/SyncTask.cs b/Source/Csla.Shared/Threading/SyncTask.cs deleted file mode 100644 index 9c4835327e..0000000000 --- a/Source/Csla.Shared/Threading/SyncTask.cs +++ /dev/null @@ -1,38 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) Marimer LLC. All rights reserved. -// Website: http://www.lhotka.net/cslanet/ -// -// -//----------------------------------------------------------------------- -using System; -using System.Globalization; -using System.Security.Principal; -using System.Threading; -using System.Threading.Tasks; - -namespace Csla.Threading -{ - /// - /// Run an async operation synchronously without - /// blocking the UI thread. - /// - public static class SyncTask - { - private static readonly TaskFactory _myTaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default); - - /// - /// Run an async function synchronously without - /// blocking the UI thread. - /// - public static T Run(Func> func) - { - var context = new ContextParams(); - return _myTaskFactory.StartNew>(delegate - { - context.SetThreadContext(); - return func(); - }).Unwrap().GetAwaiter().GetResult(); - } - } -} diff --git a/Source/Csla.Shared/Threading/TaskExtensions.cs b/Source/Csla.Shared/Threading/TaskExtensions.cs new file mode 100644 index 0000000000..f68a147984 --- /dev/null +++ b/Source/Csla.Shared/Threading/TaskExtensions.cs @@ -0,0 +1,99 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: http://www.lhotka.net/cslanet/ +// +// +//----------------------------------------------------------------------- +using System; +using System.Globalization; +using System.Security.Principal; +using System.Threading; +using System.Threading.Tasks; + +namespace Csla.Threading +{ + /// + /// Run an async operation synchronously without + /// blocking the UI thread. + /// + public static class TaskExtensions + { + private static readonly TaskFactory _myTaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default); + + /// + /// Run an async function synchronously without + /// blocking the UI thread. + /// + /// + /// The async function is run on a thread in the thread + /// pool, the result is marshalled back to the calling + /// thread. The calling thread's context is carried + /// onto the background thread. + /// + /// Task to run synchronously. + public static T RunWithContext(this Task task) + { + var context = new ContextParams(); + var background = Task.Run(() => + { + context.SetThreadContext(); + task.RunSynchronously(); + }); + SpinWait(background); + return task.Result; + } + + /// + /// Run an async function synchronously without + /// blocking the UI thread. + /// + /// + /// The async function is run on a thread in the thread + /// pool, the result is marshalled back to the calling + /// thread. The calling thread's context is carried + /// onto the background thread. + /// + /// Task to run synchronously. + /// Max time to wait for completion. + public static T RunWithContext(this Task task, TimeSpan timeout) + { + var context = new ContextParams(); + var background = Task.Run(() => + { + context.SetThreadContext(); + task.RunSynchronously(); + }); + SpinWait(background, timeout); + return task.Result; + } + + /// + /// Wait synchronously (spinwait) for the task to complete. + /// + /// Task on which to wait. + public static void SpinWait(this Task task) + { + while (!task.IsCompleted) + { + Thread.Sleep(1); + } + } + + /// + /// Wait synchronously (spinwait) for the task to complete. + /// + /// Task on which to wait. + /// Timeout value + public static void SpinWait(this Task task, TimeSpan timeout) + { + var deadline = DateTime.Now + timeout; + while (!task.IsCompleted) + { + if (DateTime.Now > deadline) + throw new TimeoutException(); + Thread.Sleep(1); + } + } + } +} diff --git a/Source/csla.netcore.test/Threading/TaskExtenstionTests.cs b/Source/csla.netcore.test/Threading/TaskExtenstionTests.cs new file mode 100644 index 0000000000..1bdebcfc4e --- /dev/null +++ b/Source/csla.netcore.test/Threading/TaskExtenstionTests.cs @@ -0,0 +1,62 @@ +//----------------------------------------------------------------------- +// +// Copyright (c) Marimer LLC. All rights reserved. +// Website: http://www.lhotka.net/cslanet/ +// +// no summary +//----------------------------------------------------------------------- +using System; +using System.Threading.Tasks; + +#if !NUNIT +using Microsoft.VisualStudio.TestTools.UnitTesting; +#else +using NUnit.Framework; +using TestClass = NUnit.Framework.TestFixtureAttribute; +using TestInitialize = NUnit.Framework.SetUpAttribute; +using TestCleanup = NUnit.Framework.TearDownAttribute; +using TestMethod = NUnit.Framework.TestAttribute; +#endif +using Csla.Threading; + +namespace Csla.Test.Threading +{ + [TestClass] + public class TaskExtenstionTests + { + [TestMethod] + public void SpinWait() + { + int x = 0; + var task = Task.Run(() => { System.Threading.Thread.Sleep(10); x = 42; }); + task.SpinWait(); + Assert.AreEqual(42, x); + } + + [TestMethod] + [ExpectedException(typeof(TimeoutException))] + public void SpinWaitTimeout() + { + var task = Task.Run(() => System.Threading.Thread.Sleep(10)); + task.SpinWait(new TimeSpan(0, 0, 0)); + } + + [TestMethod] + public void RunWithContext() + { + int x = 0; + ApplicationContext.ClientContext["x"] = 42; + var task = new Task(() => (int)ApplicationContext.ClientContext["x"]); + x = task.RunWithContext(); + Assert.AreEqual(42, x); + } + + [TestMethod] + [ExpectedException(typeof(TimeoutException))] + public void RunWithContextTimeout() + { + var task = new Task(() => { System.Threading.Thread.Sleep(10); return 42; }); + int x = task.RunWithContext(new TimeSpan(0, 0, 0)); + } + } +} diff --git a/Source/csla.netcore.test/csla.netcore.test.csproj b/Source/csla.netcore.test/csla.netcore.test.csproj index f9eab4141e..11e2d0c18b 100644 --- a/Source/csla.netcore.test/csla.netcore.test.csproj +++ b/Source/csla.netcore.test/csla.netcore.test.csproj @@ -4,6 +4,8 @@ netcoreapp2.1 false + + Csla.Test