Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
FileVersionManager.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.Concurrent;
5 using System.Collections.Generic;
6 using System.IO;
7 using System.Linq;
8 using System.Threading;
9 using SiliconStudio.BuildEngine;
10 using SiliconStudio.Core.Diagnostics;
11 using SiliconStudio.Core.IO;
12 using SiliconStudio.Core.Storage;
13 
14 namespace SiliconStudio.Assets
15 {
16  public class FileVersionManager
17  {
18  private static readonly object TrackerLock = new object();
19  private static FileVersionManager instance;
20  private readonly FileVersionTracker tracker;
21  private readonly Thread asyncRunner;
22  private readonly AutoResetEvent asyncRequestAvailable;
23  private readonly ConcurrentQueue<AsyncRequest> asyncRequests;
24  private bool isDisposing;
25  private bool isDisposed;
26 
27  private FileVersionManager()
28  {
29  // Environment.SpecialFolder.ApplicationData
30  asyncRequestAvailable = new AutoResetEvent(false);
31  asyncRequests = new ConcurrentQueue<AsyncRequest>();
32 
33  // Loads the file version cache
34  tracker = FileVersionTracker.GetDefault();
35  asyncRunner = new Thread(SafeAction.Wrap(ComputeFileHashAsyncRunner)) { IsBackground = true };
36  asyncRunner.Start();
37  }
38 
39  public static FileVersionManager Instance
40  {
41  get
42  {
43  lock (TrackerLock)
44  {
45  if (instance != null)
46  return instance;
47 
48  instance = new FileVersionManager();
49  AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
50  return instance;
51  }
52  }
53  }
54 
55  public static void Shutdown()
56  {
57  lock (TrackerLock)
58  {
59  if (instance == null)
60  return;
61  instance.Dispose();
62  instance = null;
63  }
64  }
65 
67  {
68  if (!File.Exists(path))
69  return ObjectId.Empty;
70 
71  return tracker.ComputeFileHash(path);
72  }
73 
74 
75  public void ComputeFileHashAsync(UFile path, Action<UFile, ObjectId> fileHashCallback = null, CancellationToken? cancellationToken = null)
76  {
77  if (path == null) throw new ArgumentNullException("path");
78 
79  lock (asyncRequests)
80  {
81  asyncRequests.Enqueue(new AsyncRequest(path, fileHashCallback, cancellationToken));
82  }
83  asyncRequestAvailable.Set();
84  }
85 
86  public void ComputeFileHashAsync(IEnumerable<UFile> paths, Action<UFile, ObjectId> fileHashCallback = null, CancellationToken? cancellationToken = null)
87  {
88  if (paths == null) throw new ArgumentNullException("paths");
89 
90  lock (asyncRequests)
91  {
92  foreach(var path in paths)
93  asyncRequests.Enqueue(new AsyncRequest(path, fileHashCallback, cancellationToken));
94  }
95  asyncRequestAvailable.Set();
96  }
97 
98  private readonly HashSet<AsyncRequest> requestsToProcess = new HashSet<AsyncRequest>();
99 
100  private void ComputeFileHashAsyncRunner()
101  {
102  while (!isDisposing)
103  {
104  if (asyncRequestAvailable.WaitOne())
105  {
106  lock (asyncRequests)
107  {
108  // Dequeue as much as possible in a single row
109  while (true)
110  {
111  AsyncRequest asyncRequest;
112  if (asyncRequests.TryDequeue(out asyncRequest))
113  {
114  requestsToProcess.Add(asyncRequest);
115  }
116  else
117  {
118  break;
119  }
120  }
121  }
122  }
123 
124  // Early exit
125  if (isDisposing)
126  {
127  return;
128  }
129 
130  foreach (var request in requestsToProcess)
131  {
132  if (isDisposing)
133  {
134  return;
135  }
136 
137  if (request.CancellationToken.HasValue && request.CancellationToken.Value.IsCancellationRequested)
138  {
139  continue;
140  }
141 
142  var hash = ComputeFileHash(request.File);
143 
144  if (request.CancellationToken.HasValue && request.CancellationToken.Value.IsCancellationRequested)
145  {
146  continue;
147  }
148 
149  if (isDisposing)
150  {
151  return;
152  }
153 
154  if (request.FileHashCallback != null)
155  {
156  request.FileHashCallback(request.File, hash);
157  }
158  }
159  // Once we have processed the list, we can clear it
160  requestsToProcess.Clear();
161  }
162  }
163 
164  private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
165  {
166  Shutdown();
167  }
168 
169  private void Dispose()
170  {
171  if (isDisposed)
172  return;
173 
174  // Set to true and let the async runner thread terminates
175  isDisposing = true;
176  asyncRequestAvailable.Set();
177 
178  asyncRunner.Join();
179 
180  isDisposed = true;
181  }
182 
183  private struct AsyncRequest : IEquatable<AsyncRequest>
184  {
185  public AsyncRequest(UFile file, Action<UFile, ObjectId> fileHashCallback, CancellationToken? cancellationToken)
186  {
187  File = file;
188  FileHashCallback = fileHashCallback;
189  CancellationToken = cancellationToken;
190  }
191 
192  public readonly UFile File;
193 
194  public readonly Action<UFile, ObjectId> FileHashCallback;
195 
196  public readonly CancellationToken? CancellationToken;
197 
198  public bool Equals(AsyncRequest other)
199  {
200  return Equals(File, other.File) && Equals(FileHashCallback, other.FileHashCallback) && CancellationToken.Equals(other.CancellationToken);
201  }
202 
203  public override bool Equals(object obj)
204  {
205  if (ReferenceEquals(null, obj)) return false;
206  return obj is AsyncRequest && Equals((AsyncRequest)obj);
207  }
208 
209  public override int GetHashCode()
210  {
211  unchecked
212  {
213  int hashCode = (File != null ? File.GetHashCode() : 0);
214  hashCode = (hashCode*397) ^ (FileHashCallback != null ? FileHashCallback.GetHashCode() : 0);
215  hashCode = (hashCode*397) ^ CancellationToken.GetHashCode();
216  return hashCode;
217  }
218  }
219 
220  public static bool operator ==(AsyncRequest left, AsyncRequest right)
221  {
222  return left.Equals(right);
223  }
224 
225  public static bool operator !=(AsyncRequest left, AsyncRequest right)
226  {
227  return !left.Equals(right);
228  }
229  }
230  }
231 }
static ThreadStart Wrap(ThreadStart action, [CallerFilePath] string sourceFilePath="", [CallerMemberName] string memberName="", [CallerLineNumber] int sourceLineNumber=0)
Definition: SafeAction.cs:13
void ComputeFileHashAsync(IEnumerable< UFile > paths, Action< UFile, ObjectId > fileHashCallback=null, CancellationToken?cancellationToken=null)
void ComputeFileHashAsync(UFile path, Action< UFile, ObjectId > fileHashCallback=null, CancellationToken?cancellationToken=null)
System.IO.File File
static readonly ObjectId Empty
Definition: ObjectId.cs:15
A hash to uniquely identify data.
Definition: ObjectId.cs:13
Defines a normalized file path. See UPath for details. This class cannot be inherited.
Definition: UFile.cs:13