public static void Create(Database database, string name) { if (database == null) throw new ArgumentNullException("database", "database is null."); if (name == null) throw new ArgumentNullException("name", "name is null."); if (Exists(database, name)) throw new ArgumentException(string.Format("Workflow with the name \"{0}\" already exists.", name), "name"); var workflow = new Workflow(name); database.WorkflowsLock.EnterWriteLock(); try { workflow.WorkflowLock.EnterWriteLock(); try { database.WorkflowNames.Add(workflow.Name, workflow.Id); database.Workflows.Add(workflow.Id, workflow); } finally { workflow.WorkflowLock.ExitWriteLock(); } } finally { database.WorkflowsLock.ExitWriteLock(); } }
public static bool Exists(Database database, string name) { if (database == null) throw new ArgumentNullException("database", "database is null."); if (name == null) throw new ArgumentNullException("name", "name is null."); database.WorkflowsLock.EnterReadLock(); try { return database.WorkflowNames.ContainsKey(name); } finally { database.WorkflowsLock.ExitReadLock(); } }
public static bool IsEmpty(Database database, string name) { if (database == null) throw new ArgumentNullException("database", "database is null."); if (name == null) throw new ArgumentNullException("name", "name is null."); database.QueuesLock.EnterReadLock(); try { if (!database.QueueNames.ContainsKey(name)) throw new ArgumentException(string.Format("Queue with the name \"{0}\" not found.", name), "name"); var queue = database.Queues[database.QueueNames[name]]; queue.QueueLock.EnterReadLock(); try { return !queue.QueuedTasks.Any(); } finally { queue.QueueLock.ExitReadLock(); } } finally { database.QueuesLock.ExitReadLock(); } }
public static QueueTask Dequeue(Database database, string queueName) { if (database == null) throw new ArgumentNullException("database", "database is null."); if (queueName == null) throw new ArgumentNullException("queueName", "queueName is null."); if (Queue.IsEmpty(database, queueName)) return null; Workflow workflow; Task task; Queue queue; Guid workflowId, taskId; for (int attemptNum = 0; attemptNum < MAX_DEQUEUE_ATTEMPTS; attemptNum++) { queue = Queue.Get(database, queueName); queue.QueueLock.EnterReadLock(); try { if (queue.QueuedTasks.Count == 0) return null; var first = queue.QueuedTasks.First.Value; workflowId = first.Item1; taskId = first.Item2; } finally { queue.QueueLock.ExitReadLock(); } workflow = Workflow.Get(database, workflowId); if (workflow == null) continue; workflow.WorkflowLock.EnterReadLock(); try { task = workflow.Tasks[taskId]; } finally { workflow.WorkflowLock.ExitReadLock(); } workflow.WorkflowLock.EnterWriteLock(); try { queue.QueueLock.EnterWriteLock(); try { // Validate state; if (queue.QueuedTasks.Count == 0) return null; if (queue.QueuedTasks.First().Item2 != taskId) continue; queue.QueuedTasks.RemoveFirst(); queue.RunningTasks.Add(taskId); task.State = TaskState.Running; return new QueueTask() { WorkflowName = workflow.Name, TaskName = task.Name, }; } finally { queue.QueueLock.ExitWriteLock(); } } finally { workflow.WorkflowLock.ExitWriteLock(); } } return null; }
public static void Delete(Database database, string name) { if (database == null) throw new ArgumentNullException("database", "database is null."); if (name == null) throw new ArgumentNullException("name", "name is null."); if (!Queue.Exists(database, name)) throw new ArgumentException(string.Format("Queue with the name \"{0}\" not found.", name), "name"); if (!Queue.IsEmpty(database, name)) throw new InvalidOperationException(string.Format("Failed deleting queue with name \"{0}\", queue is not empty.", name)); var queue = Queue.Get(database, name); queue.QueueLock.EnterWriteLock(); try { if (queue.QueuedTasks.Any()) throw new InvalidOperationException(string.Format("Failed deleting queue with name \"{0}\", queue is not empty.", name)); database.QueuesLock.EnterWriteLock(); try { queue.QueuedTasks = null; database.Queues.Remove(queue.Id); database.QueueNames.Remove(queue.Name); } finally { database.QueuesLock.ExitWriteLock(); } } finally { queue.QueueLock.ExitWriteLock(); } }
public static void Create(Database database, string name) { if (database == null) throw new ArgumentNullException("database", "database is null."); if (name == null) throw new ArgumentNullException("name", "name is null."); if (Exists(database, name)) throw new ArgumentException(string.Format("Queue with the name \"{0}\" already exists.", name), "name"); var queue = new Queue(name); queue.QueueLock.EnterWriteLock(); try { database.QueuesLock.EnterWriteLock(); try { database.QueueNames.Add(queue.Name, queue.Id); database.Queues.Add(queue.Id, queue); } finally { database.QueuesLock.ExitWriteLock(); } } finally { queue.QueueLock.ExitWriteLock(); } }
public static void Resume(Database database, string name) { if (database == null) throw new ArgumentNullException("database", "database is null."); if (name == null) throw new ArgumentNullException("name", "name is null."); var workflow = Get(database, name); workflow.WorkflowLock.EnterWriteLock(); try { if (!workflow.Suspended) return; database.QueuesLock.EnterReadLock(); try { var tasksToQueue = workflow.Tasks.Values.Where(task => task.State == TaskState.Queued).ToList(); var queues = tasksToQueue.Select(task => task.QueueId).OrderBy(id => id).Distinct().Select(id => database.Queues[id]).ToList(); int queueLocksHeld = 0; try { foreach (var queue in queues) { queue.QueueLock.EnterWriteLock(); queueLocksHeld++; } foreach (var task in tasksToQueue) { database.Queues[task.QueueId].QueuedTasks.AddLast(new LinkedListNode<Tuple<Guid, Guid>>(new Tuple<Guid, Guid>(workflow.Id, task.Id))); } workflow.Suspended = false; } finally { // Release write locks for any that were taken. for (int i = 0; i < queueLocksHeld; i++) { queues[i].QueueLock.ExitWriteLock(); } } } finally { database.QueuesLock.ExitReadLock(); } } finally { workflow.WorkflowLock.ExitWriteLock(); } }
internal static Queue Get(Database database, string name) { if (database == null) throw new ArgumentNullException("database", "database is null."); if (name == null) throw new ArgumentNullException("name", "name is null."); database.QueuesLock.EnterReadLock(); try { if (!database.QueueNames.ContainsKey(name)) throw new Exception(string.Format("Queue named \"{0}\" not found.", name)); return database.Queues[database.QueueNames[name]]; } finally { database.QueuesLock.ExitReadLock(); } }
public static int RunningCount(Database database, string name) { if (database == null) throw new ArgumentNullException("database", "database is null."); if (name == null) throw new ArgumentNullException("name", "name is null."); var queue = Get(database, name); queue.QueueLock.EnterReadLock(); try { return queue.RunningTasks.Count; } finally { queue.QueueLock.ExitReadLock(); } }
public void Initialise() { this.database = new Database(); }
public static bool IsSuspended(Database database, string name) { if (database == null) throw new ArgumentNullException("database", "database is null."); if (name == null) throw new ArgumentNullException("name", "name is null."); var workflow = Get(database, name); workflow.WorkflowLock.EnterReadLock(); try { return workflow.Suspended; } finally { workflow.WorkflowLock.ExitReadLock(); } }
internal static Workflow Get(Database database, Guid id) { if (database == null) throw new ArgumentNullException("database", "database is null."); if (id == Guid.Empty) throw new ArgumentException("id", "id is empty."); database.WorkflowsLock.EnterReadLock(); try { if (!database.Workflows.ContainsKey(id)) return null; return database.Workflows[id]; } finally { database.WorkflowsLock.ExitReadLock(); } }
public static void Suspend(Database database, string name) { throw new NotImplementedException(); }
public static QueueTask Peek(Database database, string queueName) { if (database == null) throw new ArgumentNullException("database", "database is null."); if (queueName == null) throw new ArgumentNullException("queueName", "queueName is null."); Guid workflowId, taskId; var queue = Queue.Get(database, queueName); queue.QueueLock.EnterReadLock(); try { if (queue.QueuedTasks.Count == 0) return null; var first = queue.QueuedTasks.First.Value; workflowId = first.Item1; taskId = first.Item2; } finally { queue.QueueLock.ExitReadLock(); } database.WorkflowsLock.EnterReadLock(); try { if (!database.Workflows.ContainsKey(workflowId)) return null; var workflow = database.Workflows[workflowId]; workflow.WorkflowLock.EnterReadLock(); try { return new QueueTask() { WorkflowName = workflow.Name, TaskName = workflow.Tasks[taskId].Name, }; } finally { workflow.WorkflowLock.ExitReadLock(); } } finally { database.WorkflowsLock.ExitReadLock(); } }
public static void Create(Database database, string workflowName, string name, string queueName) { if (database == null) throw new ArgumentNullException("database", "database is null."); if (workflowName == null) throw new ArgumentNullException("workflowName", "workflowName is null."); if (name == null) throw new ArgumentNullException("name", "name is null."); if (queueName == null) throw new ArgumentNullException("queueName", "queueName is null."); Task task; var workflow = Workflow.Get(database, workflowName); var queue = Queue.Get(database, queueName); queue.QueueLock.EnterReadLock(); try { task = new Task(name, queue.Id); } finally { queue.QueueLock.ExitReadLock(); } database.WorkflowsLock.EnterReadLock(); try { if (!database.WorkflowNames.ContainsKey(workflowName)) throw new Exception(string.Format("Workflow with the name \"{0}\" not found.", workflowName)); workflow = database.Workflows[database.WorkflowNames[workflowName]]; if (workflow.Suspended) { workflow.WorkflowLock.EnterUpgradeableReadLock(); try { workflow.Tasks.Add(task.Id, task); workflow.TaskNames.Add(task.Name, task.Id); } finally { workflow.WorkflowLock.ExitUpgradeableReadLock(); } } else { database.QueuesLock.EnterReadLock(); try { workflow.WorkflowLock.EnterUpgradeableReadLock(); try { queue.QueueLock.EnterWriteLock(); try { workflow.Tasks.Add(task.Id, task); workflow.TaskNames.Add(task.Name, task.Id); queue.QueuedTasks.AddLast(new LinkedListNode<Tuple<Guid, Guid>>(new Tuple<Guid, Guid>(workflow.Id, task.Id))); } finally { queue.QueueLock.ExitWriteLock(); } } finally { workflow.WorkflowLock.ExitUpgradeableReadLock(); } } finally { database.QueuesLock.ExitReadLock(); } } } finally { database.WorkflowsLock.ExitReadLock(); } }
/// <summary> /// Requeue a task if it was unable to be completed. /// </summary> /// <param name="database">Database instance.</param> /// <param name="workflowName">Name of the workflow of the task to requeue.</param> /// <param name="taskName">Name of the task to requeue.</param> public static void Requeue(Database database, string workflowName, string taskName) { throw new NotImplementedException(); }
internal static Task Get(Database database, string workflowName, string name) { if (database == null) throw new ArgumentNullException("database", "database is null."); if (workflowName == null) throw new ArgumentNullException("workflowName", "workflowName is null."); if (name == null) throw new ArgumentNullException("name", "name is null."); Workflow workflow = Workflow.Get(database, workflowName); workflow.WorkflowLock.EnterReadLock(); try { if (!workflow.TaskNames.ContainsKey(name)) throw new Exception(string.Format("Task named \"{0}\" not found in the workflow named \"{1}\".", name, workflowName)); return workflow.Tasks[workflow.TaskNames[name]]; } finally { workflow.WorkflowLock.ExitReadLock(); } }
public static void Complete(Database database, string workflowName, string taskName) { if (database == null) throw new ArgumentNullException("database", "database is null."); if (workflowName == null) throw new ArgumentNullException("workflowName", "workflowName is null."); if (taskName == null) throw new ArgumentNullException("taskName", "taskName is null."); Workflow workflow; Task task; Queue queue; bool workflowCompleted = false; database.WorkflowsLock.EnterReadLock(); try { database.QueuesLock.EnterReadLock(); try { workflow = database.Workflows[database.WorkflowNames[workflowName]]; workflow.WorkflowLock.EnterWriteLock(); try { task = workflow.Tasks[workflow.TaskNames[taskName]]; queue = database.Queues[task.QueueId]; var queuesIdsToLock = task.DependencyTo.Select(taskId => workflow.Tasks[taskId].QueueId).ToList(); queuesIdsToLock.Add(task.QueueId); queuesIdsToLock = queuesIdsToLock.OrderBy(id => id).Distinct().ToList(); int queueLockCount = 0; try { foreach (var queueId in queuesIdsToLock) { database.Queues[queueId].QueueLock.EnterWriteLock(); queueLockCount++; } queue.RunningTasks.Remove(task.Id); task.State = TaskState.Completed; workflow.CompletedTasks.Add(task.Id); foreach (var nextTaskId in task.DependencyTo) { var nextTask = workflow.Tasks[nextTaskId]; nextTask.OutstandingDependencies.Remove(task.Id); if (nextTask.OutstandingDependencies.Count == 0) { nextTask.State = TaskState.Queued; var nextQueue = database.Queues[nextTask.QueueId]; nextQueue.QueuedTasks.AddLast(new LinkedListNode<Tuple<Guid, Guid>>(new Tuple<Guid, Guid>(workflow.Id, nextTask.Id))); } } if (workflow.CompletedTasks.Count == workflow.Tasks.Count) { workflowCompleted = true; } } finally { // Release all taken locks in order taken. for (int i = queueLockCount - 1; i >= 0; i--) { database.Queues[queuesIdsToLock[i]].QueueLock.ExitWriteLock(); } } } finally { workflow.WorkflowLock.ExitWriteLock(); } } finally { database.QueuesLock.ExitReadLock(); } } finally { database.WorkflowsLock.ExitReadLock(); } if (workflowCompleted) { database.WorkflowsLock.EnterWriteLock(); try { database.WorkflowNames.Remove(workflow.Name); database.Workflows.Remove(workflow.Id); } finally { database.WorkflowsLock.ExitWriteLock(); } } }
public static void AddDependency(Database database, string workflowName, string nameDependantOn, string nameDependancyTo) { if (database == null) throw new ArgumentNullException("database", "database is null."); if (workflowName == null) throw new ArgumentNullException("workflowName", "workflowName is null."); if (nameDependantOn == null) throw new ArgumentNullException("nameDependantOn", "nameDependantOn is null."); if (workflowName == null) throw new ArgumentNullException("nameDependancyTo", "nameDependancyTo is null."); var workflow = Workflow.Get(database, workflowName); var dependantOn = Task.Get(database, workflowName, nameDependantOn); var dependancyTo = Task.Get(database, workflowName, nameDependancyTo); var dependancyToQueue = Queue.Get(database, dependancyTo.QueueId); workflow.WorkflowLock.EnterWriteLock(); try { switch (dependancyTo.State) { case TaskState.AwaitDependence: break; case TaskState.Queued: if (!workflow.Suspended) { dependancyToQueue.QueueLock.EnterWriteLock(); try { dependancyToQueue.QueuedTasks.Remove(dependancyToQueue.QueuedTasks.First(t => t.Item2 == dependancyTo.Id)); } finally { dependancyToQueue.QueueLock.ExitWriteLock(); } } break; case TaskState.Running: case TaskState.Completed: case TaskState.Failed: default: throw new InvalidOperationException("Can only add dependancy to a task which has not yet been started."); } dependancyTo.State = TaskState.AwaitDependence; dependantOn.DependencyTo.Add(dependancyTo.Id); dependancyTo.DependantOn.Add(dependantOn.Id); switch (dependantOn.State) { case TaskState.AwaitDependence: case TaskState.Queued: case TaskState.Running: dependancyTo.OutstandingDependencies.Add(dependantOn.Id); break; } } finally { workflow.WorkflowLock.ExitWriteLock(); } }
internal static Queue Get(Database database, Guid guid) { if (database == null) throw new ArgumentNullException("database", "database is null."); database.QueuesLock.EnterReadLock(); try { return database.Queues[guid]; } finally { database.QueuesLock.ExitReadLock(); } }
static void Main(string[] args) { queueNames = Enumerable.Range(1, QueueCount).Select(i => string.Format("Queue {0}", i)).ToList(); database = new Database(); foreach (var name in queueNames) { Queue.Create(database, name); } for (int i = 0; i < QueueProcessors; i++) { ThreadPool.QueueUserWorkItem(new WaitCallback(QueueProcessor), queueNames[i % QueueCount]); } for (int i = 0; i < WorkflowCreators; i++) { ThreadPool.QueueUserWorkItem(new WaitCallback(TaskCreator)); } while (!stopping) { PrintOverview(); if (Console.KeyAvailable) { var keyPress = Console.ReadKey(true); switch (keyPress.Key) { case ConsoleKey.DownArrow: // Decrease process time. if (TaskCompletionTime.TotalMilliseconds >= 1) TaskCompletionTime = TaskCompletionTime.Add(TimeSpan.FromMilliseconds(-1)); break; case ConsoleKey.UpArrow: // Increase process time. TaskCompletionTime = TaskCompletionTime.Add(TimeSpan.FromMilliseconds(1)); break; case ConsoleKey.LeftArrow: // Decrease rate of queuing. WorkflowCreationFrequency = WorkflowCreationFrequency.Add(TimeSpan.FromMilliseconds(1)); break; case ConsoleKey.RightArrow: // Increase rate of queuing. if (WorkflowCreationFrequency.TotalMilliseconds >= 1) WorkflowCreationFrequency = WorkflowCreationFrequency.Add(TimeSpan.FromMilliseconds(-1)); break; case ConsoleKey.OemComma: // < - Decrease check frequency. QueueCheckFrequency = QueueCheckFrequency.Add(TimeSpan.FromMilliseconds(1)); break; case ConsoleKey.OemPeriod: // > - Increase check frequency. if (QueueCheckFrequency.TotalMilliseconds >= 1) QueueCheckFrequency = QueueCheckFrequency.Add(TimeSpan.FromMilliseconds(-1)); break; case ConsoleKey.X: stopping = true; break; default: break; } } Thread.Sleep(200); } }