/// <summary>The parallel for each call, that ensures valid multi threaded opeartions for webgrease calls.</summary> /// <param name="idParts">The id parts.</param> /// <param name="items">The items.</param> /// <param name="parallelAction">The parallel action.</param> /// <param name="serialAction">The serial action.</param> /// <typeparam name="T">The type of items</typeparam> public void ParallelForEach <T>(Func <T, string[]> idParts, IEnumerable <T> items, Func <IWebGreaseContext, T, ParallelLoopState, bool> parallelAction, Func <IWebGreaseContext, T, bool> serialAction = null) { var id = idParts(default(T)); this.SectionedAction(id).Execute(() => { var serialLock = new object(); var parallelForEachItems = new List <Tuple <IWebGreaseContext, DelayedLogManager, T> >(); var done = 0; foreach (var item in items) { // TODO: Better name then item ToString? var delayedLogManager = new DelayedLogManager(this.Log, item.ToString()); var threadContext = new WebGreaseContext(new WebGreaseConfiguration(this.Configuration), delayedLogManager.LogManager, this.Cache.CurrentCacheSection, this.Preprocessing); var success = true; if (serialAction != null) { success = serialAction(threadContext, item); } if (success) { parallelForEachItems.Add(new Tuple <IWebGreaseContext, DelayedLogManager, T>(threadContext, delayedLogManager, item)); } } Parallel.ForEach( parallelForEachItems, (item, state) => { var sectionId = ToStringId(idParts(item.Item3)); parallelAction(item.Item1, item.Item3, state); var measureResult = item.Item1.Measure.GetResults(); Safe.Lock( serialLock, Safe.MaxLockTimeout, () => { this.threadedMeasureResults.AddRange(item.Item1.ThreadedMeasureResults); this.threadedMeasureResults.Add(new KeyValuePair <string, IEnumerable <TimeMeasureResult> >(sectionId, measureResult)); item.Item2.Flush(); done++; if (done == parallelForEachItems.Count - 1) { parallelForEachItems.ForEach(i => i.Item2.Flush()); } }); }); }); }
/// <summary>Add resultsToAdd to results.</summary> /// <param name="resultsToAdd">The results To Add.</param> /// <param name="groupSelector">The group Selector.</param> /// <returns>The grouped result</returns> public static IEnumerable <TimeMeasureResult> Group(this IEnumerable <TimeMeasureResult> resultsToAdd, Func <TimeMeasureResult, string> groupSelector) { return (resultsToAdd .GroupBy(groupSelector) .Select(s => new TimeMeasureResult { IdParts = WebGreaseContext.ToIdParts(s.Key), Count = s.Min(m => m.Count), Duration = s.Sum(m => m.Duration) }) .OrderByDescending(r => r.Duration) .ToArray()); }
/// <summary>Gets the measure results for this timer.</summary> /// <returns>The measure results.</returns> public TimeMeasureResult[] GetResults() { return (this.measurements.Last().OrderByDescending(m => m.Value) .Select( m => new TimeMeasureResult { IdParts = WebGreaseContext.ToIdParts(m.Key), Duration = m.Value, Count = this.measurementCounts.Last()[m.Key] }) .ToArray()); }
/// <summary>The main entry point to the tool.</summary> /// <param name="args">The command line parameters.</param> /// <returns>The program result.</returns> internal static int Main(string[] args) { try { if (args == null || !args.Any()) { Console.WriteLine(ResourceStrings.Usage); return(1); } WebGreaseConfiguration config; var mode = GenerateConfiguration(args, out config); var context = new WebGreaseContext(config); switch (mode) { case ActivityMode.Minify: ExecuteMinification(context); break; case ActivityMode.Validate: ExecuteValidation(context); break; case ActivityMode.Bundle: ExecuteBundling(context); break; case ActivityMode.AutoName: ExecuteHashFiles(context); break; case ActivityMode.SpriteImages: ExecuteImageSpriting(context); break; default: Console.WriteLine(ResourceStrings.Usage); return(1); } } catch (Exception ex) { // general catch so unhandled exceptions don't result in a stack dump on the screen HandleError(ex, null, ResourceStrings.GeneralErrorMessage); return(-1); } return(0); }
/// <summary>Starts a new timer.</summary> /// <param name="isGroup">If it should also start a Group.</param> /// <param name="idParts">The id parts.</param> public void Start(bool isGroup, params string[] idParts) { var id = WebGreaseContext.ToStringId(idParts); if (this.timers.Any(t => t.Id.Equals(id))) { throw new BuildWorkflowException("An error occurred while starting timer for {0}, probably a wrong start/end for key: ".InvariantFormat(id)); } this.PauseLastTimer(); this.timers.Add(new TimeMeasureItem(id, DateTime.Now)); if (isGroup) { this.BeginGroup(); } }
/// <summary>Ends an already started timer.</summary> /// <param name="isGroup">if it is a group.</param> /// <param name="idParts">The id parts.</param> public void End(bool isGroup, params string[] idParts) { if (isGroup) { this.EndGroup(); } var id = WebGreaseContext.ToStringId(idParts); var lastTimer = this.timers.Last(); if (lastTimer.Id != id) { throw new BuildWorkflowException("Trying to end a timer that was not started."); } this.StopTimer(lastTimer); this.ResumeLastTimer(); }
/// <summary> /// Executes the action for the cached section /// the boolean/success result of the action will determine if the action will be stored in cache. /// </summary> /// <param name="cachableSectionAction">The section action.</param> /// <returns>If all was successfull (being passed from within the actions).</returns> public bool Execute(Func <ICacheSection, bool> cachableSectionAction) { var id = WebGreaseContext.ToStringId(this.idParts); var webGreaseSectionKey = new WebGreaseSectionKey(this.context, id, this.cacheVarByContentItem, this.cacheVarBySetting, this.cacheVarByFileSet); var sectionLock = SectionLocks.GetOrAdd(webGreaseSectionKey.Value, new object()); return(Safe.Lock( sectionLock, this.cacheInfiniteWaitForLock ? Safe.MaxLockTimeout : Safe.DefaultLockTimeout, () => { var errorHasOccurred = false; EventHandler logOnErrorOccurred = delegate { errorHasOccurred = true; }; this.context.Log.ErrorOccurred += logOnErrorOccurred; var cacheSection = this.context.Cache.BeginSection(webGreaseSectionKey); try { if (this.context.TemporaryIgnore(this.cacheVarByFileSet, this.cacheVarByContentItem) && !errorHasOccurred) { cacheSection.Save(); return true; } cacheSection.Load(); if (this.cacheIsSkipable && cacheSection.CanBeSkipped()) { if (this.whenSkippedAction != null) { this.whenSkippedAction(cacheSection); } if (!errorHasOccurred) { return true; } } if (this.restoreFromCacheAction != null && cacheSection.CanBeRestoredFromCache()) { if (this.restoreFromCacheAction(cacheSection) && !errorHasOccurred) { return true; } } this.context.Measure.Start(this.isGroup, this.idParts); try { if (!cachableSectionAction(cacheSection) || errorHasOccurred) { return false; } cacheSection.Save(); return true; } finally { this.context.Measure.End(this.isGroup, this.idParts); } } finally { this.context.Log.ErrorOccurred -= logOnErrorOccurred; cacheSection.EndSection(); } })); }