From b531735b0ca34680471eec81a11370837c802fc2 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 3 May 2013 13:46:35 -0400 Subject: [PATCH] changed ActualChildren to a ConcurrentDictionary to allow for easy add/removes --- .../Entities/CollectionFolder.cs | 4 +- MediaBrowser.Controller/Entities/Folder.cs | 90 ++++++++++++++----- .../Entities/IndexFolder.cs | 7 +- MediaBrowser.sln | 3 + 4 files changed, 77 insertions(+), 27 deletions(-) diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index 42c112752..67692273d 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -69,7 +69,7 @@ namespace MediaBrowser.Controller.Entities /// Our children are actually just references to the ones in the physical root... /// /// The actual children. - protected override ConcurrentBag ActualChildren + protected override ConcurrentDictionary ActualChildren { get { @@ -92,7 +92,7 @@ namespace MediaBrowser.Controller.Entities .Where(i => folderIds.Contains(i.Id)) .SelectMany(c => c.Children); - return new ConcurrentBag(ourChildren); + return new ConcurrentDictionary(ourChildren.ToDictionary(i => i.Id)); } } } diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index bd2fa742e..463e16425 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -1,6 +1,5 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Progress; -using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Persistence; @@ -84,6 +83,50 @@ namespace MediaBrowser.Controller.Entities return (userId + DisplayPreferencesId.ToString()).GetMD5(); } + /// + /// Adds the child. + /// + /// The item. + /// The cancellation token. + /// Task. + /// Unable to add + item.Name + public async Task AddChild(BaseItem item, CancellationToken cancellationToken) + { + if (!_children.TryAdd(item.Id, item)) + { + throw new InvalidOperationException("Unable to add " + item.Name); + } + + var newChildren = Children.ToList(); + + await LibraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false); + + await LibraryManager.SaveChildren(Id, newChildren, cancellationToken).ConfigureAwait(false); + } + + /// + /// Removes the child. + /// + /// The item. + /// The cancellation token. + /// Task. + /// Unable to remove + item.Name + public Task RemoveChild(BaseItem item, CancellationToken cancellationToken) + { + BaseItem removed; + + if (!_children.TryRemove(item.Id, out removed)) + { + throw new InvalidOperationException("Unable to remove " + item.Name); + } + + var newChildren = Children.ToList(); + + LibraryManager.ReportItemRemoved(item); + + return LibraryManager.SaveChildren(Id, newChildren, cancellationToken); + } + #region Indexing /// @@ -400,7 +443,7 @@ namespace MediaBrowser.Controller.Entities /// /// The children /// - private ConcurrentBag _children; + private ConcurrentDictionary _children; /// /// The _children initialized /// @@ -413,7 +456,7 @@ namespace MediaBrowser.Controller.Entities /// Gets or sets the actual children. /// /// The actual children. - protected virtual ConcurrentBag ActualChildren + protected virtual ConcurrentDictionary ActualChildren { get { @@ -436,11 +479,11 @@ namespace MediaBrowser.Controller.Entities /// /// The children. [IgnoreDataMember] - public ConcurrentBag Children + public IEnumerable Children { get { - return ActualChildren; + return ActualChildren.Values; } } @@ -476,10 +519,10 @@ namespace MediaBrowser.Controller.Entities /// We want this sychronous. /// /// ConcurrentBag{BaseItem}. - protected virtual ConcurrentBag LoadChildren() + protected virtual ConcurrentDictionary LoadChildren() { //just load our children from the repo - the library will be validated and maintained in other processes - return new ConcurrentBag(GetCachedChildren()); + return new ConcurrentDictionary(GetCachedChildren().ToDictionary(i => i.Id)); } /// @@ -565,7 +608,7 @@ namespace MediaBrowser.Controller.Entities progress.Report(5); //build a dictionary of the current children we have now by Id so we can compare quickly and easily - var currentChildren = ActualChildren.ToDictionary(i => i.Id); + var currentChildren = ActualChildren; //create a list for our validated children var validChildren = new ConcurrentBag>(); @@ -615,14 +658,15 @@ namespace MediaBrowser.Controller.Entities //that's all the new and changed ones - now see if there are any that are missing var itemsRemoved = currentChildren.Values.Except(newChildren).ToList(); - var childrenReplaced = false; - - if (itemsRemoved.Count > 0) + foreach (var item in itemsRemoved) { - ActualChildren = new ConcurrentBag(newChildren); - childrenReplaced = true; + BaseItem removed; - foreach (var item in itemsRemoved) + if (!_children.TryRemove(item.Id, out removed)) + { + Logger.Error("Failed to remove {0}", item.Name); + } + else { LibraryManager.ReportItemRemoved(item); } @@ -632,19 +676,21 @@ namespace MediaBrowser.Controller.Entities foreach (var item in newItems) { - Logger.Debug("** " + item.Name + " Added to library."); - - if (!childrenReplaced) - { - _children.Add(item); - } - if (saveTasks.Count > 50) { await Task.WhenAll(saveTasks).ConfigureAwait(false); saveTasks.Clear(); } + if (!_children.TryAdd(item.Id, item)) + { + Logger.Error("Failed to add {0}", item.Name); + } + else + { + Logger.Debug("** " + item.Name + " Added to library."); + } + saveTasks.Add(LibraryManager.CreateItem(item, CancellationToken.None)); } @@ -807,7 +853,7 @@ namespace MediaBrowser.Controller.Entities } // If indexed is false or the indexing function is null - return result ?? (ActualChildren.Where(c => c.IsVisible(user))); + return result ?? (Children.Where(c => c.IsVisible(user))); } /// diff --git a/MediaBrowser.Controller/Entities/IndexFolder.cs b/MediaBrowser.Controller/Entities/IndexFolder.cs index bbfb01fc6..165bab632 100644 --- a/MediaBrowser.Controller/Entities/IndexFolder.cs +++ b/MediaBrowser.Controller/Entities/IndexFolder.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Extensions; +using System; +using MediaBrowser.Common.Extensions; using MediaBrowser.Model.Entities; using System.Collections.Concurrent; using System.Collections.Generic; @@ -109,7 +110,7 @@ namespace MediaBrowser.Controller.Entities /// Override to return the children defined to us when we were created /// /// The actual children. - protected override ConcurrentBag LoadChildren() + protected override ConcurrentDictionary LoadChildren() { var originalChildSource = ChildSource.ToList(); @@ -134,7 +135,7 @@ namespace MediaBrowser.Controller.Entities // Now - since we built the index grouping from the bottom up - we now need to properly set Parents from the top down SetParents(this, kids.OfType()); - return new ConcurrentBag(kids); + return new ConcurrentDictionary(kids.ToDictionary(i => i.Id)); } /// diff --git a/MediaBrowser.sln b/MediaBrowser.sln index f9f5e9436..eb3251f74 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -173,4 +173,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection EndGlobal