diff --git a/README.md b/README.md
new file mode 100644
index 0000000..82b7cb8
--- /dev/null
+++ b/README.md
@@ -0,0 +1,18 @@
+# Uno.Core
+
+Uno.Core is nventive's attempt at filling some gaps in the existing .net framework and related technologies; hence reducing friction
+and increasing the predictability of the api. It consists of a set of helpers, extension methods and additional abstractions.
+
+## Build status
+
+| Target | Branch | Status | Usage |
+| ------ | ------ | ------ | ----- |
+| development | master |[](https://ci.appveyor.com/project/nventivedevops/uno-core/branch/master) | For developing and testing |
+| beta | beta |[](https://ci.appveyor.com/project/nventivedevops/uno-core/branch/beta) | Testing before release as stable |
+| release | stable |[](https://ci.appveyor.com/project/nventivedevops/uno-core/branch/stable) | Last stable version |
+
+## Package
+[](https://www.nuget.org/packages/Uno.Core/)
+
+## Documentation
+_Coming soon._
\ No newline at end of file
diff --git a/Readme.md b/Readme.md
deleted file mode 100644
index 17d01d5..0000000
--- a/Readme.md
+++ /dev/null
@@ -1,13 +0,0 @@
-# Uno.Core
-
-Uno.Core is nventive's attempt at filling some gaps in the existing .net framework and related technologies; hence reducing friction
-and increasing the predictability of the api. It consists of a set of helpers, extension methods and additional abstractions.
-
-## Build status
-
-| Target | Branch | Status | Recommended Nuget packages version |
-| ------ | ------ | ------ | ------ |
-| development | master |[](https://ci.appveyor.com/project/nventivedevops/uno-core/branch/master) | [](https://www.nuget.org/packages/Uno.Core/) |
-
-## Documentation
-_Coming soon._
\ No newline at end of file
diff --git a/src/Uno.Core.Tests/Extensions/StringExtensionsFixture.cs b/src/Uno.Core.Tests/Extensions/StringExtensionsFixture.cs
index 9b42bac..48cac58 100644
--- a/src/Uno.Core.Tests/Extensions/StringExtensionsFixture.cs
+++ b/src/Uno.Core.Tests/Extensions/StringExtensionsFixture.cs
@@ -315,15 +315,27 @@ public void StringFormatsErrorHandlingAreIdentical()
[TestMethod]
public void CustomStringFormatsWithMultipleValues()
{
- Assert.AreEqual(
- StringExtensions.Format("For {0} and {1}?", 42, 3.1416),
- "For 42 and 3.1416?");
- Assert.AreEqual(
- StringExtensions.Format("For {0:#.##;(#.##);ZERO} and {1:#.##;(#.##);ZERO}?", 42, 3.1416),
- "For 42 and 3.14?");
- Assert.AreEqual(
- StringExtensions.Format("For {0:#.##;(#.##);ZERO;ONE}, {1:#.##;(#.##);ZERO;ONE} and {2:#.##;(#.##);ZERO;ONE}?", 42, 3.1416, 1.004),
- "For 42, 3.14 and ONE?");
+ var previous = CultureInfo.CurrentUICulture;
+ try
+ {
+ CultureInfo.CurrentUICulture = new CultureInfo("en-US", false);
+
+ Assert.AreEqual(
+ StringExtensions.Format("For {0} and {1}?", 42, 3.1416),
+ "For 42 and 3.1416?");
+ Assert.AreEqual(
+ StringExtensions.Format("For {0:#.##;(#.##);ZERO} and {1:#.##;(#.##);ZERO}?", 42, 3.1416),
+ "For 42 and 3.14?");
+ Assert.AreEqual(
+ StringExtensions.Format(
+ "For {0:#.##;(#.##);ZERO;ONE}, {1:#.##;(#.##);ZERO;ONE} and {2:#.##;(#.##);ZERO;ONE}?", 42,
+ 3.1416, 1.004),
+ "For 42, 3.14 and ONE?");
+ }
+ finally
+ {
+ CultureInfo.CurrentUICulture = previous;
+ }
}
}
}
diff --git a/src/Uno.Core.Tests/Threading/LockAsyncFixture.cs b/src/Uno.Core.Tests/Threading/LockAsyncFixture.cs
new file mode 100644
index 0000000..1c2c36d
--- /dev/null
+++ b/src/Uno.Core.Tests/Threading/LockAsyncFixture.cs
@@ -0,0 +1,115 @@
+// ******************************************************************
+// Copyright � 2015-2018 nventive inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// ******************************************************************
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Uno.Threading;
+
+namespace Uno.Core.Tests.Threading
+{
+ [TestClass]
+ public class LockAsyncFixture
+ {
+ [TestMethod]
+ public async Task BasicLock()
+ {
+ var gate = new AsyncLock();
+ var sync = new object();
+ var gate2 = new object();
+ var current = 0;
+ var maxCurrent = 0;
+ var waiting = 0;
+
+ var func1 = Funcs.Create(async ct => {
+
+ await Task.Yield();
+
+ lock (sync)
+ {
+ waiting++;
+ Monitor.Wait(sync);
+ }
+
+ using(await gate.LockAsync(ct))
+ {
+ lock (gate2)
+ {
+ current++;
+ maxCurrent = Math.Max(current, maxCurrent);
+ }
+
+ await Task.Delay(100);
+
+ lock (gate2)
+ {
+ current--;
+ }
+ }
+
+ return 42;
+ });
+
+ var c1 = func1(CancellationToken.None);
+ var c2 = func1(CancellationToken.None);
+
+ while (waiting != 2)
+ {
+ await Task.Delay(100);
+ }
+
+ lock(sync)
+ {
+ Monitor.PulseAll(sync);
+ }
+
+ await c1;
+ await c2;
+
+ Assert.AreEqual(1, maxCurrent);
+ }
+
+ [TestMethod]
+ public async Task CancelledLock()
+ {
+ var gate = new AsyncLock();
+
+ var func = Funcs.Create(async (int wait, CancellationToken ct) => {
+
+ using(await gate.LockAsync(ct))
+ {
+ await Task.Delay(wait, ct);
+ }
+
+ return 42;
+ });
+
+ using (await gate.LockAsync(CancellationToken.None))
+ {
+ var cts = new CancellationTokenSource();
+ var r1 = func(100000, cts.Token);
+
+ cts.Cancel();
+
+ Assert.AreEqual(TaskStatus.Canceled, r1.Status);
+ }
+
+ var r2 = await func(1000, CancellationToken.None);
+ Assert.AreEqual(42, r2);
+ }
+ }
+}
diff --git a/src/Uno.Core/Threading/AsyncLock.cs b/src/Uno.Core/Threading/AsyncLock.cs
index bf74c20..6357618 100644
--- a/src/Uno.Core/Threading/AsyncLock.cs
+++ b/src/Uno.Core/Threading/AsyncLock.cs
@@ -22,23 +22,23 @@
namespace Uno.Threading
{
- ///
- /// An asynchronous lock, that can be used in conjuction with C# async/await
- ///
- public sealed class AsyncLock
- {
- private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
+ ///
+ /// An asynchronous lock, that can be used in conjunction with C# async/await
+ ///
+ public sealed class AsyncLock
+ {
+ private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
- ///
- /// Acquires the lock, then provides a disposable to release it.
- ///
- /// A cancellation token to cancel the lock
- /// An IDisposable instance that allows the release of the lock.
- public async Task LockAsync(CancellationToken ct)
- {
- await _semaphore.WaitAsync(ct);
+ ///
+ /// Acquires the lock, then provides a disposable to release it.
+ ///
+ /// A cancellation token to cancel the lock
+ /// An IDisposable instance that allows the release of the lock.
+ public async Task LockAsync(CancellationToken ct)
+ {
+ await _semaphore.WaitAsync(ct);
- return Disposable.Create(() => _semaphore.Release());
- }
- }
+ return Disposable.Create(() => _semaphore.Release());
+ }
+ }
}
diff --git a/src/Uno.Core/Threading/FastAsyncLock.cs b/src/Uno.Core/Threading/FastAsyncLock.cs
index 2ffaa6d..e796572 100644
--- a/src/Uno.Core/Threading/FastAsyncLock.cs
+++ b/src/Uno.Core/Threading/FastAsyncLock.cs
@@ -23,7 +23,7 @@
namespace Uno.Threading
{
///
- /// An re-entrant asynchronous lock, that can be used in conjuction with C# async/await
+ /// An re-entrant asynchronous lock, that can be used in conjunction with C# async/await
///
public sealed class FastAsyncLock
{
@@ -159,7 +159,7 @@ void Abort()
private void Dequeued()
{
- // Dequeing may occures more than once. So move to next step only if item is really waiting!
+ // Dequeing may occured more than once. So move to next step only if item is really waiting!
switch (Interlocked.CompareExchange(ref _state, State.Entered, State.Waiting))
{
case State.Waiting:
@@ -201,7 +201,7 @@ public void Exit()
// As we are running asynchronous, it may occures that the inner task completes from another execution context (e.g. an event).
// So unlike the synchronous lock which must be exited from the same execution context / thread (otherwise we receive an SynchronizationLockException),
// here we cannot enforce the 'Exit' to be invoked only from the entering execution context (ie. check that '_owner._localMonitor.Value == this')
- // Consequently, we have to handle concurrency with ReEntrency (Enter{Sync|Async} are not impacted since for those the caller did not received an 'Handle' yet)
+ // Consequently, we have to handle concurrency with Reentrency (Enter{Sync|Async} are not impacted since for those the caller did not received an 'Handle' yet)
if (Interlocked.Decrement(ref _count) == 0)
{
@@ -217,7 +217,7 @@ public void Exit()
}
///
- /// An handle on an async lock which makes sure that disposing it mutiple times won't exit the monitor multiple times
+ /// An handle on an async lock which makes sure that disposing it multiple times won't exit the monitor multiple times
///
private class Handle : IDisposable
{