Skip to content
This repository has been archived by the owner on Aug 8, 2024. It is now read-only.

Commit

Permalink
Expose\test generic UnsafeQueueUserWorkItem overload (dotnet#33637)
Browse files Browse the repository at this point in the history
* Expose\test generic UnsafeQueueUserWorkItem overload

* Suppress ApiCompat warning
  • Loading branch information
stephentoub authored Nov 27, 2018
1 parent e5c18b0 commit a3cef9c
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public static partial class ThreadPool
public static unsafe bool UnsafeQueueNativeOverlapped(System.Threading.NativeOverlapped* overlapped) { throw null; }
public static bool UnsafeQueueUserWorkItem(System.Threading.IThreadPoolWorkItem callBack, bool preferLocal) { throw null; }
public static bool UnsafeQueueUserWorkItem(System.Threading.WaitCallback callBack, object state) { throw null; }
public static bool UnsafeQueueUserWorkItem<TState>(System.Action<TState> callBack, TState state, bool preferLocal) { throw null; }
public static System.Threading.RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(System.Threading.WaitHandle waitObject, System.Threading.WaitOrTimerCallback callBack, object state, int millisecondsTimeOutInterval, bool executeOnlyOnce) { throw null; }
public static System.Threading.RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(System.Threading.WaitHandle waitObject, System.Threading.WaitOrTimerCallback callBack, object state, long millisecondsTimeOutInterval, bool executeOnlyOnce) { throw null; }
public static System.Threading.RegisteredWaitHandle UnsafeRegisterWaitForSingleObject(System.Threading.WaitHandle waitObject, System.Threading.WaitOrTimerCallback callBack, object state, System.TimeSpan timeout, bool executeOnlyOnce) { throw null; }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Compat issues with assembly System.Threading.ThreadPool:
MembersMustExist : Member 'System.Threading.ThreadPool.UnsafeQueueUserWorkItem<TState>(System.Action<TState>, TState, System.Boolean)' does not exist in the implementation but it does exist in the contract.
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
MembersMustExist : Member 'System.Threading.ThreadPool.QueueUserWorkItem<TState>(System.Action<TState>, TState, System.Boolean)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.ThreadPool.QueueUserWorkItem<TState>(System.Action<TState>, TState, System.Boolean)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.ThreadPool.UnsafeQueueUserWorkItem<TState>(System.Action<TState>, TState, System.Boolean)' does not exist in the implementation but it does exist in the contract.
103 changes: 70 additions & 33 deletions src/System.Threading.ThreadPool/tests/ThreadPoolTests.netcoreapp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
Expand All @@ -10,82 +11,120 @@ namespace System.Threading.ThreadPools.Tests
{
public partial class ThreadPoolTests
{
public static IEnumerable<object[]> OneBool() =>
from b in new[] { true, false }
select new object[] { b };

public static IEnumerable<object[]> TwoBools() =>
from b1 in new[] { true, false }
from b2 in new[] { true, false }
select new object[] { b1, b2 };

[Theory]
[InlineData(false)]
[InlineData(true)]
public void QueueUserWorkItem_PreferLocal_InvalidArguments_Throws(bool preferLocal)
[MemberData(nameof(TwoBools))]
public void QueueUserWorkItem_PreferLocal_InvalidArguments_Throws(bool preferLocal, bool useUnsafe)
{
AssertExtensions.Throws<ArgumentNullException>("callBack", () => ThreadPool.QueueUserWorkItem(null, new object(), preferLocal));
AssertExtensions.Throws<ArgumentNullException>("callBack", () => useUnsafe ?
ThreadPool.UnsafeQueueUserWorkItem(null, new object(), preferLocal) :
ThreadPool.QueueUserWorkItem(null, new object(), preferLocal));
}

[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task QueueUserWorkItem_PreferLocal_NullValidForState(bool preferLocal)
[MemberData(nameof(TwoBools))]
public async Task QueueUserWorkItem_PreferLocal_NullValidForState(bool preferLocal, bool useUnsafe)
{
var tcs = new TaskCompletionSource<int>();
ThreadPool.QueueUserWorkItem(s => tcs.SetResult(84), (object)null, preferLocal);
if (useUnsafe)
{
ThreadPool.UnsafeQueueUserWorkItem(s => tcs.SetResult(84), (object)null, preferLocal);
}
else
{
ThreadPool.QueueUserWorkItem(s => tcs.SetResult(84), (object)null, preferLocal);
}
Assert.Equal(84, await tcs.Task);
}

[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task QueueUserWorkItem_PreferLocal_ReferenceTypeStateObjectPassedThrough(bool preferLocal)
[MemberData(nameof(TwoBools))]
public async Task QueueUserWorkItem_PreferLocal_ReferenceTypeStateObjectPassedThrough(bool preferLocal, bool useUnsafe)
{
var tcs = new TaskCompletionSource<int>();
ThreadPool.QueueUserWorkItem(s => s.SetResult(84), tcs, preferLocal);
if (useUnsafe)
{
ThreadPool.UnsafeQueueUserWorkItem(s => s.SetResult(84), tcs, preferLocal);
}
else
{
ThreadPool.QueueUserWorkItem(s => s.SetResult(84), tcs, preferLocal);
}
Assert.Equal(84, await tcs.Task);
}

[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task QueueUserWorkItem_PreferLocal_ValueTypeStateObjectPassedThrough(bool preferLocal)
[MemberData(nameof(TwoBools))]
public async Task QueueUserWorkItem_PreferLocal_ValueTypeStateObjectPassedThrough(bool preferLocal, bool useUnsafe)
{
var tcs = new TaskCompletionSource<int>();
ThreadPool.QueueUserWorkItem(s => s.tcs.SetResult(s.value), (tcs, value: 42), preferLocal);
if (useUnsafe)
{
ThreadPool.UnsafeQueueUserWorkItem(s => s.tcs.SetResult(s.value), (tcs, value: 42), preferLocal);
}
else
{
ThreadPool.QueueUserWorkItem(s => s.tcs.SetResult(s.value), (tcs, value: 42), preferLocal);
}
Assert.Equal(42, await tcs.Task);
}

[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task QueueUserWorkItem_PreferLocal_RunsAsynchronously(bool preferLocal)
[MemberData(nameof(TwoBools))]
public async Task QueueUserWorkItem_PreferLocal_RunsAsynchronously(bool preferLocal, bool useUnsafe)
{
await Task.Factory.StartNew(() =>
{
int origThread = Environment.CurrentManagedThreadId;
var tcs = new TaskCompletionSource<int>();
ThreadPool.QueueUserWorkItem(s => s.SetResult(Environment.CurrentManagedThreadId), tcs, preferLocal);
if (useUnsafe)
{
ThreadPool.UnsafeQueueUserWorkItem(s => s.SetResult(Environment.CurrentManagedThreadId), tcs, preferLocal);
}
else
{
ThreadPool.QueueUserWorkItem(s => s.SetResult(Environment.CurrentManagedThreadId), tcs, preferLocal);
}
Assert.NotEqual(origThread, tcs.Task.GetAwaiter().GetResult());
}, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}

[Theory]
[InlineData(false)]
[InlineData(true)]
public async Task QueueUserWorkItem_PreferLocal_ExecutionContextFlowed(bool preferLocal)
[MemberData(nameof(TwoBools))]
public async Task QueueUserWorkItem_PreferLocal_ExecutionContextFlowedIfSafe(bool preferLocal, bool useUnsafe)
{
var tcs = new TaskCompletionSource<int>();
var asyncLocal = new AsyncLocal<int>() { Value = 42 };
ThreadPool.QueueUserWorkItem(s => s.SetResult(asyncLocal.Value), tcs, preferLocal);
if (useUnsafe)
{
ThreadPool.UnsafeQueueUserWorkItem(s => s.SetResult(asyncLocal.Value), tcs, preferLocal);
}
else
{
ThreadPool.QueueUserWorkItem(s => s.SetResult(asyncLocal.Value), tcs, preferLocal);
}
asyncLocal.Value = 0;
Assert.Equal(42, await tcs.Task);
Assert.Equal(useUnsafe ? 0 : 42, await tcs.Task);
}

[Theory]
[InlineData(false)]
[InlineData(true)]
[MemberData(nameof(OneBool))]
public void UnsafeQueueUserWorkItem_IThreadPoolWorkItem_Invalid_Throws(bool preferLocal)
{
AssertExtensions.Throws<ArgumentNullException>("callBack", () => ThreadPool.UnsafeQueueUserWorkItem(null, preferLocal));
AssertExtensions.Throws<ArgumentOutOfRangeException>("callBack", () => ThreadPool.UnsafeQueueUserWorkItem(new InvalidWorkItemAndTask(() => { }), preferLocal));
}

[Theory]
[InlineData(false)]
[InlineData(true)]
[MemberData(nameof(OneBool))]
public async Task UnsafeQueueUserWorkItem_IThreadPoolWorkItem_ManyIndividualItems_AllInvoked(bool preferLocal)
{
TaskCompletionSource<bool>[] tasks = Enumerable.Range(0, 100).Select(_ => new TaskCompletionSource<bool>()).ToArray();
Expand All @@ -101,8 +140,7 @@ public async Task UnsafeQueueUserWorkItem_IThreadPoolWorkItem_ManyIndividualItem
}

[Theory]
[InlineData(false)]
[InlineData(true)]
[MemberData(nameof(OneBool))]
public async Task UnsafeQueueUserWorkItem_IThreadPoolWorkItem_SameObjectReused_AllInvoked(bool preferLocal)
{
const int Iters = 100;
Expand All @@ -124,8 +162,7 @@ public async Task UnsafeQueueUserWorkItem_IThreadPoolWorkItem_SameObjectReused_A
}

[Theory]
[InlineData(false)]
[InlineData(true)]
[MemberData(nameof(OneBool))]
public async Task UnsafeQueueUserWorkItem_IThreadPoolWorkItem_ExecutionContextNotFlowed(bool preferLocal)
{
var al = new AsyncLocal<int> { Value = 42 };
Expand Down

0 comments on commit a3cef9c

Please sign in to comment.