/// <summary> /// Handler for DivideProblemMessage. /// </summary> /// <param name="message">A DivideProblemMessage.</param> /// <exception cref="System.InvalidOperationException"> /// Thrown when: /// - message is designated for TaskManger with different ID, /// - problem type can't be divided with this TaskManager, /// - dividing the problem cannot be started bacause no threads are available in thread pool. /// </exception> private void DivideProblemMessageHandler(DivideProblemMessage message) { if (Id != message.TaskManagerId) { // shouldn't ever get here - received message for other TaskManager throw new InvalidOperationException( string.Format("TaskManager with ID={0} received message for TaskManager with ID={1}.", Id, message.TaskManagerId)); } Type taskSolverType; if (!TaskSolvers.TryGetValue(message.ProblemType, out taskSolverType)) { // shouldn't ever get here - received unsolvable problem throw new InvalidOperationException( string.Format("\"{0}\" problem type can't be divided with this TaskManager.", message.ProblemType)); } var actionDescription = string.Format("Dividing problem \"{0}\"(problem instance id={1})", message.ProblemType, message.ProblemInstanceId); // should be started properly cause server sends at most as many tasks to do as count of component's threads in idle state var started = StartActionInNewThread(() => { var taskSolver = (TaskSolver)Activator.CreateInstance(taskSolverType, message.ProblemData); taskSolver.ThrowIfError(); var partialProblemsData = taskSolver.DivideProblem((int)message.ComputationalNodes); taskSolver.ThrowIfError(); var partialProblems = new List <PartialProblemsMessage.PartialProblem>(partialProblemsData.GetLength(0)); for (var i = 0; i < partialProblemsData.GetLength(0); i++) { partialProblems.Add(new PartialProblemsMessage.PartialProblem { PartialProblemId = (ulong)i, Data = partialProblemsData[i], TaskManagerId = Id }); } var partialProblemsMessage = new PartialProblemsMessage { ProblemType = message.ProblemType, ProblemInstanceId = message.ProblemInstanceId, CommonData = message.ProblemData, PartialProblems = partialProblems }; EnqueueMessageToSend(partialProblemsMessage); }, actionDescription, message.ProblemType, message.ProblemInstanceId, null); if (!started) { throw new InvalidOperationException( "Couldn't divide problem because couldn't start new thread."); } }
/// <summary> /// Handler for PartialProblemsMessage. /// </summary> /// <param name="message">A PartialProblemsMessage.</param> /// <exception cref="System.InvalidOperationException"> /// Thrown when: /// - problem type can't be solved with this ComputationalNode, /// - received more partial problems than can be currently started. /// </exception> private void PartialProblemsMessageHandler(PartialProblemsMessage message) { Type taskSolverType; if (!TaskSolvers.TryGetValue(message.ProblemType, out taskSolverType)) { // shouldn't ever get here - received unsolvable problem throw new InvalidOperationException( string.Format("\"{0}\" problem type can't be solved with this ComputationalNode.", message.ProblemType)); } var timeout = message.SolvingTimeout.HasValue ? TimeSpan.FromMilliseconds(message.SolvingTimeout.Value) : TimeSpan.MaxValue; foreach (var partialProblem in message.PartialProblems) { /* each partial problem should be started properly cause server sends at most * as many partial problems as count of component's tasks in idle state */ var actionDescription = string.Format("Solving partial problem \"{0}\"(problem instance id={1})(partial problem id={2})", message.ProblemType, message.ProblemInstanceId, partialProblem.PartialProblemId); var started = StartActionInNewThread(() => { // not sure if TaskSolver can change CommonData during computations so recreate it for each partial problem var taskSolver = (TaskSolver)Activator.CreateInstance(taskSolverType, message.CommonData); taskSolver.ThrowIfError(); // measure time using DateTime cause StopWatch is not guaranteed to be thread safe var start = DateTime.UtcNow; var partialProblemSolutionData = taskSolver.Solve(partialProblem.Data, timeout); var stop = DateTime.UtcNow; taskSolver.ThrowIfError(); var solutions = new List <SolutionsMessage.Solution> { new SolutionsMessage.Solution { PartialProblemId = partialProblem.PartialProblemId, TimeoutOccured = taskSolver.State == TaskSolver.TaskSolverState.Timeout, Type = SolutionsMessage.SolutionType.Partial, ComputationsTime = (ulong)(stop - start).TotalMilliseconds, Data = partialProblemSolutionData } }; var solutionsMessage = new SolutionsMessage { ProblemType = message.ProblemType, ProblemInstanceId = message.ProblemInstanceId, CommonData = message.CommonData, Solutions = solutions }; EnqueueMessageToSend(solutionsMessage); }, actionDescription, message.ProblemType, message.ProblemInstanceId, partialProblem.PartialProblemId); if (!started) { throw new InvalidOperationException("Received more partial problems than can be currently started."); } } }
/// <summary> /// Handler for SolutionsMessage. /// </summary> /// <param name="message">A SolutionsMessage.</param> /// <exception cref="System.InvalidOperationException"> /// Thrown when: /// - problem type can't be merged with this TaskManager, /// - merging the problem cannot be started bacause no threads are available in thread pool. /// </exception> private void SolutionsMessageHandler(SolutionsMessage message) { Type taskSolverType; if (!TaskSolvers.TryGetValue(message.ProblemType, out taskSolverType)) { // shouldn't ever get here - received unsolvable problem throw new InvalidOperationException( string.Format("\"{0}\" problem type can't be merged with this TaskManager.", message.ProblemType)); } var actionDescription = string.Format("Merging partial problems \"{0}\"(problem instance id={1})", message.ProblemType, message.ProblemInstanceId); // should be started properly cause server sends at most as many tasks to do as count of component's threads in idle state var started = StartActionInNewThread(() => { ulong totalComputationsTime = 0; var timeoutOccured = false; var solutionsData = new byte[message.Solutions.Count][]; for (var i = 0; i < message.Solutions.Count; i++) { var solution = message.Solutions[i]; if (solution.Type != SolutionsMessage.SolutionType.Partial) { throw new InvalidOperationException( string.Format("Received non-partial solution({0})(partial problem id={1}).", solution.Type, solution.PartialProblemId)); } totalComputationsTime += solution.ComputationsTime; timeoutOccured |= solution.TimeoutOccured; solutionsData[i] = solution.Data; } var taskSolver = (TaskSolver)Activator.CreateInstance(taskSolverType, message.CommonData); taskSolver.ThrowIfError(); // measure time using DateTime cause StopWatch is not guaranteed to be thread safe var start = DateTime.UtcNow; var finalSolutionData = taskSolver.MergeSolution(solutionsData); var stop = DateTime.UtcNow; taskSolver.ThrowIfError(); totalComputationsTime += (ulong)((stop - start).TotalMilliseconds); var finalSolution = new SolutionsMessage.Solution { Type = SolutionsMessage.SolutionType.Final, ComputationsTime = totalComputationsTime, TimeoutOccured = timeoutOccured, Data = finalSolutionData }; var finalSolutionMessage = new SolutionsMessage { ProblemType = message.ProblemType, ProblemInstanceId = message.ProblemInstanceId, CommonData = message.CommonData, Solutions = new List <SolutionsMessage.Solution> { finalSolution } }; EnqueueMessageToSend(finalSolutionMessage); }, actionDescription, message.ProblemType, message.ProblemInstanceId, null); if (!started) { throw new InvalidOperationException( "Couldn't merge partial solutions because couldn't start new thread."); } }