Imported Upstream version 3.6.0

Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
This commit is contained in:
Jo Shields
2014-08-13 10:39:27 +01:00
commit a575963da9
50588 changed files with 8155799 additions and 0 deletions

View File

@@ -0,0 +1,139 @@
// Scheduler.cs
//
// Copyright (c) 2008 Jérémie "Garuma" Laval
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//
#if NET_4_0
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Concurrent;
namespace Mono.Threading.Tasks
{
public class FixedTaskScheduler: TaskScheduler, IMonoTaskScheduler
{
readonly IProducerConsumerCollection<Task> workQueue;
readonly ThreadWorker[] workers;
readonly ManualResetEvent pulseHandle = new ManualResetEvent (false);
public FixedTaskScheduler ()
: this (Environment.ProcessorCount, ThreadPriority.Normal)
{
}
public FixedTaskScheduler (int maxWorker, ThreadPriority priority)
{
workQueue = new ConcurrentQueue<Task> ();
workers = new ThreadWorker [maxWorker];
for (int i = 0; i < maxWorker; i++) {
workers [i] = new ThreadWorker (workers, i, workQueue, new CyclicDeque<Task> (), priority, pulseHandle);
workers [i].Pulse ();
}
}
protected override void QueueTask (Task t)
{
// Add to the shared work pool
workQueue.TryAdd (t);
// Wake up some worker if they were asleep
PulseAll ();
}
public void MonoParticipateUntil (Task task)
{
if (task.IsCompleted)
return;
ManualResetEventSlim evt = new ManualResetEventSlim (false);
task.ContinueWith (_ => evt.Set (), TaskContinuationOptions.ExecuteSynchronously);
if (evt.IsSet || task.IsCompleted)
return;
ParticipateUntilInternal (task, evt, -1);
}
public bool MonoParticipateUntil (Task task, ManualResetEventSlim evt, int millisecondsTimeout)
{
if (task.IsCompleted)
return false;
bool isFromPredicate = true;
task.ContinueWith (_ => { isFromPredicate = false; evt.Set (); }, TaskContinuationOptions.ExecuteSynchronously);
ParticipateUntilInternal (task, evt, millisecondsTimeout);
if (task.IsCompleted)
return false;
return isFromPredicate;
}
public void ParticipateUntilInternal (Task self, ManualResetEventSlim evt, int millisecondsTimeout)
{
ThreadWorker.ParticipativeWorkerMethod (self, evt, millisecondsTimeout, workQueue, workers, pulseHandle, (a, b) => true);
}
static bool TaskCompletedPredicate (Task self)
{
return self.IsCompleted;
}
public void PulseAll ()
{
pulseHandle.Set ();
}
public void Dispose ()
{
foreach (ThreadWorker w in workers)
w.Dispose ();
}
#region Scheduler dummy stubs
protected override System.Collections.Generic.IEnumerable<Task> GetScheduledTasks ()
{
throw new System.NotImplementedException();
}
protected override bool TryDequeue (Task task)
{
throw new System.NotImplementedException();
}
protected override bool TryExecuteTaskInline (Task task, bool taskWasPreviouslyQueued)
{
task.Execute (null);
return true;
}
public override int MaximumConcurrencyLevel {
get {
return base.MaximumConcurrencyLevel;
}
}
#endregion
}
}
#endif

View File

@@ -0,0 +1,54 @@
//
// MonoTaskExtensions.cs
//
// Author:
// Jérémie "Garuma" Laval <jeremie.laval@gmail.com>
//
// Copyright (c) 2011 Jérémie "Garuma" Laval
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if NET_4_0
using System;
using System.Threading.Tasks;
using System.Reflection;
namespace Mono.Threading.Tasks
{
public static class MonoTaskExtensions
{
readonly static Action<Task, Action<Task>> internalExecute = null;
static MonoTaskExtensions ()
{
// Initialize internal execute
var method = typeof(Task).GetMethod ("Execute", BindingFlags.Instance | BindingFlags.NonPublic);
internalExecute = (Action<Task, Action<Task>>)Delegate.CreateDelegate (typeof(Action<Task, Action<Task>>), method);
}
// Allow external worker to call into the otherwise internal corresponding method of Task
public static void Execute (this Task task, Action<Task> childWorkAdder)
{
internalExecute (task, childWorkAdder);
}
}
}
#endif

