// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-#if NET_4_5
using System;
using System.Threading;
using System.Runtime.CompilerServices;
using System.Collections.Generic;
using System.Collections;
+using System.Collections.Concurrent;
namespace MonoTests.System.Runtime.CompilerServices
{
class Scheduler : TaskScheduler
{
string name;
+ int ic, qc;
public Scheduler (string name)
{
this.name = name;
}
- public int InlineCalls { get; set; }
- public int QueueCalls { get; set; }
+ public int InlineCalls { get { return ic; } }
+ public int QueueCalls { get { return qc; } }
protected override IEnumerable<Task> GetScheduledTasks ()
{
protected override void QueueTask (Task task)
{
- ++QueueCalls;
+ Interlocked.Increment (ref qc);
ThreadPool.QueueUserWorkItem (o => {
TryExecuteTask (task);
});
protected override bool TryExecuteTaskInline (Task task, bool taskWasPreviouslyQueued)
{
- ++InlineCalls;
+ Interlocked.Increment (ref ic);
return false;
}
+
+ public override string ToString ()
+ {
+ return "Scheduler-" + name;
+ }
}
class SingleThreadSynchronizationContext : SynchronizationContext
}
}
+ class NestedSynchronizationContext : SynchronizationContext
+ {
+ Thread thread;
+ readonly ConcurrentQueue<Tuple<SendOrPostCallback, object, ExecutionContext>> workQueue = new ConcurrentQueue<Tuple<SendOrPostCallback, object, ExecutionContext>> ();
+ readonly AutoResetEvent workReady = new AutoResetEvent (false);
+
+ public NestedSynchronizationContext ()
+ {
+ thread = new Thread (WorkerThreadProc) { IsBackground = true };
+ thread.Start ();
+ }
+
+ public override void Post (SendOrPostCallback d, object state)
+ {
+ var context = ExecutionContext.Capture ();
+ workQueue.Enqueue (Tuple.Create (d, state, context));
+ workReady.Set ();
+ }
+
+ void WorkerThreadProc ()
+ {
+ if (!workReady.WaitOne (10000))
+ return;
+
+ Tuple<SendOrPostCallback, object, ExecutionContext> work;
+
+ while (workQueue.TryDequeue (out work)) {
+ ExecutionContext.Run (work.Item3, _ => {
+ var oldSyncContext = SynchronizationContext.Current;
+ SynchronizationContext.SetSynchronizationContext (this);
+ work.Item1 (_);
+ SynchronizationContext.SetSynchronizationContext (oldSyncContext);
+ }, work.Item2);
+ }
+ }
+ }
+
string progress;
SynchronizationContext sc;
+ ManualResetEvent mre;
[SetUp]
public void Setup ()
Assert.IsTrue (t.Wait (3000), "#0");
Assert.AreEqual (0, t.Result, "#1");
Assert.AreEqual (0, b.InlineCalls, "#2b");
- Assert.AreEqual (2, a.QueueCalls, "#3a");
+ Assert.IsTrue (a.QueueCalls == 1 || a.QueueCalls == 2, "#3a");
Assert.AreEqual (1, b.QueueCalls, "#3b");
}
}
[Test]
+ [Ignore ("Incompatible with nunitlite")]
public void FinishedTaskOnCompleted ()
{
var mres = new ManualResetEvent (false);
mres.Set ();
// this will only terminate correctly if the test was not executed from the main thread
- // e.g. Touch.Unit defaults to run tests on the main thread and this will return false
+ // e.g. nunitlite/Touch.Unit defaults to run tests on the main thread and this will return false
Assert.AreEqual (Thread.CurrentThread.IsBackground, mres2.WaitOne (2000), "#2");;
}
[Test]
public void CompletionOnDifferentCustomSynchronizationContext ()
{
+ mre = new ManualResetEvent (false);
progress = "";
var syncContext = new SingleThreadSynchronizationContext ();
SynchronizationContext.SetSynchronizationContext (syncContext);
syncContext.Post (delegate {
- Go2 (syncContext);
+ Task t = new Task (delegate() { });
+ Go2 (syncContext, t);
+ t.Start ();
}, null);
// Custom message loop
Thread.Sleep (0);
}
- Assert.AreEqual ("132", progress);
+ Assert.AreEqual ("13xa2", progress);
}
- async void Go2 (SynchronizationContext ctx)
+ async void Go2 (SynchronizationContext ctx, Task t)
{
- await Wait2 (ctx);
+ await Wait2 (ctx, t);
- progress += "2";
+ progress += "a";
+
+ if (mre.WaitOne (5000))
+ progress += "2";
+ else
+ progress += "b";
}
- async Task Wait2 (SynchronizationContext ctx)
+ async Task Wait2 (SynchronizationContext ctx, Task t)
{
- await Task.Delay (10); // Force block suspend/return
+ await t; // Force block suspend/return
- ctx.Post (l => progress += "3", null);
+ ctx.Post (l => {
+ progress += "3";
+ mre.Set ();
+ progress += "x";
+ }, null);
progress += "1";
SynchronizationContext.SetSynchronizationContext (null);
}
+
+ [Test]
+ public void NestedLeakingSynchronizationContext ()
+ {
+ var sc = SynchronizationContext.Current;
+ if (sc == null)
+ Assert.IsTrue (NestedLeakingSynchronizationContext_MainAsync (sc).Wait (5000), "#1");
+ else
+ Assert.Ignore ("NestedSynchronizationContext may never complete on custom context");
+ }
+
+ static async Task NestedLeakingSynchronizationContext_MainAsync (SynchronizationContext sc)
+ {
+ Assert.AreSame (sc, SynchronizationContext.Current, "#1");
+ await NestedLeakingSynchronizationContext_DoWorkAsync ();
+ Assert.AreSame (sc, SynchronizationContext.Current, "#2");
+ }
+
+ static async Task NestedLeakingSynchronizationContext_DoWorkAsync ()
+ {
+ var sc = new NestedSynchronizationContext ();
+ SynchronizationContext.SetSynchronizationContext (sc);
+
+ Assert.AreSame (sc, SynchronizationContext.Current);
+ await Task.Yield ();
+ Assert.AreSame (sc, SynchronizationContext.Current);
+ }
}
}
-#endif