Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
FileVersionTracker.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;
4 using System.Collections.Generic;
5 using System.IO;
6 using System.Threading;
7 using SiliconStudio.Core;
8 using SiliconStudio.Core.Diagnostics;
9 using SiliconStudio.Core.Storage;
10 
11 namespace SiliconStudio.BuildEngine
12 {
13  /// <summary>
14  /// A tracker of file date.
15  /// </summary>
16  public class FileVersionTracker : IDisposable
17  {
18  private const string DefaultFileVersionTrackerFile = @"Silicon Studio Corp\FileVersionTracker.cache";
19  private readonly FileVersionStorage storage;
20  private readonly Dictionary<FileVersionKey, object> locks;
21  private static readonly Logger log = GlobalLogger.GetLogger("FileVersionTracker");
22  private static readonly object lockDefaultTracker = new object();
23  private static FileVersionTracker defaultFileVersionTracker;
24 
25  /// <summary>
26  /// Initializes a new instance of the <see cref="FileVersionTracker"/> class.
27  /// </summary>
28  /// <param name="stream">The stream.</param>
29  public FileVersionTracker(Stream stream)
30  {
31  storage = new FileVersionStorage(stream);
32  locks = new Dictionary<FileVersionKey, object>();
33  }
34 
35  /// <summary>
36  /// Gets the default file version tracker for this machine.
37  /// </summary>
38  /// <returns>FileVersionTracker.</returns>
40  {
41  lock (lockDefaultTracker)
42  {
43  if (defaultFileVersionTracker == null)
44  {
45  var filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), DefaultFileVersionTrackerFile);
46  var directory = Path.GetDirectoryName(filePath);
47  if (directory != null && !Directory.Exists(directory))
48  {
49  Directory.CreateDirectory(directory);
50  }
51 
52  // Loads the file version cache
53  defaultFileVersionTracker = Load(filePath);
54  }
55  }
56  return defaultFileVersionTracker;
57  }
58 
59  /// <summary>
60  /// Loads previous versions stored from the specified file path.
61  /// </summary>
62  /// <param name="filePath">The file path.</param>
63  /// <returns>FileVersionTracker.</returns>
64  public static FileVersionTracker Load(string filePath)
65  {
66  // Try to compact it before using it
67  FileVersionStorage.Compact(filePath);
68 
69  bool isFirstPass = true;
70  while (true)
71  {
72  FileStream fileStream = null;
73 
74  // Try to open the file, if we get an exception, this might be due only because someone is locking the file to
75  // save it while we are trying to open it
76  const int RetryOpenFileStream = 20;
77  var random = new Random();
78  for (int i = 0; i < RetryOpenFileStream; i++)
79  {
80  try
81  {
82  fileStream = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
83  break;
84  }
85  catch (Exception)
86  {
87  if ((i + 1) == RetryOpenFileStream)
88  throw;
89 
90  Thread.Sleep(50 + random.Next(100));
91  }
92  }
93 
94  var tracker = new FileVersionTracker(fileStream);
95  try
96  {
97  tracker.storage.LoadNewValues();
98  return tracker;
99  }
100  catch (Exception)
101  {
102  // If an exception occured, we are going to try to recover from it by reseting it.
103  // reset file length to 0
104  fileStream.SetLength(0);
105  tracker.Dispose();
106  if (!isFirstPass)
107  {
108  throw;
109  }
110  }
111  isFirstPass = false;
112  }
113  }
114 
115  public ObjectId ComputeFileHash(string filePath)
116  {
117  var inputVersionKey = new FileVersionKey(filePath);
118  storage.LoadNewValues();
119 
120  // Perform a lock per file as it can be expensive to compute
121  // them at the same time (for large file)
122  object versionLock;
123  lock (locks)
124  {
125  if (!locks.TryGetValue(inputVersionKey, out versionLock))
126  {
127  versionLock = new object();
128  locks.Add(inputVersionKey, versionLock);
129  }
130  }
131 
132  var hash = ObjectId.Empty;
133  lock (versionLock)
134  {
135  if (!storage.TryGetValue(inputVersionKey, out hash))
136  {
137  try
138  {
139  using (var fileStream = File.OpenRead(filePath))
140  using (var stream = new DigestStream(Stream.Null))
141  {
142  fileStream.CopyTo(stream);
143  hash = stream.CurrentHash;
144  }
145  }
146  catch (Exception ex)
147  {
148  log.Debug("Cannot calculate hash for file [{0}]", ex, filePath);
149  }
150  storage[inputVersionKey] = hash;
151  }
152  }
153 
154  return hash;
155  }
156 
157  public void Dispose()
158  {
159  storage.Dispose();
160  }
161  }
162 
163  [DataContract]
164  public struct FileVersionKey : IEquatable<FileVersionKey>
165  {
166  public string Path;
167 
168  public DateTime LastModifiedDate;
169 
170  public long FileSize;
171 
172  public FileVersionKey(string path)
173  {
174  if (path == null) throw new ArgumentNullException("path");
175  Path = path;
176  LastModifiedDate = DateTime.MinValue;
177  FileSize = -1;
178 
179  if (File.Exists(path))
180  {
181  LastModifiedDate = File.GetLastWriteTime(path);
182  FileSize = new FileInfo(path).Length;
183  }
184  }
185 
186  public bool Equals(FileVersionKey other)
187  {
188  return string.Equals(Path, other.Path) && LastModifiedDate.Equals(other.LastModifiedDate) && FileSize == other.FileSize;
189  }
190 
191  public override bool Equals(object obj)
192  {
193  if (ReferenceEquals(null, obj)) return false;
194  return obj is FileVersionKey && Equals((FileVersionKey)obj);
195  }
196 
197  public override int GetHashCode()
198  {
199  unchecked
200  {
201  int hashCode = (Path != null ? Path.GetHashCode() : 0);
202  hashCode = (hashCode * 397) ^ LastModifiedDate.GetHashCode();
203  hashCode = (hashCode * 397) ^ FileSize.GetHashCode();
204  return hashCode;
205  }
206  }
207 
208  public static bool operator ==(FileVersionKey left, FileVersionKey right)
209  {
210  return left.Equals(right);
211  }
212 
213  public static bool operator !=(FileVersionKey left, FileVersionKey right)
214  {
215  return !left.Equals(right);
216  }
217  }
218 
219 }
Storage used for FileVersionKey associated with an ObjectId.
static FileVersionTracker GetDefault()
Gets the default file version tracker for this machine.
System.IO.FileMode FileMode
Definition: ScriptSync.cs:33
FileVersionTracker(Stream stream)
Initializes a new instance of the FileVersionTracker class.
Base implementation for ILogger.
Definition: Logger.cs:10
System.IO.File File
static FileVersionTracker Load(string filePath)
Loads previous versions stored from the specified file path.
A hash to uniquely identify data.
Definition: ObjectId.cs:13