Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
CommandIOMonitor.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System.Collections.Generic;
4 using System.Diagnostics;
5 using System.Linq;
6 using SiliconStudio.Core.Diagnostics;
7 using SiliconStudio.Core.Serialization.Assets;
8 using SiliconStudio.Core.Storage;
9 
10 namespace SiliconStudio.BuildEngine
11 {
12  /// <summary>
13  /// This class monitors input/output access from every BuildStep execution, and display an error message if an object url is the input of a command and the output of another command running at the same time.
14  /// </summary>
15  internal class CommandIOMonitor
16  {
17  /// <summary>
18  /// A dictionary containing read access timings (value) of a given object url (key)
19  /// </summary>
20  private readonly Dictionary<ObjectUrl, List<TimeInterval<BuildStep>>> readAccesses = new Dictionary<ObjectUrl, List<TimeInterval<BuildStep>>>();
21 
22  /// <summary>
23  /// A dictionary containing write access timings (value) of a given object url (key)
24  /// </summary>
25  private readonly Dictionary<ObjectUrl, List<TimeInterval<KeyValuePair<BuildStep, ObjectId>>>> writeAccesses = new Dictionary<ObjectUrl, List<TimeInterval<KeyValuePair<BuildStep, ObjectId>>>>();
26 
27  /// <summary>
28  /// A dictionary containing execution intervals of BuildStep
29  /// </summary>
30  private readonly Dictionary<CommandBuildStep, TimeInterval> commandExecutionIntervals = new Dictionary<CommandBuildStep, TimeInterval>();
31 
32  private readonly ILogger logger;
33 
34  private readonly object lockObject = new object();
35 
36  private readonly Stopwatch stopWatch = new Stopwatch();
37 
38  public CommandIOMonitor(ILogger logger)
39  {
40  this.logger = logger;
41  stopWatch.Start();
42  }
43 
44  public void CommandStarted(CommandBuildStep command)
45  {
46  lock (lockObject)
47  {
48  long startTime = stopWatch.ElapsedTicks;
49  commandExecutionIntervals.Add(command, new TimeInterval(startTime));
50 
51  var inputHash = new HashSet<ObjectUrl>();
52  foreach (ObjectUrl inputUrl in command.Command.GetInputFiles())
53  {
54  if (inputHash.Contains(inputUrl))
55  logger.Error("The command '{0}' has several times the file '{1}' as input. Input Files must not be duplicated", command.Title, inputUrl.Path);
56  inputHash.Add(inputUrl);
57 
58  List<TimeInterval<BuildStep>> inputReadAccess;
59  if (!readAccesses.TryGetValue(inputUrl, out inputReadAccess))
60  {
61  inputReadAccess = new List<TimeInterval<BuildStep>> { new TimeInterval<BuildStep>(command, startTime) };
62  readAccesses.Add(inputUrl, inputReadAccess);
63  }
64  else
65  {
66  inputReadAccess.Add(new TimeInterval<BuildStep>(command, startTime));
67  }
68  }
69  }
70  }
71 
72  public void CommandEnded(CommandBuildStep command)
73  {
74  lock (lockObject)
75  {
76  TimeInterval commandInterval = commandExecutionIntervals[command];
77  long startTime = commandInterval.StartTime;
78  long endTime = stopWatch.ElapsedTicks;
79  commandInterval.End(endTime);
80 
81  foreach (var outputObject in command.Result.OutputObjects)
82  {
83  var outputUrl = outputObject.Key;
84  List<TimeInterval<BuildStep>> inputReadAccess;
85  if (readAccesses.TryGetValue(outputUrl, out inputReadAccess))
86  {
87  foreach (TimeInterval<BuildStep> input in inputReadAccess.Where(input => input.Object != command && input.Overlap(startTime, endTime)))
88  {
89  logger.Error("Command {0} is writing {1} while command {2} is reading it", command, outputUrl, input.Object);
90  }
91  }
92 
93  List<TimeInterval<KeyValuePair<BuildStep, ObjectId>>> outputWriteAccess;
94  if (!writeAccesses.TryGetValue(outputUrl, out outputWriteAccess))
95  {
96  outputWriteAccess = new List<TimeInterval<KeyValuePair<BuildStep, ObjectId>>> { new TimeInterval<KeyValuePair<BuildStep, ObjectId>>(new KeyValuePair<BuildStep, ObjectId>(command, outputObject.Value), startTime, endTime) };
97  writeAccesses.Add(outputUrl, outputWriteAccess);
98  }
99  else
100  {
101  foreach (var output in outputWriteAccess.Where(output => output.Object.Key != command && output.Overlap(startTime, endTime)))
102  {
103  if (outputObject.Value != output.Object.Value)
104  logger.Error("Commands {0} and {1} are both writing {2} at the same time, but they are different objects", command, output.Object, outputUrl);
105  }
106  outputWriteAccess.Add(new TimeInterval<KeyValuePair<BuildStep, ObjectId>>(new KeyValuePair<BuildStep, ObjectId>(command, outputObject.Value), startTime, endTime));
107  }
108  }
109 
110  foreach (ObjectUrl inputUrl in command.Result.InputDependencyVersions.Keys)
111  {
112  List<TimeInterval<KeyValuePair<BuildStep, ObjectId>>> outputWriteAccess;
113  if (writeAccesses.TryGetValue(inputUrl, out outputWriteAccess))
114  {
115  foreach (TimeInterval<KeyValuePair<BuildStep, ObjectId>> output in outputWriteAccess.Where(output => output.Object.Key != command && output.Overlap(startTime, endTime)))
116  {
117  logger.Error("Command {0} is writing {1} while command {2} is reading it", output.Object, inputUrl, command);
118  }
119  }
120  }
121 
122  foreach (ObjectUrl input in command.Command.GetInputFiles())
123  {
124  readAccesses[input].Single(x => x.Object == command).End(endTime);
125  }
126  }
127  }
128  }
129 }
Interface for logging.
Definition: ILogger.cs:8