View File

@@ -0,0 +1,42 @@
//
// MonoTaskScheduler.cs
//
// Author:
// Jérémie "Garuma" Laval <jeremie.laval@gmail.com>
//
// Copyright (c) 2011 Jérémie "Garuma" Laval
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#if NET_4_0
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Mono.Threading.Tasks
{
public interface IMonoTaskScheduler
{
void MonoParticipateUntil (Task task);
bool MonoParticipateUntil (Task task, ManualResetEventSlim predicateEvt, int millisecondsTimeout);
}
}
#endif

View File

@@ -0,0 +1,421 @@
// ThreadWorker.cs
//
// Copyright (c) 2008 Jérémie "Garuma" Laval
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//
#if NET_4_0
using System;
using System.Threading;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using Watch = System.Diagnostics.Stopwatch;
namespace Mono.Threading.Tasks
{
public class ThreadWorker : IDisposable
{
Thread workerThread;
/* This field is used when a TheadWorker have to call Task.Wait
* which bring him back here with the static WorkerMethod although
* it's more optimized for him to continue calling its own WorkerMethod
*/
[ThreadStatic]
static ThreadWorker autoReference;
readonly IConcurrentDeque<Task> dDeque;
readonly ThreadWorker[] others;
readonly ManualResetEvent waitHandle;
readonly IProducerConsumerCollection<Task> sharedWorkQueue;
readonly ThreadPriority threadPriority;
// Flag to tell if workerThread is running
int started = 0;
readonly int workerLength;
readonly int workerPosition;
const int maxRetry = 3;
const int sleepThreshold = 100;
int deepSleepTime = 8;
readonly Action<Task> adder;
Task currentTask;
public ThreadWorker (ThreadWorker[] others,
int workerPosition,
IProducerConsumerCollection<Task> sharedWorkQueue,
IConcurrentDeque<Task> dDeque,
ThreadPriority priority,
ManualResetEvent handle)
{
this.others = others;
this.dDeque = dDeque;
this.sharedWorkQueue = sharedWorkQueue;
this.workerLength = others.Length;
this.workerPosition = workerPosition;
this.waitHandle = handle;
this.threadPriority = priority;
this.adder = new Action<Task> (ChildWorkAdder);
InitializeUnderlyingThread ();
}
protected virtual void InitializeUnderlyingThread ()
{
this.workerThread = new Thread (WorkerMethodWrapper);
this.workerThread.IsBackground = true;
this.workerThread.Priority = threadPriority;
this.workerThread.Name = "ParallelFxThreadWorker";
}
public virtual void Dispose ()
{
Stop ();
if (workerThread.ThreadState != ThreadState.Stopped)
workerThread.Abort ();
}
public virtual void Pulse ()
{
if (started == 1)
return;
// If the thread was stopped then set it in use and restart it
int result = Interlocked.Exchange (ref started, 1);
if (result != 0)
return;
if (this.workerThread.ThreadState != ThreadState.Unstarted) {
InitializeUnderlyingThread ();
}
workerThread.Start ();
}
public virtual void Stop ()
{
// Set the flag to stop so that the while in the thread will stop
// doing its infinite loop.
started = 0;
}
// This is the actual method called in the Thread
protected virtual void WorkerMethodWrapper ()
{
int sleepTime = 0;
autoReference = this;
bool wasWokenUp = false;
// Main loop
while (started == 1) {
bool result = false;
result = WorkerMethod ();
if (!result && wasWokenUp)
waitHandle.Reset ();
wasWokenUp = false;
Thread.Yield ();
if (result) {
deepSleepTime = 8;
sleepTime = 0;
continue;
}
// If we are spinning too much, have a deeper sleep
if (++sleepTime > sleepThreshold && sharedWorkQueue.Count == 0) {
wasWokenUp = waitHandle.WaitOne ((deepSleepTime = deepSleepTime >= 0x4000 ? 0x4000 : deepSleepTime << 1));
}
}
started = 0;
}
// Main method, used to do all the logic of retrieving, processing and stealing work.
protected virtual bool WorkerMethod ()
{
bool result = false;
bool hasStolenFromOther;
do {
hasStolenFromOther = false;
Task value;
// We fill up our work deque concurrently with other ThreadWorker
while (sharedWorkQueue.Count > 0) {
waitHandle.Set ();
while (sharedWorkQueue.TryTake (out value)) {
dDeque.PushBottom (value);
}
// Now we process our work
while (dDeque.PopBottom (out value) == PopResult.Succeed) {
waitHandle.Set ();
ExecuteTask (value, ref result);
}
}
// When we have finished, steal from other worker
ThreadWorker other;
// Repeat the operation a little so that we can let other things process.
for (int j = 0; j < maxRetry; ++j) {
int len = workerLength + workerPosition;
// Start stealing with the ThreadWorker at our right to minimize contention
for (int it = workerPosition + 1; it < len; ++it) {
int i = it % workerLength;
if ((other = others [i]) == null || other == this)
continue;
// Maybe make this steal more than one item at a time, see TODO.
while (other.dDeque.PopTop (out value) == PopResult.Succeed) {
if (!hasStolenFromOther)
waitHandle.Set ();
hasStolenFromOther = true;
ExecuteTask (value, ref result);
}
}
}
} while (sharedWorkQueue.Count > 0 || hasStolenFromOther);
return result;
}
void ExecuteTask (Task value, ref bool result)
{
if (value == null)
return;
var saveCurrent = currentTask;
currentTask = value;
value.Execute (adder);
result = true;
currentTask = saveCurrent;
}
// Almost same as above but with an added predicate and treating one item at a time.
// It's used by Scheduler Participate(...) method for special waiting case like
// Task.WaitAll(someTasks) or Task.WaitAny(someTasks)
// Predicate should be really fast and not blocking as it is called a good deal of time
// Also, the method skip tasks that are LongRunning to avoid blocking (Task are not LongRunning by default)
public static void ParticipativeWorkerMethod (Task self,
ManualResetEventSlim predicateEvt,
int millisecondsTimeout,
IProducerConsumerCollection<Task> sharedWorkQueue,
ThreadWorker[] others,
ManualResetEvent evt,
Func<Task, Task, bool> checkTaskFitness)
{
const int stage1 = 5, stage2 = 0;
int tries = 50;
WaitHandle[] handles = null;
Watch watch = Watch.StartNew ();
if (millisecondsTimeout == -1)
millisecondsTimeout = int.MaxValue;
bool aggressive = false;
bool hasAutoReference = autoReference != null;
Action<Task> adder = null;
while (!predicateEvt.IsSet && watch.ElapsedMilliseconds < millisecondsTimeout && !self.IsCompleted) {
// We try to execute the self task as it may be the simplest way to unlock
// the situation
if (self.Status == TaskStatus.WaitingToRun) {
self.Execute (hasAutoReference ? autoReference.adder : (Action<Task>)null);
if (predicateEvt.IsSet || watch.ElapsedMilliseconds > millisecondsTimeout)
return;
}
Task value;
// If we are in fact a normal ThreadWorker, use our own deque
if (hasAutoReference) {
var enumerable = autoReference.dDeque.GetEnumerable ();
if (adder == null)
adder = hasAutoReference ? autoReference.adder : (Action<Task>)null;
if (enumerable != null) {
foreach (var t in enumerable) {
if (t == null)
continue;
if (checkTaskFitness (self, t))
t.Execute (adder);
if (predicateEvt.IsSet || watch.ElapsedMilliseconds > millisecondsTimeout)
return;
}
}
}
int count = sharedWorkQueue.Count;
// Dequeue only one item as we have restriction
while (--count >= 0 && sharedWorkQueue.TryTake (out value) && value != null) {
evt.Set ();
if (checkTaskFitness (self, value) || aggressive)
value.Execute (null);
else {
if (autoReference == null)
sharedWorkQueue.TryAdd (value);
else
autoReference.dDeque.PushBottom (value);
evt.Set ();
}
if (predicateEvt.IsSet || watch.ElapsedMilliseconds > millisecondsTimeout)
return;
}
// First check to see if we comply to predicate
if (predicateEvt.IsSet || watch.ElapsedMilliseconds > millisecondsTimeout)
return;
// Try to complete other work by stealing since our desired tasks may be in other worker
ThreadWorker other;
for (int i = 0; i < others.Length; i++) {
if ((other = others [i]) == autoReference || other == null)
continue;
if (other.dDeque.PopTop (out value) == PopResult.Succeed && value != null) {
evt.Set ();
if (checkTaskFitness (self, value) || aggressive)
value.Execute (null);
else {
if (autoReference == null)
sharedWorkQueue.TryAdd (value);
else
autoReference.dDeque.PushBottom (value);
evt.Set ();
}
}
if (predicateEvt.IsSet || watch.ElapsedMilliseconds > millisecondsTimeout)
return;
}
/* Waiting is split in 4 phases
* - until stage 1 we simply yield the thread to let others add data
* - between stage 1 and stage2 we use ManualResetEventSlim light waiting mechanism
* - after stage2 we fall back to the heavier WaitHandle waiting mechanism
* - if really the situation isn't evolving after a couple of sleep, we disable
* task fitness check altogether
*/
if (--tries > stage1)
Thread.Yield ();
else if (tries >= stage2)
predicateEvt.Wait (ComputeTimeout (5, millisecondsTimeout, watch));
else {
if (tries == stage2 - 1)
handles = new [] { predicateEvt.WaitHandle, evt };
System.Threading.WaitHandle.WaitAny (handles, ComputeTimeout (1000, millisecondsTimeout, watch));
if (tries == stage2 - 10)
aggressive = true;
}
}
}
public static ThreadWorker AutoReference {
get {
return autoReference;
}
set {
autoReference = value;
}
}
protected IConcurrentDeque<Task> Deque {
get {
return dDeque;
}
}
protected ThreadWorker[] Others {
get {
return others;
}
}
protected ManualResetEvent WaitHandle {
get {
return waitHandle;
}
}
protected ThreadPriority Priority {
get {
return threadPriority;
}
}
protected int WorkerPosition {
get {
return workerPosition;
}
}
protected virtual void ChildWorkAdder (Task t)
{
dDeque.PushBottom (t);
waitHandle.Set ();
}
static int ComputeTimeout (int proposed, int timeout, Watch watch)
{
return timeout == int.MaxValue ? proposed : System.Math.Min (proposed, System.Math.Max (0, (int)(timeout - watch.ElapsedMilliseconds)));
}
public bool Finished {
get {
return started == 0;
}
}
public int Id {
get {
return workerThread.ManagedThreadId;
}
}
public virtual bool Equals (ThreadWorker other)
{
return (other == null) ? false : object.ReferenceEquals (this.dDeque, other.dDeque);
}
public override bool Equals (object obj)
{
ThreadWorker temp = obj as ThreadWorker;
return temp == null ? false : Equals (temp);
}
public override int GetHashCode ()
{
return workerThread.ManagedThreadId.GetHashCode ();
}
}
}
#endif