Skip to content

Commit

Permalink
Fix for exceptions thrown during render with multiple worker processe…
Browse files Browse the repository at this point in the history
…s. Keeps multiple processes from competing during renders.
  • Loading branch information
jetheredge committed Dec 4, 2011
1 parent e3e52b3 commit 1019c0b
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 82 deletions.
168 changes: 91 additions & 77 deletions SquishIt.Framework/Base/BundleBase.cs
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web;
using SquishIt.Framework.Minifiers;
using SquishIt.Framework.Resolvers;
Expand Down Expand Up @@ -401,102 +402,115 @@ protected string RenderDebug(string name = null)
return content;
}

private static string renderMutexId = "C9CA8ED9-9354-4047-8601-5CC0602FC505";
private static Mutex renderMutex = new Mutex(false, renderMutexId);
private string RenderRelease(string key, string renderTo, IRenderer renderer)
{
string content;
if (!bundleCache.TryGetValue(key, out content))
{
var files = new List<string>();
foreach (var groupBundleKVP in GroupBundles)
renderMutex.WaitOne();
try
{
var group = groupBundleKVP.Key;
var groupBundle = groupBundleKVP.Value;
if (!bundleCache.TryGetValue(key, out content))
{
var files = new List<string>();
foreach (var groupBundleKVP in GroupBundles)
{
var group = groupBundleKVP.Key;
var groupBundle = groupBundleKVP.Value;

string minifiedContent = null;
string hash = null;
bool hashInFileName = false;
string minifiedContent = null;
string hash = null;
bool hashInFileName = false;

DependentFiles.Clear();
DependentFiles.Clear();

if (renderTo == null)
{
renderTo = renderPathCache[CachePrefix + "." + group + "." + key];
}
else
{
renderPathCache[CachePrefix + "." + group + "." + key] = renderTo;
}
if (renderTo == null)
{
renderTo = renderPathCache[CachePrefix + "." + group + "." + key];
}
else
{
renderPathCache[CachePrefix + "." + group + "." + key] = renderTo;
}

string outputFile = FileSystem.ResolveAppRelativePathToFileSystem(renderTo);
var renderToPath = ExpandAppRelativePath(renderTo);
string outputFile = FileSystem.ResolveAppRelativePathToFileSystem(renderTo);
var renderToPath = ExpandAppRelativePath(renderTo);

if (!String.IsNullOrEmpty(BaseOutputHref))
{
renderToPath = String.Concat(BaseOutputHref.TrimEnd('/'), "/", renderToPath.TrimStart('/'));
}
if (!String.IsNullOrEmpty(BaseOutputHref))
{
renderToPath = String.Concat(BaseOutputHref.TrimEnd('/'), "/", renderToPath.TrimStart('/'));
}

var remoteAssetPaths = new List<string>();
foreach (var asset in groupBundle.Assets)
{
if (asset.IsRemote)
{
remoteAssetPaths.Add(asset.RemotePath);
}
}
var remoteAssetPaths = new List<string>();
foreach (var asset in groupBundle.Assets)
{
if (asset.IsRemote)
{
remoteAssetPaths.Add(asset.RemotePath);
}
}

files.AddRange(GetFiles(groupBundle.Assets.Where(asset =>
asset.IsEmbeddedResource ||
asset.IsLocal ||
asset.IsRemoteDownload).ToList()));
files.AddRange(GetFiles(groupBundle.Assets.Where(asset =>
asset.IsEmbeddedResource ||
asset.IsLocal ||
asset.IsRemoteDownload).ToList()));

DependentFiles.AddRange(files);
DependentFiles.AddRange(files);

if (renderTo.Contains("#"))
{
hashInFileName = true;
minifiedContent = Minifier.Minify(BeforeMinify(outputFile, files, arbitrary));
hash = hasher.GetHash(minifiedContent);
renderToPath = renderToPath.Replace("#", hash);
outputFile = outputFile.Replace("#", hash);
}
if (renderTo.Contains("#"))
{
hashInFileName = true;
minifiedContent = Minifier.Minify(BeforeMinify(outputFile, files, arbitrary));
hash = hasher.GetHash(minifiedContent);
renderToPath = renderToPath.Replace("#", hash);
outputFile = outputFile.Replace("#", hash);
}

if (ShouldRenderOnlyIfOutputFileIsMissing && FileExists(outputFile))
{
minifiedContent = ReadFile(outputFile);
}
else
{
minifiedContent = minifiedContent ?? Minifier.Minify(BeforeMinify(outputFile, files, arbitrary));
renderer.Render(minifiedContent, outputFile);
}
if (ShouldRenderOnlyIfOutputFileIsMissing && FileExists(outputFile))
{
minifiedContent = ReadFile(outputFile);
}
else
{
minifiedContent = minifiedContent ?? Minifier.Minify(BeforeMinify(outputFile, files, arbitrary));
renderer.Render(minifiedContent, outputFile);
}

if (hash == null && !string.IsNullOrEmpty(HashKeyName))
{
hash = hasher.GetHash(minifiedContent);
}
if (hash == null && !string.IsNullOrEmpty(HashKeyName))
{
hash = hasher.GetHash(minifiedContent);
}

string renderedTag;
if (hashInFileName)
{
renderedTag = FillTemplate(groupBundle, renderToPath);
}
else
{
if (string.IsNullOrEmpty(HashKeyName))
{
renderedTag = FillTemplate(groupBundle, renderToPath);
}
else if (renderToPath.Contains("?"))
{
renderedTag = FillTemplate(groupBundle, renderToPath + "&" + HashKeyName + "=" + hash);
}
else
{
renderedTag = FillTemplate(groupBundle, renderToPath + "?" + HashKeyName + "=" + hash);
}
}
string renderedTag;
if (hashInFileName)
{
renderedTag = FillTemplate(groupBundle, renderToPath);
}
else
{
if (string.IsNullOrEmpty(HashKeyName))
{
renderedTag = FillTemplate(groupBundle, renderToPath);
}
else if (renderToPath.Contains("?"))
{
renderedTag = FillTemplate(groupBundle, renderToPath + "&" + HashKeyName + "=" + hash);
}
else
{
renderedTag = FillTemplate(groupBundle, renderToPath + "?" + HashKeyName + "=" + hash);
}
}

content += String.Concat(GetFilesForRemote(remoteAssetPaths, groupBundle), renderedTag);
content += String.Concat(GetFilesForRemote(remoteAssetPaths, groupBundle), renderedTag);
}
}
}
finally
{
renderMutex.ReleaseMutex();
}
bundleCache.Add(key, content, DependentFiles);
}
Expand Down
9 changes: 4 additions & 5 deletions SquishIt.Framework/Files/RetryableFileOpener.cs
Expand Up @@ -48,10 +48,10 @@ public FileStream OpenFileStream(string filePath, int retry, FileMode fileMode,
var stream = new FileStream(filePath, fileMode, fileAccess, fileShare);
return stream;
}
catch(DirectoryNotFoundException)
{
throw;
}
catch(DirectoryNotFoundException)
{
throw;
}
catch (FileNotFoundException)
{
throw;
Expand All @@ -61,7 +61,6 @@ public FileStream OpenFileStream(string filePath, int retry, FileMode fileMode,
delay += 100;
if (i == retry) throw;
}

Thread.Sleep(delay);
}

Expand Down
92 changes: 92 additions & 0 deletions SquishIt.Tests/MultipleWorkerProcessesTests.cs
@@ -0,0 +1,92 @@
using System;
using System.IO;
using System.Linq;
using System.Threading;
using NUnit.Framework;
using SquishIt.Framework;

namespace SquishIt.Tests
{
[TestFixture]
public class MultipleWorkerProcessesTests
{
[Test,Explicit]
public void ExceptionIsThrowWhenMultipleWorkerThreadsAreWorking()
{
int writingThreads = 4;
int readingThreads = 20;
int numberOfExecutions = 500;

for (int i = 0; i < numberOfExecutions; i++)
{
for (int y = 0; y < writingThreads; y++)
{
Thread writingThread = new Thread(new ThreadStart(() =>
{
try
{
System.Diagnostics.Debug.WriteLine("Writing CSS...");
Bundle.Css()
.Add("1.css")
.Add("2.css")
.Add("3.css")
.Add("4.css")
.Add("5.css")
.Add("6.css")
.ForceRelease()
.Render("combined_#.css");
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}));

writingThread.Start();
}

for (int y = 0; y < readingThreads; y++)
{
Thread readingThread = new Thread(new ThreadStart(() =>
{
var files = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "combined*.css");
var ii = 0;
while (ii < 1000)
{
System.Diagnostics.Debug.WriteLine("Reading CSS...");
files.ToList().ForEach(x => File.ReadAllText(x));
Thread.Sleep(50);
ii++;
}
}));

readingThread.Start();
}
}

Thread.Sleep(20000);
}

/*public void ReadAllCssFiles(object state)
{
var files = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "combined*.css");
var i = 0;
while (i < 2000)
{
System.Diagnostics.Debug.WriteLine("Reading CSS...");
files.ToList().ForEach(x => File.ReadAllText(x));
Thread.Sleep(50);
i++;
}
}*/
}
}
1 change: 1 addition & 0 deletions SquishIt.Tests/SquishIt.Tests.csproj
Expand Up @@ -77,6 +77,7 @@
<Compile Include="Helpers\ResolverFactoryScope.cs" />
<Compile Include="JavaScriptBundleTests.cs" />
<Compile Include="JavaScriptMinifierTests.cs" />
<Compile Include="MultipleWorkerProcessesTests.cs" />
<Compile Include="Stubs\StubBundleCache.cs" />
<Compile Include="Stubs\StubCurrentDirectoryWrapper.cs" />
<Compile Include="Stubs\StubDebugStatusReader.cs" />
Expand Down

0 comments on commit 1019c0b

Please sign in to comment.