diff options
Diffstat (limited to 'Mono.Addins/Mono.Addins.Database/AddinDatabase.cs')
-rw-r--r-- | Mono.Addins/Mono.Addins.Database/AddinDatabase.cs | 530 |
1 files changed, 290 insertions, 240 deletions
diff --git a/Mono.Addins/Mono.Addins.Database/AddinDatabase.cs b/Mono.Addins/Mono.Addins.Database/AddinDatabase.cs index 34f931f..842c12b 100644 --- a/Mono.Addins/Mono.Addins.Database/AddinDatabase.cs +++ b/Mono.Addins/Mono.Addins.Database/AddinDatabase.cs @@ -28,40 +28,43 @@ using System; -using System.Threading; using System.Collections; -using System.Collections.Specialized; -using System.IO; -using System.Xml; -using System.Reflection; -using Mono.Addins.Description; using System.Collections.Generic; +using System.Collections.Immutable; +using System.IO; using System.Linq; -using Mono.Addins.Serialization; +using System.Threading; +using Mono.Addins.Description; namespace Mono.Addins.Database { - class AddinDatabase + class AddinDatabase { public const string GlobalDomain = "global"; public const string UnknownDomain = "unknown"; public const string VersionTag = "004"; - List<Addin> allSetupInfos; - List<Addin> addinSetupInfos; - List<Addin> rootSetupInfos; + readonly AddinEngine addinEngine; + readonly AddinRegistry registry; + readonly string addinDbDir; + readonly FileDatabase fileDatabase; + readonly object localLock = new object (); + + bool allSetupInfosLoaded; + ImmutableArray<Addin> allSetupInfos; + ImmutableArray<Addin> addinSetupInfos; + ImmutableArray<Addin> rootSetupInfos; + + Dictionary<string, Addin> cachedAddinSetupInfos = new Dictionary<string, Addin> (); + ImmutableAddinHostIndex hostIndex; + internal static bool RunningSetupProcess; + bool fatalDatabseError; - Hashtable cachedAddinSetupInfos = new Hashtable (); - AddinHostIndex hostIndex; - FileDatabase fileDatabase; - string addinDbDir; DatabaseConfiguration config = null; - AddinRegistry registry; int lastDomainId; - AddinEngine addinEngine; - AddinFileSystemExtension fs = new AddinFileSystemExtension (); + AddinFileSystemExtension fileSystemExtension = new AddinFileSystemExtension (); List<object> extensions = new List<object> (); public AddinDatabase (AddinEngine addinEngine, AddinRegistry registry) @@ -71,13 +74,20 @@ namespace Mono.Addins.Database addinDbDir = Path.Combine (registry.AddinCachePath, "addin-db-" + VersionTag); fileDatabase = new FileDatabase (AddinDbDir); } - + + public AddinDatabaseTransaction BeginTransaction (AddinEngineTransaction addinEngineTransaction = null) + { + return new AddinDatabaseTransaction (this, localLock, addinEngineTransaction); + } + + internal AddinEngine AddinEngine => addinEngine; + string AddinDbDir { get { return addinDbDir; } } public AddinFileSystemExtension FileSystem { - get { return fs; } + get { return fileSystemExtension; } } public string AddinCachePath { @@ -116,36 +126,34 @@ namespace Mono.Addins.Database } } - public void Clear () - { - if (Directory.Exists (AddinCachePath)) - Directory.Delete (AddinCachePath, true); - if (Directory.Exists (AddinFolderCachePath)) - Directory.Delete (AddinFolderCachePath, true); - } - public void CopyExtensions (AddinDatabase other) { - foreach (object o in other.extensions) - RegisterExtension (o); + lock (extensions) { + foreach (object o in other.extensions) + RegisterExtension (o); + } } public void RegisterExtension (object extension) { - extensions.Add (extension); - if (extension is AddinFileSystemExtension) - fs = (AddinFileSystemExtension) extension; - else - throw new NotSupportedException (); + lock (extensions) { + extensions.Add (extension); + if (extension is AddinFileSystemExtension) + fileSystemExtension = (AddinFileSystemExtension)extension; + else + throw new NotSupportedException (); + } } public void UnregisterExtension (object extension) { - extensions.Remove (extension); - if ((extension as AddinFileSystemExtension) == fs) - fs = new AddinFileSystemExtension (); - else - throw new InvalidOperationException (); + lock (extensions) { + extensions.Remove (extension); + if ((extension as AddinFileSystemExtension) == fileSystemExtension) + fileSystemExtension = new AddinFileSystemExtension (); + else + throw new InvalidOperationException (); + } } public ExtensionNodeSet FindNodeSet (string domain, string addinId, string id) @@ -190,98 +198,69 @@ namespace Mono.Addins.Database // Get the cached list if the add-in list has already been loaded. // The domain doesn't have to be checked again, since it is always the same - IEnumerable<Addin> result = null; - - if ((flags & AddinSearchFlagsInternal.IncludeAll) == AddinSearchFlagsInternal.IncludeAll) { - if (allSetupInfos != null) - result = allSetupInfos; - } - else if ((flags & AddinSearchFlagsInternal.IncludeAddins) == AddinSearchFlagsInternal.IncludeAddins) { - if (addinSetupInfos != null) - result = addinSetupInfos; - } - else { - if (rootSetupInfos != null) - result = rootSetupInfos; - } - - if (result == null) { - InternalCheck (domain); - using (fileDatabase.LockRead ()) { - result = InternalGetInstalledAddins (domain, null, flags & ~AddinSearchFlagsInternal.LatestVersionsOnly); - } - } - - if ((flags & AddinSearchFlagsInternal.LatestVersionsOnly) == AddinSearchFlagsInternal.LatestVersionsOnly) - result = result.Where (a => a.IsLatestVersion); - - if ((flags & AddinSearchFlagsInternal.ExcludePendingUninstall) == AddinSearchFlagsInternal.ExcludePendingUninstall) - result = result.Where (a => !IsRegisteredForUninstall (a.Description.Domain, a.Id)); - - return result; + return InternalGetInstalledAddins (domain, null, flags & ~AddinSearchFlagsInternal.LatestVersionsOnly, false); } - IEnumerable<Addin> InternalGetInstalledAddins (string domain, AddinSearchFlagsInternal type) + IEnumerable<Addin> InternalGetInstalledAddins (string domain, AddinSearchFlagsInternal type, bool dbIsLockedForRead) { - return InternalGetInstalledAddins (domain, null, type); + return InternalGetInstalledAddins (domain, null, type, dbIsLockedForRead); } - IEnumerable<Addin> InternalGetInstalledAddins (string domain, string idFilter, AddinSearchFlagsInternal type) + IEnumerable<Addin> InternalGetInstalledAddins (string domain, string idFilter, AddinSearchFlagsInternal type, bool dbIsLockedForRead) { - if ((type & AddinSearchFlagsInternal.LatestVersionsOnly) != 0) - throw new InvalidOperationException ("LatestVersionsOnly flag not supported here"); - - if (allSetupInfos == null) { - Dictionary<string,Addin> adict = new Dictionary<string, Addin> (); + if (!allSetupInfosLoaded) { + lock (localLock) { + if (!allSetupInfosLoaded) { + Dictionary<string, Addin> adict = new Dictionary<string, Addin> (); - // Global add-ins are valid for any private domain - if (domain != AddinDatabase.GlobalDomain) - FindInstalledAddins (adict, AddinDatabase.GlobalDomain, idFilter); + using (!dbIsLockedForRead ? fileDatabase.LockRead() : null) { + // Global add-ins are valid for any private domain + if (domain != AddinDatabase.GlobalDomain) + FindInstalledAddins (adict, AddinDatabase.GlobalDomain); - FindInstalledAddins (adict, domain, idFilter); - List<Addin> alist = new List<Addin> (adict.Values); - UpdateLastVersionFlags (alist); - if (idFilter != null) - return alist; - allSetupInfos = alist; - } - if ((type & AddinSearchFlagsInternal.IncludeAll) == AddinSearchFlagsInternal.IncludeAll) - return FilterById (allSetupInfos, idFilter); - - if ((type & AddinSearchFlagsInternal.IncludeAddins) == AddinSearchFlagsInternal.IncludeAddins) { - if (addinSetupInfos == null) { - addinSetupInfos = new List<Addin> (); - foreach (Addin adn in allSetupInfos) - if (!adn.Description.IsRoot) - addinSetupInfos.Add (adn); + FindInstalledAddins (adict, domain); + } + List<Addin> alist = new List<Addin> (adict.Values); + UpdateLastVersionFlags (alist); + allSetupInfos = alist.ToImmutableArray (); + addinSetupInfos = alist.Where (addin => !addin.Description.IsRoot).ToImmutableArray (); + rootSetupInfos = alist.Where (addin => addin.Description.IsRoot).ToImmutableArray (); + allSetupInfosLoaded = true; + } } - return FilterById (addinSetupInfos, idFilter); } - else { - if (rootSetupInfos == null) { - rootSetupInfos = new List<Addin> (); - foreach (Addin adn in allSetupInfos) - if (adn.Description.IsRoot) - rootSetupInfos.Add (adn); - } - return FilterById (rootSetupInfos, idFilter); + IEnumerable<Addin> result; + + if ((type & AddinSearchFlagsInternal.IncludeAll) == AddinSearchFlagsInternal.IncludeAll) { + result = allSetupInfos; + } else if ((type & AddinSearchFlagsInternal.IncludeAddins) == AddinSearchFlagsInternal.IncludeAddins) { + result = addinSetupInfos; + } else { + result = rootSetupInfos; } + + result = FilterById (result, idFilter); + + if ((type & AddinSearchFlagsInternal.LatestVersionsOnly) == AddinSearchFlagsInternal.LatestVersionsOnly) + result = result.Where (a => a.IsLatestVersion); + + if ((type & AddinSearchFlagsInternal.ExcludePendingUninstall) == AddinSearchFlagsInternal.ExcludePendingUninstall) + result = result.Where (a => !IsRegisteredForUninstall (a.Description.Domain, a.Id)); + return result; } - - IEnumerable<Addin> FilterById (List<Addin> addins, string id) + + IEnumerable<Addin> FilterById (IEnumerable<Addin> addins, string id) { if (id == null) return addins; return addins.Where (a => Addin.GetIdName (a.Id) == id); } - void FindInstalledAddins (Dictionary<string,Addin> result, string domain, string idFilter) + void FindInstalledAddins (Dictionary<string,Addin> result, string domain) { - if (idFilter == null) - idFilter = "*"; string dir = Path.Combine (AddinCachePath, domain); if (Directory.Exists (dir)) { - foreach (string file in fileDatabase.GetDirectoryFiles (dir, idFilter + ",*.maddin")) { + foreach (string file in fileDatabase.GetDirectoryFiles (dir, "*,*.maddin")) { string id = Path.GetFileNameWithoutExtension (file); if (!result.ContainsKey (id)) { var adesc = GetInstalledDomainAddin (domain, id, true, false, false); @@ -330,23 +309,26 @@ namespace Mono.Addins.Database else return null; } - + Addin GetInstalledDomainAddin (string domain, string id, bool exactVersionMatch, bool enabledOnly, bool dbLockCheck) { - Addin sinfo = null; string idd = id + " " + domain; - object ob = cachedAddinSetupInfos [idd]; - if (ob != null) { - sinfo = ob as Addin; + Addin sinfo; + bool found; + lock (cachedAddinSetupInfos) { + found = cachedAddinSetupInfos.TryGetValue (idd, out sinfo); + } + + if (found) { if (sinfo != null) { if (!enabledOnly || sinfo.Enabled) return sinfo; if (exactVersionMatch) return null; - } - else if (enabledOnly) + } else if (enabledOnly) { // Ignore the 'not installed' flag when disabled add-ins are allowed return null; + } } if (dbLockCheck) @@ -376,13 +358,15 @@ namespace Mono.Addins.Database } if (foundDomain != null) { sinfo = new Addin (this, foundDomain, id); - cachedAddinSetupInfos [idd] = sinfo; - if (!enabledOnly || sinfo.Enabled) - return sinfo; - if (exactVersionMatch) { - // Cache lookups with negative result - cachedAddinSetupInfos [idd] = this; - return null; + lock (cachedAddinSetupInfos) { + cachedAddinSetupInfos [idd] = sinfo; + if (!enabledOnly || sinfo.Enabled) + return sinfo; + if (exactVersionMatch) { + // Cache lookups with negative result + cachedAddinSetupInfos [idd] = null; + return null; + } } } } @@ -393,7 +377,7 @@ namespace Mono.Addins.Database string bestVersion = null; Addin.GetIdParts (id, out name, out version); - foreach (Addin ia in InternalGetInstalledAddins (domain, name, AddinSearchFlagsInternal.IncludeAll)) + foreach (Addin ia in InternalGetInstalledAddins (domain, name, AddinSearchFlagsInternal.IncludeAll, true)) { if ((!enabledOnly || ia.Enabled) && (version.Length == 0 || ia.SupportsVersion (version)) && @@ -404,15 +388,20 @@ namespace Mono.Addins.Database } } if (sinfo != null) { - cachedAddinSetupInfos [idd] = sinfo; + lock (cachedAddinSetupInfos) { + cachedAddinSetupInfos [idd] = sinfo; + } return sinfo; } } - + // Cache lookups with negative result // Ignore the 'not installed' flag when disabled add-ins are allowed - if (enabledOnly) - cachedAddinSetupInfos [idd] = this; + if (enabledOnly) { + lock (cachedAddinSetupInfos) { + cachedAddinSetupInfos [idd] = null; + } + } return null; } } @@ -426,20 +415,22 @@ namespace Mono.Addins.Database { InternalCheck (domain); Addin ainfo = null; - - object ob = cachedAddinSetupInfos [assemblyLocation]; - if (ob != null) - return ob as Addin; // Don't use a cast here is ob may not be an Addin. - AddinHostIndex index = GetAddinHostIndex (); + lock (cachedAddinSetupInfos) { + if (cachedAddinSetupInfos.TryGetValue (assemblyLocation, out var ob)) + return ob; // this can be null, if the add-in is disabled + } + + var index = GetAddinHostIndex (); string addin, addinFile, rdomain; if (index.GetAddinForAssembly (assemblyLocation, out addin, out addinFile, out rdomain)) { string sid = addin + " " + rdomain; - ainfo = cachedAddinSetupInfos [sid] as Addin; - if (ainfo == null) - ainfo = new Addin (this, rdomain, addin); - cachedAddinSetupInfos [assemblyLocation] = ainfo; - cachedAddinSetupInfos [addin + " " + rdomain] = ainfo; + lock (cachedAddinSetupInfos) { + if (!cachedAddinSetupInfos.TryGetValue(sid, out ainfo)) + ainfo = new Addin (this, rdomain, addin); + cachedAddinSetupInfos [assemblyLocation] = ainfo; + cachedAddinSetupInfos [sid] = ainfo; + } } return ainfo; @@ -469,8 +460,14 @@ namespace Mono.Addins.Database { EnableAddin (domain, id, true); } - - internal void EnableAddin (string domain, string id, bool exactVersionMatch) + + public void EnableAddin (string domain, string id, bool exactVersionMatch) + { + using var transaction = BeginTransaction (); + EnableAddin (transaction, domain, id, exactVersionMatch); + } + + void EnableAddin (AddinDatabaseTransaction dbTransaction, string domain, string id, bool exactVersionMatch) { Addin ainfo = GetInstalledAddin (domain, id, exactVersionMatch, false); if (ainfo == null) @@ -486,19 +483,26 @@ namespace Mono.Addins.Database if (dep is AddinDependency) { AddinDependency adep = dep as AddinDependency; string adepid = Addin.GetFullId (ainfo.AddinInfo.Namespace, adep.AddinId, adep.Version); - EnableAddin (domain, adepid, false); + EnableAddin (dbTransaction, domain, adepid, false); } } - Configuration.SetEnabled (id, true, ainfo.AddinInfo.EnabledByDefault, true); - SaveConfiguration (); + Configuration.SetEnabled (dbTransaction, id, true, ainfo.AddinInfo.EnabledByDefault, true); + SaveConfiguration (dbTransaction); - if (addinEngine != null && addinEngine.IsInitialized) - addinEngine.ActivateAddin (id); + if (addinEngine != null && addinEngine.IsInitialized) { + addinEngine.ActivateAddin (dbTransaction.GetAddinEngineTransaction(), id); + } } - + public void DisableAddin (string domain, string id, bool exactVersionMatch = false, bool onlyForCurrentSession = false) { + using var transaction = BeginTransaction (); + DisableAddin (transaction, domain, id, exactVersionMatch, onlyForCurrentSession); + } + + void DisableAddin (AddinDatabaseTransaction dbTransaction, string domain, string id, bool exactVersionMatch = false, bool onlyForCurrentSession = false) + { Addin ai = GetInstalledAddin (domain, id, true); if (ai == null) throw new InvalidOperationException ("Add-in '" + id + "' not installed."); @@ -506,8 +510,8 @@ namespace Mono.Addins.Database if (!IsAddinEnabled (domain, id, exactVersionMatch)) return; - Configuration.SetEnabled (id, false, ai.AddinInfo.EnabledByDefault, exactVersionMatch, onlyForCurrentSession); - SaveConfiguration (); + Configuration.SetEnabled (dbTransaction, id, false, ai.AddinInfo.EnabledByDefault, exactVersionMatch, onlyForCurrentSession); + SaveConfiguration (dbTransaction); // Disable all add-ins which depend on it @@ -531,7 +535,7 @@ namespace Mono.Addins.Database Addin adepinfo = GetInstalledAddin (domain, adepid, false, true); if (adepinfo == null) { - DisableAddin (domain, ainfo.Id, onlyForCurrentSession: onlyForCurrentSession); + DisableAddin (dbTransaction, domain, ainfo.Id, onlyForCurrentSession: onlyForCurrentSession); break; } } @@ -539,25 +543,26 @@ namespace Mono.Addins.Database } catch { // If something goes wrong, enable the add-in again - Configuration.SetEnabled (id, true, ai.AddinInfo.EnabledByDefault, false, onlyForCurrentSession); - SaveConfiguration (); + Configuration.SetEnabled (dbTransaction, id, true, ai.AddinInfo.EnabledByDefault, false, onlyForCurrentSession); + SaveConfiguration (dbTransaction); throw; } - if (addinEngine != null && addinEngine.IsInitialized) - addinEngine.UnloadAddin (id); + if (addinEngine != null && addinEngine.IsInitialized) { + addinEngine.UnloadAddin (dbTransaction.GetAddinEngineTransaction(), id); + } } - public void UpdateEnabledStatus () + void UpdateEnabledStatus (AddinDatabaseTransaction transaction) { // Ensure that all enabled addins that have dependencies also have their dependencies enabled. HashSet<Addin> updatedAddins = new HashSet<Addin> (); var allAddins = GetInstalledAddins (registry.CurrentDomain, AddinSearchFlagsInternal.IncludeAddins | AddinSearchFlagsInternal.LatestVersionsOnly).ToList (); foreach (Addin addin in allAddins) - UpdateEnabledStatus (registry.CurrentDomain, addin, allAddins, updatedAddins); + UpdateEnabledStatus (transaction, registry.CurrentDomain, addin, allAddins, updatedAddins); } - void UpdateEnabledStatus (string domain, Addin addin, List<Addin> allAddins, HashSet<Addin> updatedAddins) + void UpdateEnabledStatus (AddinDatabaseTransaction transaction, string domain, Addin addin, List<Addin> allAddins, HashSet<Addin> updatedAddins) { if (!updatedAddins.Add (addin)) return; @@ -575,12 +580,12 @@ namespace Mono.Addins.Database string adepid = Addin.GetFullId (addin.AddinInfo.Namespace, adep.AddinId, null); var dependency = allAddins.FirstOrDefault (a => Addin.GetFullId (a.Namespace, a.LocalId, null) == adepid); if (dependency != null) { - UpdateEnabledStatus (domain, dependency, allAddins, updatedAddins); + UpdateEnabledStatus (transaction, domain, dependency, allAddins, updatedAddins); if (!dependency.Enabled) { // One of the dependencies is disabled, so this add-in also needs to be disabled. // However, we disabled only for the current configuration, we don't want to change // what the user configured. - DisableAddin (domain, addin.Id, onlyForCurrentSession: true); + DisableAddin (transaction, domain, addin.Id, onlyForCurrentSession: true); return; } } @@ -589,9 +594,10 @@ namespace Mono.Addins.Database public void RegisterForUninstall (string domain, string id, IEnumerable<string> files) { - DisableAddin (domain, id, true); - Configuration.RegisterForUninstall (id, files); - SaveConfiguration (); + using var transaction = BeginTransaction (); + DisableAddin (transaction, domain, id, true); + Configuration.RegisterForUninstall (transaction, id, files); + SaveConfiguration (transaction); } public bool IsRegisteredForUninstall (string domain, string addinId) @@ -630,7 +636,8 @@ namespace Mono.Addins.Database AddinUpdateData updateData = new AddinUpdateData (this, monitor); // Clear cached data - cachedAddinSetupInfos.Clear (); + lock(cachedAddinSetupInfos) + cachedAddinSetupInfos.Clear (); // Collect all information @@ -703,7 +710,7 @@ namespace Mono.Addins.Database } // If the original file does not exist, the description can be deleted - if (!fs.FileExists (conf.AddinFile)) { + if (!fileSystemExtension.FileExists (conf.AddinFile)) { SafeDelete (monitor, file); continue; } @@ -1012,19 +1019,19 @@ namespace Mono.Addins.Database internal void ResetBasicCachedData () { - allSetupInfos = null; - addinSetupInfos = null; - rootSetupInfos = null; + lock(localLock) + allSetupInfosLoaded = false; } - internal void ResetCachedData () + internal void ResetCachedData (AddinDatabaseTransaction dbTransaction = null) { ResetBasicCachedData (); hostIndex = null; - cachedAddinSetupInfos.Clear (); + lock(cachedAddinSetupInfos) + cachedAddinSetupInfos.Clear (); dependsOnCache.Clear (); if (addinEngine != null) - addinEngine.ResetCachedData (); + addinEngine.ResetCachedData (dbTransaction?.GetAddinEngineTransaction()); } Dictionary<string, HashSet<string>> dependsOnCache = new Dictionary<string, HashSet<string>> (); @@ -1088,79 +1095,87 @@ namespace Mono.Addins.Database Update (monitor, domain, context); } - - public void Update (IProgressStatus monitor, string domain, ScanOptions context = null) + + public void Update(IProgressStatus monitor, string domain, ScanOptions context = null, AddinEngineTransaction addinEngineTransaction = null) { if (monitor == null) - monitor = new ConsoleProgressStatus (false); + monitor = new ConsoleProgressStatus(false); if (RunningSetupProcess) return; - + fatalDatabseError = false; - + DateTime tim = DateTime.Now; - - RunPendingUninstalls (monitor); - - Hashtable installed = new Hashtable (); - bool changesFound = CheckFolders (monitor, domain); - + + using var dbTransaction = BeginTransaction(addinEngineTransaction); + + RunPendingUninstalls(dbTransaction, monitor); + + Hashtable installed = new Hashtable(); + bool changesFound = CheckFolders(monitor, domain); + if (monitor.IsCanceled) return; - + if (monitor.LogLevel > 1) - monitor.Log ("Folders checked (" + (int) (DateTime.Now - tim).TotalMilliseconds + " ms)"); - - if (changesFound) { + monitor.Log("Folders checked (" + (int)(DateTime.Now - tim).TotalMilliseconds + " ms)"); + + if (changesFound) + { // Something has changed, the add-ins need to be re-scanned, but it has // to be done in an external process - - if (domain != null) { - using (fileDatabase.LockRead ()) { - foreach (Addin ainfo in InternalGetInstalledAddins (domain, AddinSearchFlagsInternal.IncludeAddins)) { - installed [ainfo.Id] = ainfo.Id; - } + + if (domain != null) + { + foreach (Addin ainfo in InternalGetInstalledAddins(domain, AddinSearchFlagsInternal.IncludeAddins, false)) + { + installed[ainfo.Id] = ainfo.Id; } } - - RunScannerProcess (monitor, context); - - ResetCachedData (); - - registry.NotifyDatabaseUpdated (); + + RunScannerProcess(monitor, context); + + ResetCachedData(dbTransaction); + + registry.NotifyDatabaseUpdated(); } - + if (fatalDatabseError) - monitor.ReportError ("The add-in database could not be updated. It may be due to file corruption. Try running the setup repair utility", null); - + monitor.ReportError("The add-in database could not be updated. It may be due to file corruption. Try running the setup repair utility", null); + // Update the currently loaded add-ins - if (changesFound && domain != null && addinEngine != null && addinEngine.IsInitialized) { - Hashtable newInstalled = new Hashtable (); - foreach (Addin ainfo in GetInstalledAddins (domain, AddinSearchFlagsInternal.IncludeAddins)) { - newInstalled [ainfo.Id] = ainfo.Id; + if (changesFound && domain != null && addinEngine != null && addinEngine.IsInitialized) + { + Hashtable newInstalled = new Hashtable(); + foreach (Addin ainfo in GetInstalledAddins(domain, AddinSearchFlagsInternal.IncludeAddins)) + { + newInstalled[ainfo.Id] = ainfo.Id; } - - foreach (string aid in installed.Keys) { + + foreach (string aid in installed.Keys) + { // Always try to unload, event if the add-in was not currently loaded. // Required since the add-ins has to be marked as 'disabled', to avoid // extensions from this add-in to be loaded - if (!newInstalled.Contains (aid)) - addinEngine.UnloadAddin (aid); + if (!newInstalled.Contains(aid)) + addinEngine.UnloadAddin(dbTransaction.GetAddinEngineTransaction(), aid); } - - foreach (string aid in newInstalled.Keys) { - if (!installed.Contains (aid)) { - Addin addin = addinEngine.Registry.GetAddin (aid); + + foreach (string aid in newInstalled.Keys) + { + if (!installed.Contains(aid)) + { + Addin addin = addinEngine.Registry.GetAddin(aid); if (addin != null) - addinEngine.ActivateAddin (aid); + addinEngine.ActivateAddin(dbTransaction.GetAddinEngineTransaction(), aid); } } } - UpdateEnabledStatus (); + UpdateEnabledStatus(dbTransaction); } - void RunPendingUninstalls (IProgressStatus monitor) + void RunPendingUninstalls (AddinDatabaseTransaction dbTransaction, IProgressStatus monitor) { bool changesDone = false; @@ -1198,12 +1213,12 @@ namespace Mono.Addins.Database } if (canUninstall) { - Configuration.UnregisterForUninstall (adn.AddinId); + Configuration.UnregisterForUninstall (dbTransaction, adn.AddinId); changesDone = true; } } if (changesDone) - SaveConfiguration (); + SaveConfiguration (dbTransaction); } void RunScannerProcess (IProgressStatus monitor, ScanOptions context) @@ -1214,8 +1229,8 @@ namespace Mono.Addins.Database IProgressStatus scanMonitor = monitor; context = context ?? new ScanOptions (); - if (fs.GetType () != typeof (AddinFileSystemExtension)) - context.FileSystemExtension = fs; + if (fileSystemExtension.GetType () != typeof (AddinFileSystemExtension)) + context.FileSystemExtension = fileSystemExtension; bool retry = false; do { @@ -1350,10 +1365,10 @@ namespace Mono.Addins.Database void InternalScanFolders (IProgressStatus monitor, AddinScanResult scanResult) { try { - fs.ScanStarted (); + fileSystemExtension.ScanStarted (); InternalScanFolders2 (monitor, scanResult); } finally { - fs.ScanFinished (); + fileSystemExtension.ScanFinished (); } } @@ -1366,7 +1381,7 @@ namespace Mono.Addins.Database return; try { - scanResult.HostIndex = GetAddinHostIndex (); + scanResult.HostIndex = new AddinHostIndex(GetAddinHostIndex ()); } catch (Exception ex) { if (scanResult.CheckOnly) { @@ -1385,7 +1400,7 @@ namespace Mono.Addins.Database AddinScanFolderInfo folderInfo; bool res = ReadFolderInfo (monitor, file, out folderInfo); bool validForDomain = scanResult.Domain == null || folderInfo.Domain == GlobalDomain || folderInfo.Domain == scanResult.Domain; - if (!res || (validForDomain && !fs.DirectoryExists (folderInfo.Folder))) { + if (!res || (validForDomain && !fileSystemExtension.DirectoryExists (folderInfo.Folder))) { if (res) { // Folder has been deleted. Remove the add-ins it had. updater.UpdateDeletedAddins (monitor, folderInfo); @@ -1444,7 +1459,7 @@ namespace Mono.Addins.Database if (monitor.LogLevel > 1) monitor.Log ("Folders scan completed (" + (int) (DateTime.Now - tim).TotalMilliseconds + " ms)"); - SaveAddinHostIndex (); + SaveAddinHostIndex (scanResult); ResetCachedData (); if (!scanResult.ChangesFound) { @@ -1472,7 +1487,9 @@ namespace Mono.Addins.Database if (monitor.LogLevel > 1) monitor.Log ("Add-in relations analyzed (" + (int) (DateTime.Now - tim).TotalMilliseconds + " ms)"); - SaveAddinHostIndex (); + SaveAddinHostIndex (scanResult); + + hostIndex = scanResult.HostIndex.ToImmutableAddinHostIndex (); } public void ParseAddin (IProgressStatus progressStatus, string domain, string file, string outFile, bool inProcess) @@ -1768,25 +1785,25 @@ namespace Mono.Addins.Database return false; } } - - AddinHostIndex GetAddinHostIndex () + + ImmutableAddinHostIndex GetAddinHostIndex () { if (hostIndex != null) return hostIndex; using (fileDatabase.LockRead ()) { if (fileDatabase.Exists (HostIndexFile)) - hostIndex = AddinHostIndex.Read (fileDatabase, HostIndexFile); + hostIndex = AddinHostIndex.ReadAsImmutable (fileDatabase, HostIndexFile); else - hostIndex = new AddinHostIndex (); + hostIndex = new ImmutableAddinHostIndex (); } return hostIndex; } - void SaveAddinHostIndex () + void SaveAddinHostIndex (AddinScanResult scanResult) { - if (hostIndex != null) - hostIndex.Write (fileDatabase, HostIndexFile); + if (scanResult.HostIndex != null) + scanResult.HostIndex.Write (fileDatabase, HostIndexFile); } internal string GetUniqueAddinId (string file, string oldId, string ns, string version) @@ -1844,18 +1861,20 @@ namespace Mono.Addins.Database DatabaseConfiguration Configuration { get { if (config == null) { - using (fileDatabase.LockRead ()) { - if (fileDatabase.Exists (ConfigFile)) - config = DatabaseConfiguration.Read (ConfigFile); - else - config = DatabaseConfiguration.ReadAppConfig (); + lock (localLock) { + using (fileDatabase.LockRead ()) { + if (fileDatabase.Exists (ConfigFile)) + config = DatabaseConfiguration.Read (ConfigFile); + else + config = DatabaseConfiguration.ReadAppConfig (); + } } } return config; } } - void SaveConfiguration () + void SaveConfiguration (AddinDatabaseTransaction dbTransaction) { if (config != null) { using (fileDatabase.LockWrite ()) { @@ -1996,7 +2015,38 @@ namespace Mono.Addins.Database list.Add (desc); } } - + + class AddinDatabaseTransaction : IDisposable + { + readonly AddinDatabase addinDatabase; + readonly object localLock; + AddinEngineTransaction addinEngineTransaction; + bool addinEngineTransactionStarted; + + public AddinDatabaseTransaction (AddinDatabase addinDatabase, object localLock, AddinEngineTransaction addinEngineTransaction) + { + this.addinDatabase = addinDatabase; + this.localLock = localLock; + this.addinEngineTransaction = addinEngineTransaction; + Monitor.Enter (localLock); + } + + public AddinEngineTransaction GetAddinEngineTransaction() + { + if (addinEngineTransaction != null) + return addinEngineTransaction; + addinEngineTransactionStarted = true; + return addinEngineTransaction = addinDatabase.AddinEngine.BeginEngineTransaction(); + } + + public void Dispose () + { + Monitor.Exit(localLock); + if (addinEngineTransactionStarted) + addinEngineTransaction.Dispose(); + } + } + // Keep in sync with AddinSearchFlags [Flags] enum AddinSearchFlagsInternal |