diff --git a/SquishIt.Framework/Base/BundleBase.cs b/SquishIt.Framework/Base/BundleBase.cs index b444b0c..8aa9f39 100644 --- a/SquishIt.Framework/Base/BundleBase.cs +++ b/SquishIt.Framework/Base/BundleBase.cs @@ -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; @@ -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(); - 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(); + 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(); - foreach (var asset in groupBundle.Assets) - { - if (asset.IsRemote) - { - remoteAssetPaths.Add(asset.RemotePath); - } - } + var remoteAssetPaths = new List(); + 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); } diff --git a/SquishIt.Framework/Files/RetryableFileOpener.cs b/SquishIt.Framework/Files/RetryableFileOpener.cs index 4d0b451..5d8c8ae 100644 --- a/SquishIt.Framework/Files/RetryableFileOpener.cs +++ b/SquishIt.Framework/Files/RetryableFileOpener.cs @@ -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; @@ -61,7 +61,6 @@ public FileStream OpenFileStream(string filePath, int retry, FileMode fileMode, delay += 100; if (i == retry) throw; } - Thread.Sleep(delay); } diff --git a/SquishIt.Tests/MultipleWorkerProcessesTests.cs b/SquishIt.Tests/MultipleWorkerProcessesTests.cs new file mode 100644 index 0000000..3ed7d5d --- /dev/null +++ b/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++; + } + }*/ + } +} diff --git a/SquishIt.Tests/SquishIt.Tests.csproj b/SquishIt.Tests/SquishIt.Tests.csproj index 409b825..624aa06 100644 --- a/SquishIt.Tests/SquishIt.Tests.csproj +++ b/SquishIt.Tests/SquishIt.Tests.csproj @@ -77,6 +77,7 @@ +