You've already forked linux-packaging-mono
							
							
		
			
				
	
	
		
			387 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			387 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System;
 | |
| using System.Collections;
 | |
| using System.Collections.ObjectModel;
 | |
| using System.Collections.Generic;
 | |
| using System.Threading;
 | |
| using System.Collections.Specialized;
 | |
| using System.Diagnostics;
 | |
| using System.Workflow.Runtime;
 | |
| using System.Globalization;
 | |
| 
 | |
| namespace System.Workflow.Runtime.Hosting
 | |
| {
 | |
|     [Obsolete("The System.Workflow.* types are deprecated.  Instead, please use the new types from System.Activities.*")]
 | |
|     public class DefaultWorkflowSchedulerService : WorkflowSchedulerService
 | |
|     {
 | |
|         // next two fields controlled by locking the timerQueue
 | |
|         private KeyedPriorityQueue<Guid, CallbackInfo, DateTime> timerQueue = new KeyedPriorityQueue<Guid, CallbackInfo, DateTime>();
 | |
|         private Timer callbackTimer;
 | |
| 
 | |
|         private TimerCallback timerCallback;
 | |
|         private const string MAX_SIMULTANEOUS_WORKFLOWS_KEY = "maxSimultaneousWorkflows";
 | |
|         private const int DEFAULT_MAX_SIMULTANEOUS_WORKFLOWS = 5;
 | |
|         private static TimeSpan infinite = new TimeSpan(Timeout.Infinite);
 | |
|         private readonly int maxSimultaneousWorkflows;       // Maximum number of work items allowed in ThreadPool queue
 | |
|         private static TimeSpan fiveMinutes = new TimeSpan(0, 5, 0);
 | |
| 
 | |
|         // next three fields controlled by locking the waitingQueue
 | |
|         private int numCurrentWorkers;
 | |
|         private Queue<WorkItem> waitingQueue;       // Queue for extra items waiting to be allowed into thread pool
 | |
|         private volatile bool running = false;
 | |
| 
 | |
|         private IList<PerformanceCounter> queueCounters;    // expose internal queue length
 | |
| 
 | |
|         private static int DefaultThreadCount
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 return Environment.ProcessorCount == 1
 | |
|                     ? DEFAULT_MAX_SIMULTANEOUS_WORKFLOWS
 | |
|                     : (int)(DEFAULT_MAX_SIMULTANEOUS_WORKFLOWS * Environment.ProcessorCount * .8);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public DefaultWorkflowSchedulerService()
 | |
|             : this(DefaultThreadCount)
 | |
|         {
 | |
|         }
 | |
| 
 | |
| 
 | |
|         public DefaultWorkflowSchedulerService(int maxSimultaneousWorkflows)
 | |
|             : base()
 | |
|         {
 | |
|             if (maxSimultaneousWorkflows < 1)
 | |
|                 throw new ArgumentOutOfRangeException(MAX_SIMULTANEOUS_WORKFLOWS_KEY, maxSimultaneousWorkflows, String.Empty);
 | |
|             this.maxSimultaneousWorkflows = maxSimultaneousWorkflows;
 | |
|             init();
 | |
|         }
 | |
| 
 | |
|         public DefaultWorkflowSchedulerService(NameValueCollection parameters)
 | |
|             : base()
 | |
|         {
 | |
|             if (parameters == null)
 | |
|                 throw new ArgumentNullException("parameters");
 | |
| 
 | |
|             maxSimultaneousWorkflows = DefaultThreadCount;
 | |
|             foreach (string key in parameters.Keys)
 | |
|             {
 | |
|                 if (key == null)
 | |
|                     throw new ArgumentException(String.Format(Thread.CurrentThread.CurrentCulture, ExecutionStringManager.UnknownConfigurationParameter, "null"));
 | |
|                 string p = parameters[key];
 | |
|                 if (!key.Equals(MAX_SIMULTANEOUS_WORKFLOWS_KEY, StringComparison.OrdinalIgnoreCase))
 | |
|                     throw new ArgumentException(String.Format(Thread.CurrentThread.CurrentCulture, ExecutionStringManager.UnknownConfigurationParameter, key));
 | |
|                 if (!int.TryParse(p, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.CurrentCulture, out maxSimultaneousWorkflows))
 | |
|                     throw new FormatException(MAX_SIMULTANEOUS_WORKFLOWS_KEY);
 | |
|             }
 | |
| 
 | |
|             if (maxSimultaneousWorkflows < 1)
 | |
|                 throw new ArgumentOutOfRangeException(MAX_SIMULTANEOUS_WORKFLOWS_KEY, maxSimultaneousWorkflows, String.Empty);
 | |
| 
 | |
|             init();
 | |
|         }
 | |
| 
 | |
|         private void init()
 | |
|         {
 | |
|             timerCallback = new TimerCallback(OnTimerCallback);
 | |
|             timerQueue.FirstElementChanged += OnFirstElementChanged;
 | |
|             waitingQueue = new Queue<WorkItem>();
 | |
|         }
 | |
| 
 | |
| 
 | |
|         public int MaxSimultaneousWorkflows
 | |
|         {
 | |
|             get { return maxSimultaneousWorkflows; }
 | |
|         }
 | |
| 
 | |
|         internal protected override void Schedule(WaitCallback callback, Guid workflowInstanceId)
 | |
|         {
 | |
|             WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "Scheduling work for instance {0}", workflowInstanceId);
 | |
| 
 | |
|             if (callback == null)
 | |
|                 throw new ArgumentNullException("callback");
 | |
|             if (workflowInstanceId == Guid.Empty)
 | |
|                 throw new ArgumentException(String.Format(CultureInfo.CurrentUICulture, ExecutionStringManager.CantBeEmptyGuid, "workflowInstanceId"));
 | |
| 
 | |
|             // Add the work item to our internal queue and signal the ProcessQueue thread
 | |
|             EnqueueWorkItem(new WorkItem(callback, workflowInstanceId));
 | |
|         }
 | |
| 
 | |
|         internal protected override void Schedule(WaitCallback callback, Guid workflowInstanceId, DateTime whenUtc, Guid timerId)
 | |
|         {
 | |
|             WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "Scheduling work for instance {0} on timer ID {1} in {2}", workflowInstanceId, timerId, (whenUtc - DateTime.UtcNow));
 | |
| 
 | |
|             if (callback == null)
 | |
|                 throw new ArgumentNullException("callback");
 | |
|             if (timerId == Guid.Empty)
 | |
|                 throw new ArgumentException(String.Format(CultureInfo.CurrentUICulture, ExecutionStringManager.CantBeEmptyGuid, "timerId"));
 | |
|             if (workflowInstanceId == Guid.Empty)
 | |
|                 throw new ArgumentException(String.Format(CultureInfo.CurrentUICulture, ExecutionStringManager.CantBeEmptyGuid, "workflowInstanceId"));
 | |
| 
 | |
|             CallbackInfo ci = new CallbackInfo(this, callback, workflowInstanceId, whenUtc);
 | |
| 
 | |
|             lock (timerQueue)
 | |
|             {
 | |
|                 timerQueue.Enqueue(timerId, ci, whenUtc);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         internal protected override void Cancel(Guid timerId)
 | |
|         {
 | |
|             WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "Cancelling work with timer ID {0}", timerId);
 | |
| 
 | |
|             if (timerId == Guid.Empty)
 | |
|                 throw new ArgumentException(String.Format(CultureInfo.CurrentUICulture, ExecutionStringManager.CantBeEmptyGuid, "timerId"), "timerId");
 | |
| 
 | |
|             lock (timerQueue)
 | |
|             {
 | |
|                 timerQueue.Remove(timerId);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         override protected void OnStarted()
 | |
|         {
 | |
|             lock (timerQueue)
 | |
|             {
 | |
|                 base.OnStarted();
 | |
|                 CallbackInfo ci = timerQueue.Peek();
 | |
|                 if (ci != null)
 | |
|                     callbackTimer = CreateTimerCallback(ci);
 | |
|                 running = true;
 | |
|             }
 | |
|             lock (waitingQueue)
 | |
|             {
 | |
|                 int nToStart = Math.Min(maxSimultaneousWorkflows, waitingQueue.Count);
 | |
|                 for (int i = 0; i < nToStart; i++)
 | |
|                 {
 | |
|                     if (ThreadPool.QueueUserWorkItem(QueueWorkerProcess))
 | |
|                     {
 | |
|                         numCurrentWorkers++;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             if (queueCounters == null && this.Runtime.PerformanceCounterManager != null)
 | |
|             {
 | |
|                 queueCounters = this.Runtime.PerformanceCounterManager.CreateCounters(ExecutionStringManager.PerformanceCounterWorkflowsWaitingName);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         protected internal override void Stop()
 | |
|         {
 | |
|             lock (timerQueue)
 | |
|             {
 | |
|                 base.Stop();
 | |
|                 if (callbackTimer != null)
 | |
|                 {
 | |
|                     callbackTimer.Dispose();
 | |
|                     callbackTimer = null;
 | |
|                 }
 | |
|                 running = false;
 | |
|             }
 | |
|             lock (waitingQueue)
 | |
|             {
 | |
|                 while (numCurrentWorkers > 0)
 | |
|                 {
 | |
|                     Monitor.Wait(waitingQueue);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private void OnFirstElementChanged(object source, KeyedPriorityQueueHeadChangedEventArgs<CallbackInfo> e)
 | |
|         {
 | |
|             // timerQueue must have been locked by operation that caused this event to fire
 | |
| 
 | |
|             if (callbackTimer != null)
 | |
|             {
 | |
|                 callbackTimer.Dispose();
 | |
|                 callbackTimer = null;
 | |
|             }
 | |
|             if (e.NewFirstElement != null && this.State == WorkflowRuntimeServiceState.Started)
 | |
|             {
 | |
|                 callbackTimer = CreateTimerCallback(e.NewFirstElement);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private void OnTimerCallback(object ignored)
 | |
|         {
 | |
|             //Make sure activity ID comes out of Threadpool are initialized to null.
 | |
|             Trace.CorrelationManager.ActivityId = Guid.Empty;
 | |
| 
 | |
|             CallbackInfo ci = null;
 | |
|             bool fire = false;
 | |
|             try
 | |
|             {
 | |
|                 lock (timerQueue)
 | |
|                 {
 | |
|                     if (State == WorkflowRuntimeServiceState.Started)
 | |
|                     {
 | |
|                         ci = timerQueue.Peek();
 | |
|                         if (ci != null)
 | |
|                         {
 | |
|                             if (ci.IsExpired)
 | |
|                             {
 | |
|                                 WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "Timeout occured for timer for instance {0}", ci.State);
 | |
|                                 timerQueue.Dequeue();
 | |
|                                 fire = true;
 | |
|                             }
 | |
|                             else
 | |
|                             {
 | |
|                                 callbackTimer = CreateTimerCallback(ci);
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|                 if (fire && ci != null)
 | |
|                     ci.Callback(ci.State);
 | |
|             }
 | |
|             // Ignore cases where the workflow has been stolen out from under us
 | |
|             catch (WorkflowOwnershipException)
 | |
|             { }
 | |
|             catch (ThreadAbortException e)
 | |
|             {
 | |
|                 WorkflowTrace.Host.TraceEvent(TraceEventType.Error, 0, "Timeout for instance, {0} threw exception {1}", ci == null ? null : ci.State, e.Message);
 | |
|                 RaiseServicesExceptionNotHandledEvent(e, (Guid)ci.State);
 | |
|                 throw;
 | |
|             }
 | |
|             catch (Exception e)
 | |
|             {
 | |
|                 WorkflowTrace.Host.TraceEvent(TraceEventType.Error, 0, "Timeout for instance, {0} threw exception {1}", ci == null ? null : ci.State, e.Message);
 | |
|                 RaiseServicesExceptionNotHandledEvent(e, (Guid)ci.State);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private Timer CreateTimerCallback(CallbackInfo info)
 | |
|         {
 | |
|             DateTime now = DateTime.UtcNow;
 | |
|             TimeSpan span = (info.When > now) ? info.When - now : TimeSpan.Zero;
 | |
|             if (span > fiveMinutes) // never let more than five minutes go by without checking
 | |
|                 span = fiveMinutes;
 | |
|             return new Timer(timerCallback, info.State, span, infinite);
 | |
|         }
 | |
| 
 | |
|         private void EnqueueWorkItem(WorkItem workItem)
 | |
|         {
 | |
|             lock (waitingQueue)
 | |
|             {
 | |
|                 waitingQueue.Enqueue(workItem);
 | |
|                 if (running && numCurrentWorkers < maxSimultaneousWorkflows)
 | |
|                 {
 | |
|                     if (ThreadPool.QueueUserWorkItem(this.QueueWorkerProcess))
 | |
|                     {
 | |
|                         numCurrentWorkers++;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             if (queueCounters != null)
 | |
|             {
 | |
|                 foreach (PerformanceCounter p in queueCounters)
 | |
|                 {
 | |
|                     p.RawValue = waitingQueue.Count;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private void QueueWorkerProcess(object state /*unused*/)
 | |
|         {
 | |
|             //Make sure activity ID comes out of Threadpool are initialized to null.
 | |
|             Trace.CorrelationManager.ActivityId = Guid.Empty;
 | |
| 
 | |
|             while (true)
 | |
|             {
 | |
|                 WorkItem workItem;
 | |
|                 lock (waitingQueue)
 | |
|                 {
 | |
|                     if (waitingQueue.Count == 0 || !running)
 | |
|                     {
 | |
|                         numCurrentWorkers--;
 | |
|                         Monitor.Pulse(waitingQueue);
 | |
|                         return;
 | |
|                     }
 | |
|                     workItem = waitingQueue.Dequeue();
 | |
|                 }
 | |
|                 if (queueCounters != null)
 | |
|                 {
 | |
|                     foreach (PerformanceCounter p in queueCounters)
 | |
|                     {
 | |
|                         p.RawValue = waitingQueue.Count;
 | |
|                     }
 | |
|                 }
 | |
|                 workItem.Invoke(this);
 | |
|             }
 | |
|         }
 | |
| 
 | |
| 
 | |
|         internal class WorkItem
 | |
|         {
 | |
|             private WaitCallback callback;
 | |
|             private object state;
 | |
| 
 | |
|             public WorkItem(WaitCallback callback, object state)
 | |
|             {
 | |
|                 this.callback = callback;
 | |
|                 this.state = state;
 | |
|             }
 | |
| 
 | |
|             public WaitCallback Callback
 | |
|             {
 | |
|                 get { return callback; }
 | |
|             }
 | |
| 
 | |
|             public void Invoke(WorkflowSchedulerService service)
 | |
|             {
 | |
|                 try
 | |
|                 {
 | |
|                     WorkflowTrace.Host.TraceEvent(TraceEventType.Information, 0, "Running workflow {0}", state);
 | |
|                     Callback(state);
 | |
|                 }
 | |
|                 catch (Exception e)
 | |
|                 {
 | |
|                     if (WorkflowExecutor.IsIrrecoverableException(e))
 | |
|                     {
 | |
|                         throw;
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         service.RaiseExceptionNotHandledEvent(e, (Guid)state);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         internal class CallbackInfo
 | |
|         {
 | |
|             WaitCallback callback;
 | |
|             object state;
 | |
|             DateTime when;
 | |
|             WorkflowSchedulerService service;
 | |
| 
 | |
|             public CallbackInfo(WorkflowSchedulerService service, WaitCallback callback, object state, DateTime when)
 | |
|             {
 | |
|                 this.service = service;
 | |
|                 this.callback = callback;
 | |
|                 this.state = state;
 | |
|                 this.when = when;
 | |
|             }
 | |
| 
 | |
|             public DateTime When
 | |
|             {
 | |
|                 get { return when; }
 | |
|             }
 | |
| 
 | |
|             public bool IsExpired
 | |
|             {
 | |
|                 get { return DateTime.UtcNow >= when; }
 | |
|             }
 | |
| 
 | |
|             public object State
 | |
|             {
 | |
|                 get { return state; }
 | |
|             }
 | |
| 
 | |
|             public WaitCallback Callback
 | |
|             {
 | |
|                 get { return callback; }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 |