Skip to content

Commit

Permalink
MarimerLLC#1059 Add TaskExtensions and tests; use in HttpProxy
Browse files Browse the repository at this point in the history
  • Loading branch information
rockfordlhotka committed Apr 2, 2019
1 parent 9576666 commit e5f3998
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 47 deletions.
2 changes: 1 addition & 1 deletion Source/Csla.Shared/Csla.Shared.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Threading\BusyLock.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Threading\ContextParams.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Threading\CslaTaskScheduler.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Threading\SyncTask.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Threading\TaskExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)TransactionalAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)TransactionalTypes.cs" />
<Compile Include="$(MSBuildThisFileDirectory)TransactionIsolationLevel.cs" />
Expand Down
7 changes: 4 additions & 3 deletions Source/Csla.Shared/DataPortalClient/HttpProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -395,11 +396,11 @@ public async Task<DataPortalResult> Delete(Type objectType, object criteria, Dat

private async Task<byte[]> 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<Task<byte[]>>(
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;
}

Expand Down
5 changes: 0 additions & 5 deletions Source/Csla.Shared/Threading/ContextParams.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}
38 changes: 0 additions & 38 deletions Source/Csla.Shared/Threading/SyncTask.cs

This file was deleted.

99 changes: 99 additions & 0 deletions Source/Csla.Shared/Threading/TaskExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//-----------------------------------------------------------------------
// <copyright file="TaskExtensions.cs" company="Marimer LLC">
// Copyright (c) Marimer LLC. All rights reserved.
// Website: http://www.lhotka.net/cslanet/
// </copyright>
// <summary></summary>
//-----------------------------------------------------------------------
using System;
using System.Globalization;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;

namespace Csla.Threading
{
/// <summary>
/// Run an async operation synchronously without
/// blocking the UI thread.
/// </summary>
public static class TaskExtensions
{
private static readonly TaskFactory _myTaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);

/// <summary>
/// Run an async function synchronously without
/// blocking the UI thread.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <param name="task">Task to run synchronously.</param>
public static T RunWithContext<T>(this Task<T> task)
{
var context = new ContextParams();
var background = Task.Run(() =>
{
context.SetThreadContext();
task.RunSynchronously();
});
SpinWait(background);
return task.Result;
}

/// <summary>
/// Run an async function synchronously without
/// blocking the UI thread.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <param name="task">Task to run synchronously.</param>
/// <param name="timeout">Max time to wait for completion.</param>
public static T RunWithContext<T>(this Task<T> task, TimeSpan timeout)
{
var context = new ContextParams();
var background = Task.Run(() =>
{
context.SetThreadContext();
task.RunSynchronously();
});
SpinWait(background, timeout);
return task.Result;
}

/// <summary>
/// Wait synchronously (spinwait) for the task to complete.
/// </summary>
/// <param name="task">Task on which to wait.</param>
public static void SpinWait(this Task task)
{
while (!task.IsCompleted)
{
Thread.Sleep(1);
}
}

/// <summary>
/// Wait synchronously (spinwait) for the task to complete.
/// </summary>
/// <param name="task">Task on which to wait.</param>
/// <param name="timeout">Timeout value</param>
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);
}
}
}
}
62 changes: 62 additions & 0 deletions Source/csla.netcore.test/Threading/TaskExtenstionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//-----------------------------------------------------------------------
// <copyright file="TaskExtenstionTests.cs" company="Marimer LLC">
// Copyright (c) Marimer LLC. All rights reserved.
// Website: http://www.lhotka.net/cslanet/
// </copyright>
// <summary>no summary</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>(() => (int)ApplicationContext.ClientContext["x"]);
x = task.RunWithContext();
Assert.AreEqual(42, x);
}

[TestMethod]
[ExpectedException(typeof(TimeoutException))]
public void RunWithContextTimeout()
{
var task = new Task<int>(() => { System.Threading.Thread.Sleep(10); return 42; });
int x = task.RunWithContext(new TimeSpan(0, 0, 0));
}
}
}
2 changes: 2 additions & 0 deletions Source/csla.netcore.test/csla.netcore.test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
<TargetFramework>netcoreapp2.1</TargetFramework>

<IsPackable>false</IsPackable>

<RootNamespace>Csla.Test</RootNamespace>
</PropertyGroup>

<ItemGroup>
Expand Down

0 comments on commit e5f3998

Please sign in to comment.