Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono-addins.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLluis Sanchez <llsan@microsoft.com>2022-09-13 20:34:02 +0300
committerLluis Sanchez <llsan@microsoft.com>2022-09-13 20:38:42 +0300
commit377ecba0c4d6ce6c87c8b9fe748dcdbfaebd7ddd (patch)
tree076f169f9232422789ba6c4988d8b3ee1e3605f1
parent3cf96414944ee8c5d1da11f97195a5163d610397 (diff)
More thread safety
Store extension content data in a snapshot class, so that it is possible to swap it all at once when committing a transaction. Propagate transactions to more methods. Events and virtual methods are now guaranteed to be executed sequentially, and never concurrently. Add threading docs.
-rw-r--r--Mono.Addins/Mono.Addins.Database/AddinDatabase.cs136
-rw-r--r--Mono.Addins/Mono.Addins/AddinEngine.cs231
-rw-r--r--Mono.Addins/Mono.Addins/AddinManager.cs29
-rw-r--r--Mono.Addins/Mono.Addins/AddinRegistry.cs5
-rw-r--r--Mono.Addins/Mono.Addins/ConditionType.cs3
-rw-r--r--Mono.Addins/Mono.Addins/ExtensionContext.cs337
-rw-r--r--Mono.Addins/Mono.Addins/ExtensionContextTransaction.cs173
-rw-r--r--Mono.Addins/Mono.Addins/ExtensionNode.cs171
-rw-r--r--Mono.Addins/Mono.Addins/ExtensionTree.cs28
-rw-r--r--Mono.Addins/Mono.Addins/RuntimeAddin.cs5
-rw-r--r--Mono.Addins/Mono.Addins/TreeNode.cs40
-rw-r--r--Version.props2
12 files changed, 722 insertions, 438 deletions
diff --git a/Mono.Addins/Mono.Addins.Database/AddinDatabase.cs b/Mono.Addins/Mono.Addins.Database/AddinDatabase.cs
index 0043774..73cb55c 100644
--- a/Mono.Addins/Mono.Addins.Database/AddinDatabase.cs
+++ b/Mono.Addins/Mono.Addins.Database/AddinDatabase.cs
@@ -75,11 +75,13 @@ namespace Mono.Addins.Database
fileDatabase = new FileDatabase (AddinDbDir);
}
- public AddinDatabaseTransaction BeginTransaction ()
+ public AddinDatabaseTransaction BeginTransaction (ExtensionContextTransaction addinEngineTransaction = null)
{
- return new AddinDatabaseTransaction (this, localLock);
+ return new AddinDatabaseTransaction (this, localLock, addinEngineTransaction);
}
-
+
+ internal AddinEngine AddinEngine => addinEngine;
+
string AddinDbDir {
get { return addinDbDir; }
}
@@ -489,8 +491,7 @@ namespace Mono.Addins.Database
SaveConfiguration (dbTransaction);
if (addinEngine != null && addinEngine.IsInitialized) {
- using var transaction = addinEngine.BeginTransaction ();
- addinEngine.ActivateAddin (transaction, id);
+ addinEngine.ActivateAddin (dbTransaction.GetAddinEngineTransaction(), id);
}
}
@@ -548,8 +549,7 @@ namespace Mono.Addins.Database
}
if (addinEngine != null && addinEngine.IsInitialized) {
- using var transaction = addinEngine.BeginTransaction ();
- addinEngine.UnloadAddin (transaction, id);
+ addinEngine.UnloadAddin (dbTransaction.GetAddinEngineTransaction(), id);
}
}
@@ -1023,7 +1023,7 @@ namespace Mono.Addins.Database
allSetupInfosLoaded = false;
}
- internal void ResetCachedData ()
+ internal void ResetCachedData (AddinDatabaseTransaction dbTransaction = null)
{
ResetBasicCachedData ();
hostIndex = null;
@@ -1031,7 +1031,7 @@ namespace Mono.Addins.Database
cachedAddinSetupInfos.Clear ();
dependsOnCache.Clear ();
if (addinEngine != null)
- addinEngine.ResetCachedData ();
+ addinEngine.ResetCachedData (dbTransaction?.GetAddinEngineTransaction());
}
Dictionary<string, HashSet<string>> dependsOnCache = new Dictionary<string, HashSet<string>> ();
@@ -1095,79 +1095,84 @@ 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, ExtensionContextTransaction addinEngineTransaction = null)
{
if (monitor == null)
- monitor = new ConsoleProgressStatus (false);
+ monitor = new ConsoleProgressStatus(false);
if (RunningSetupProcess)
return;
-
+
fatalDatabseError = false;
-
+
DateTime tim = DateTime.Now;
- using (var dbTransaction = BeginTransaction ()) {
- RunPendingUninstalls (dbTransaction, monitor);
- }
-
- Hashtable installed = new Hashtable ();
- bool changesFound = CheckFolders (monitor, domain);
-
+ 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) {
- foreach (Addin ainfo in InternalGetInstalledAddins (domain, AddinSearchFlagsInternal.IncludeAddins, false)) {
- 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;
}
- using (var transaction = addinEngine.BeginTransaction ()) {
- 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 (transaction, aid);
- }
+ 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(dbTransaction.GetAddinEngineTransaction(), aid);
+ }
- foreach (string aid in newInstalled.Keys) {
- if (!installed.Contains (aid)) {
- Addin addin = addinEngine.Registry.GetAddin (aid);
- if (addin != null)
- addinEngine.ActivateAddin (transaction, aid);
- }
+ foreach (string aid in newInstalled.Keys)
+ {
+ if (!installed.Contains(aid))
+ {
+ Addin addin = addinEngine.Registry.GetAddin(aid);
+ if (addin != null)
+ addinEngine.ActivateAddin(dbTransaction.GetAddinEngineTransaction(), aid);
}
}
}
- using var dbTransation = BeginTransaction ();
- UpdateEnabledStatus (dbTransation);
+ UpdateEnabledStatus(dbTransaction);
}
void RunPendingUninstalls (AddinDatabaseTransaction dbTransaction, IProgressStatus monitor)
@@ -2015,16 +2020,29 @@ namespace Mono.Addins.Database
{
readonly AddinDatabase addinDatabase;
readonly object localLock;
+ ExtensionContextTransaction addinEngineTransaction;
+ bool addinEngineTransactionStarted;
- public AddinDatabaseTransaction (AddinDatabase addinDatabase, object localLock)
+ public AddinDatabaseTransaction (AddinDatabase addinDatabase, object localLock, ExtensionContextTransaction addinEngineTransaction)
{
this.addinDatabase = addinDatabase;
this.localLock = localLock;
+ this.addinEngineTransaction = addinEngineTransaction;
Monitor.Enter (localLock);
}
+ public ExtensionContextTransaction GetAddinEngineTransaction()
+ {
+ if (addinEngineTransaction != null)
+ return addinEngineTransaction;
+ addinEngineTransactionStarted = true;
+ return addinEngineTransaction = addinDatabase.AddinEngine.BeginTransaction();
+ }
+
public void Dispose ()
{
+ if (addinEngineTransactionStarted)
+ addinEngineTransaction.Dispose();
Monitor.Exit (localLock);
}
}
diff --git a/Mono.Addins/Mono.Addins/AddinEngine.cs b/Mono.Addins/Mono.Addins/AddinEngine.cs
index 1e2c39a..69a4112 100644
--- a/Mono.Addins/Mono.Addins/AddinEngine.cs
+++ b/Mono.Addins/Mono.Addins/AddinEngine.cs
@@ -37,7 +37,6 @@ using Mono.Addins.Description;
using Mono.Addins.Database;
using Mono.Addins.Localization;
using System.Collections.Generic;
-using System.Diagnostics;
using System.Collections.Immutable;
namespace Mono.Addins
@@ -61,10 +60,35 @@ namespace Mono.Addins
IAddinInstaller installer;
bool checkAssemblyLoadConflicts;
- ImmutableDictionary<string,RuntimeAddin> loadedAddins = ImmutableDictionary<string,RuntimeAddin>.Empty;
- ImmutableDictionary<string,ExtensionNodeSet> nodeSets = ImmutableDictionary<string, ExtensionNodeSet>.Empty;
- ImmutableDictionary<string,string> autoExtensionTypes = ImmutableDictionary<string, string>.Empty;
- ImmutableDictionary<string, RuntimeAddin> assemblyResolvePaths = ImmutableDictionary<string, RuntimeAddin>.Empty;
+
+ // This collection is only used during a transaction, so it doesn't need to be immutable
+ Dictionary<string, ExtensionNodeSet> nodeSets = new Dictionary<string, ExtensionNodeSet>();
+
+ AddinEngineSnapshot currentSnapshot = new AddinEngineSnapshot();
+
+ internal class AddinEngineSnapshot: ExtensionContextSnapshot
+ {
+ public ImmutableDictionary<string, RuntimeAddin> LoadedAddins;
+ public ImmutableDictionary<string, string> AutoExtensionTypes;
+ public ImmutableDictionary<string, RuntimeAddin> AssemblyResolvePaths;
+
+ public AddinEngineSnapshot()
+ {
+ LoadedAddins = ImmutableDictionary<string, RuntimeAddin>.Empty;
+ AutoExtensionTypes = ImmutableDictionary<string, string>.Empty;
+ AssemblyResolvePaths = ImmutableDictionary<string, RuntimeAddin>.Empty;
+ }
+
+ public override void CopyFrom(ExtensionContextSnapshot other)
+ {
+ base.CopyFrom(other);
+ var context = (AddinEngineSnapshot)other;
+ LoadedAddins = context.LoadedAddins;
+ AutoExtensionTypes = context.AutoExtensionTypes;
+ AssemblyResolvePaths = context.AssemblyResolvePaths;
+ }
+ }
+
AddinLocalizer defaultLocalizer;
IProgressStatus defaultProgressStatus = new ConsoleProgressStatus (false);
@@ -94,7 +118,18 @@ namespace Mono.Addins
public AddinEngine ()
{
}
-
+
+ internal override ExtensionContextSnapshot CreateSnapshot ()
+ {
+ return new AddinEngineSnapshot();
+ }
+
+ internal override void SetSnapshot (ExtensionContextSnapshot newSnapshot)
+ {
+ currentSnapshot = (AddinEngineSnapshot)newSnapshot;
+ base.SetSnapshot(newSnapshot);
+ }
+
/// <summary>
/// Initializes the add-in engine
/// </summary>
@@ -265,7 +300,7 @@ namespace Mono.Addins
{
string assemblyName = args.Name;
- if (assemblyResolvePaths.TryGetValue (assemblyName, out var inAddin)) {
+ if (currentSnapshot.AssemblyResolvePaths.TryGetValue (assemblyName, out var inAddin)) {
if (inAddin.TryGetAssembly (assemblyName, out var assembly))
return assembly;
@@ -291,7 +326,6 @@ namespace Mono.Addins
initialized = false;
AppDomain.CurrentDomain.AssemblyLoad -= new AssemblyLoadEventHandler (OnAssemblyLoaded);
AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomainAssemblyResolve;
- loadedAddins = ImmutableDictionary<string, RuntimeAddin>.Empty;
registry.Dispose ();
registry = null;
startupDirectory = null;
@@ -394,7 +428,7 @@ namespace Mono.Addins
{
ValidateAddinRoots ();
- assemblyResolvePaths.TryGetValue(asm.FullName, out var ad);
+ currentSnapshot.AssemblyResolvePaths.TryGetValue(asm.FullName, out var ad);
return ad;
}
@@ -459,19 +493,46 @@ namespace Mono.Addins
/// </returns>
public bool IsAddinLoaded (string id)
{
- CheckInitialized ();
- ValidateAddinRoots ();
- return loadedAddins.ContainsKey (Addin.GetIdName (id));
+ return IsAddinLoaded(currentSnapshot, id);
}
-
+
+ bool IsAddinLoaded(AddinEngineSnapshot snapshot, string id)
+ {
+ CheckInitialized();
+ ValidateAddinRoots();
+ return snapshot.LoadedAddins.ContainsKey(Addin.GetIdName(id));
+ }
+
internal RuntimeAddin GetAddin (string id)
{
ValidateAddinRoots ();
RuntimeAddin a;
- loadedAddins.TryGetValue (Addin.GetIdName (id), out a);
+ currentSnapshot.LoadedAddins.TryGetValue (Addin.GetIdName (id), out a);
return a;
}
-
+
+ internal RuntimeAddin GetAddin(ExtensionContextTransaction transaction, string id)
+ {
+ ValidateAddinRoots();
+ RuntimeAddin a;
+ transaction.AddinEngineSnapshot.LoadedAddins.TryGetValue(Addin.GetIdName(id), out a);
+ return a;
+ }
+
+ internal RuntimeAddin GetOrLoadAddin(string id, bool throwExceptions)
+ {
+ ValidateAddinRoots();
+ RuntimeAddin a;
+ currentSnapshot.LoadedAddins.TryGetValue(Addin.GetIdName(id), out a);
+ if (a == null)
+ {
+ using var tr = BeginTransaction();
+ LoadAddin(tr, null, id, throwExceptions);
+ tr.AddinEngineSnapshot.LoadedAddins.TryGetValue(Addin.GetIdName(id), out a);
+ }
+ return a;
+ }
+
internal void ActivateAddin (ExtensionContextTransaction transaction, string id)
{
ActivateAddinExtensions (transaction, id);
@@ -481,10 +542,10 @@ namespace Mono.Addins
{
RemoveAddinExtensions (transaction, id);
- RuntimeAddin addin = GetAddin (id);
+ RuntimeAddin addin = GetAddin (transaction, id);
if (addin != null) {
addin.UnloadExtensions (transaction);
- loadedAddins = loadedAddins.Remove (Addin.GetIdName (id));
+ transaction.AddinEngineSnapshot.LoadedAddins = transaction.AddinEngineSnapshot.LoadedAddins.Remove (Addin.GetIdName (id));
foreach (var asm in addin.Module.AssemblyNames) {
transaction.UnregisterAssemblyResolvePaths (asm);
}
@@ -492,11 +553,6 @@ namespace Mono.Addins
}
}
- internal void BulkUnregisterAssemblyResolvePaths (IEnumerable<string> assemblies)
- {
- assemblyResolvePaths = assemblyResolvePaths.RemoveRange (assemblies);
- }
-
/// <summary>
/// Forces the loading of an add-in.
/// </summary>
@@ -515,16 +571,17 @@ namespace Mono.Addins
public void LoadAddin (IProgressStatus statusMonitor, string id)
{
CheckInitialized ();
- if (LoadAddin (statusMonitor, id, true)) {
- var adn = GetAddin (id);
+ using var transaction = BeginTransaction();
+ if (LoadAddin (transaction, statusMonitor, id, true)) {
+ var adn = GetAddin (transaction, id);
adn.EnsureAssembliesLoaded ();
}
}
- internal bool LoadAddin (IProgressStatus statusMonitor, string id, bool throwExceptions)
+ internal bool LoadAddin (ExtensionContextTransaction transaction, IProgressStatus statusMonitor, string id, bool throwExceptions)
{
try {
- if (IsAddinLoaded (id))
+ if (IsAddinLoaded (transaction.AddinEngineSnapshot, id))
return true;
if (!Registry.IsAddinEnabled (id)) {
@@ -537,14 +594,13 @@ namespace Mono.Addins
var addins = new List<Addin> ();
Stack depCheck = new Stack ();
- ResolveLoadDependencies (addins, depCheck, id, false);
+ ResolveLoadDependencies (transaction, addins, depCheck, id, false);
addins.Reverse ();
if (statusMonitor != null)
statusMonitor.SetMessage ("Loading Addins");
if (addins.Count > 0) {
- ExtensionContextTransaction transaction = null;
try {
for (int n = 0; n < addins.Count; n++) {
@@ -552,15 +608,12 @@ namespace Mono.Addins
statusMonitor.SetProgress ((double)n / (double)addins.Count);
Addin iad = addins [n];
- if (IsAddinLoaded (iad.Id))
+ if (IsAddinLoaded (transaction.AddinEngineSnapshot, iad.Id))
continue;
if (statusMonitor != null)
statusMonitor.SetMessage (string.Format (GettextCatalog.GetString ("Loading {0} add-in"), iad.Id));
- if (transaction == null)
- transaction = BeginTransaction ();
-
if (!InsertAddin (transaction, statusMonitor, iad))
return false;
}
@@ -580,11 +633,11 @@ namespace Mono.Addins
}
}
- internal override void ResetCachedData ()
+ internal override void ResetCachedData (ExtensionContextTransaction transaction)
{
- foreach (RuntimeAddin ad in loadedAddins.Values)
+ foreach (RuntimeAddin ad in transaction.AddinEngineSnapshot.LoadedAddins.Values)
ad.Addin.ResetCachedData ();
- base.ResetCachedData ();
+ base.ResetCachedData (transaction);
}
bool InsertAddin (ExtensionContextTransaction transaction, IProgressStatus statusMonitor, Addin iad)
@@ -596,7 +649,7 @@ namespace Mono.Addins
RegisterAssemblyResolvePaths (transaction, runtimeAddin, iad.Description.MainModule);
// Register the add-in
- loadedAddins = loadedAddins.SetItem(Addin.GetIdName (runtimeAddin.Id), runtimeAddin);
+ transaction.AddinEngineSnapshot.LoadedAddins = transaction.AddinEngineSnapshot.LoadedAddins.SetItem(Addin.GetIdName (runtimeAddin.Id), runtimeAddin);
if (!AddinDatabase.RunningSetupProcess) {
// Load the extension points and other addin data
@@ -630,11 +683,6 @@ namespace Mono.Addins
}
}
- internal void BulkRegisterAssemblyResolvePaths (IEnumerable<KeyValuePair<string,RuntimeAddin>> registrations)
- {
- assemblyResolvePaths = assemblyResolvePaths.SetItems (registrations);
- }
-
internal void InsertExtensionPoint (ExtensionContextTransaction transaction, RuntimeAddin addin, ExtensionPoint ep)
{
CreateExtensionPoint (ep);
@@ -645,9 +693,9 @@ namespace Mono.Addins
}
}
- bool ResolveLoadDependencies (List<Addin> addins, Stack depCheck, string id, bool optional)
+ bool ResolveLoadDependencies (ExtensionContextTransaction transaction, List<Addin> addins, Stack depCheck, string id, bool optional)
{
- if (IsAddinLoaded (id))
+ if (IsAddinLoaded (transaction.AddinEngineSnapshot, id))
return true;
if (depCheck.Contains (id))
@@ -676,7 +724,7 @@ namespace Mono.Addins
if (adep != null) {
try {
string adepid = Addin.GetFullId (iad.AddinInfo.Namespace, adep.AddinId, adep.Version);
- ResolveLoadDependencies (addins, depCheck, adepid, false);
+ ResolveLoadDependencies (transaction, addins, depCheck, adepid, false);
} catch (MissingDependencyException) {
if (optional)
return false;
@@ -691,7 +739,7 @@ namespace Mono.Addins
AddinDependency adep = dep as AddinDependency;
if (adep != null) {
string adepid = Addin.GetFullId (iad.Namespace, adep.AddinId, adep.Version);
- if (!ResolveLoadDependencies (addins, depCheck, adepid, true))
+ if (!ResolveLoadDependencies (transaction, addins, depCheck, adepid, true))
return false;
}
}
@@ -704,27 +752,25 @@ namespace Mono.Addins
void RegisterNodeSets (ExtensionContextTransaction transaction, string addinId, ExtensionNodeSetCollection nsets)
{
- nodeSets = nodeSets.SetItems (nsets.Select (nset => {
+ foreach (ExtensionNodeSet nset in nsets)
+ {
nset.SourceAddinId = addinId;
- return new KeyValuePair<string, ExtensionNodeSet> (nset.Id, nset);
- }));
+ nodeSets[nset.Id] = nset;
+ }
}
internal void UnregisterAddinNodeSets (ExtensionContextTransaction transaction, string addinId)
{
- nodeSets = nodeSets.RemoveRange (nodeSets.Where(n => n.Value.SourceAddinId == addinId).Select(n => n.Key));
+ foreach (var nset in nodeSets.Values.Where(n => n.SourceAddinId == addinId).ToList())
+ nodeSets.Remove(nset.Id);
}
- internal string GetNodeTypeAddin (ExtensionNodeSet nset, string type, string callingAddinId)
+ internal ExtensionNodeType FindType (ExtensionContextTransaction transaction, ExtensionNodeSet nset, string name, string callingAddinId)
{
- ExtensionNodeType nt = FindType (nset, type, callingAddinId);
- if (nt != null)
- return nt.AddinId;
- else
- return null;
+ return FindType (nodeSets, nset, name, callingAddinId);
}
-
- internal ExtensionNodeType FindType (ExtensionNodeSet nset, string name, string callingAddinId)
+
+ ExtensionNodeType FindType (Dictionary<string, ExtensionNodeSet> sets, ExtensionNodeSet nset, string name, string callingAddinId)
{
if (nset == null)
return null;
@@ -736,11 +782,11 @@ namespace Mono.Addins
foreach (string ns in nset.NodeSets) {
ExtensionNodeSet regSet;
- if (!nodeSets.TryGetValue (ns, out regSet)) {
+ if (!sets.TryGetValue (ns, out regSet)) {
ReportError ("Unknown node set: " + ns, callingAddinId, null, false);
return null;
}
- ExtensionNodeType nt = FindType (regSet, name, callingAddinId);
+ ExtensionNodeType nt = FindType (sets, regSet, name, callingAddinId);
if (nt != null)
return nt;
}
@@ -754,11 +800,6 @@ namespace Mono.Addins
transaction.RegisterAutoTypeExtensionPoint (typeName, path);
}
- internal void BulkRegisterAutoTypeExtensionPoint (List<KeyValuePair<string, string>> autoExtensionPoints)
- {
- autoExtensionTypes = autoExtensionTypes.AddRange (autoExtensionPoints);
- }
-
internal void UnregisterAutoTypeExtensionPoint (ExtensionContextTransaction transaction, string typeName, string path)
{
if (Util.TryParseTypeName (typeName, out var t, out var _))
@@ -766,14 +807,12 @@ namespace Mono.Addins
transaction.UnregisterAutoTypeExtensionPoint (typeName);
}
- internal void BulkUnregisterAutoTypeExtensionPoint (List<string> autoExtensionPointTypes)
- {
- autoExtensionTypes = autoExtensionTypes.RemoveRange (autoExtensionPointTypes);
- }
-
+ /// <summary>
+ /// Returns an extension point identified by a type
+ /// </summary>
internal string GetAutoTypeExtensionPoint (Type type)
{
- autoExtensionTypes.TryGetValue (type.FullName, out var path);
+ currentSnapshot.AutoExtensionTypes.TryGetValue (type.FullName, out var path);
return path;
}
@@ -798,8 +837,9 @@ namespace Mono.Addins
}
}
if (copy != null) {
+ using var tr = BeginTransaction();
foreach (Assembly asm in copy)
- CheckHostAssembly (asm);
+ CheckHostAssembly (tr, asm);
}
}
@@ -807,11 +847,13 @@ namespace Mono.Addins
{
lock (pendingRootChecks)
pendingRootChecks.Clear ();
+
+ using var tr = BeginTransaction();
foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies ())
- CheckHostAssembly (asm);
+ CheckHostAssembly (tr, asm);
}
- void CheckHostAssembly (Assembly asm)
+ void CheckHostAssembly (ExtensionContextTransaction transaction, Assembly asm)
{
if (AddinDatabase.RunningSetupProcess || asm is System.Reflection.Emit.AssemblyBuilder || asm.IsDynamic)
return;
@@ -838,7 +880,7 @@ namespace Mono.Addins
ainfo = Registry.GetAddinForHostAssembly (asmFile);
}
- if (ainfo != null && !IsAddinLoaded (ainfo.Id)) {
+ if (ainfo != null && !IsAddinLoaded (transaction.AddinEngineSnapshot, ainfo.Id)) {
AddinDescription adesc = null;
try {
adesc = ainfo.Description;
@@ -849,12 +891,12 @@ namespace Mono.Addins
// If the add-in has changed, update the add-in database.
// We do it here because once loaded, add-in roots can't be
// reloaded like regular add-ins.
- Registry.Update (null);
+ Registry.Update (null, transaction);
ainfo = Registry.GetAddinForHostAssembly (asmFile);
if (ainfo == null)
return;
}
- LoadAddin (null, ainfo.Id, false);
+ LoadAddin (transaction, null, ainfo.Id, false);
}
}
@@ -894,39 +936,32 @@ namespace Mono.Addins
internal void ReportAddinLoad (RuntimeAddin addin)
{
NotifyAddinLoaded (addin);
- var handler = AddinLoaded;
- if (handler != null) {
- try {
- handler (null, new AddinEventArgs (addin.Id));
- } catch {
- // Ignore subscriber exceptions
- }
+ if (AddinLoaded != null) {
+ InvokeCallback(() =>
+ {
+ AddinLoaded?.Invoke(this, new AddinEventArgs(addin.Id));
+ },null);
}
}
internal void ReportAddinUnload (string id)
{
- var handler = AddinUnloaded;
- if (handler != null) {
- try {
- handler (null, new AddinEventArgs (id));
- } catch {
- // Ignore subscriber exceptions
- }
+ if (AddinUnloaded != null) {
+ InvokeCallback(() =>
+ {
+ AddinUnloaded?.Invoke(this, new AddinEventArgs(id));
+ },null);
}
}
internal void ReportAddinAssembliesLoad (string id)
{
- var handler = AddinAssembliesLoaded;
- if (handler != null) {
- try {
- handler (null, new AddinEventArgs (id));
- } catch {
- // Ignore subscriber exceptions
- }
+ if (AddinAssembliesLoaded != null) {
+ InvokeCallback(() =>
+ {
+ AddinAssembliesLoaded?.Invoke(this, new AddinEventArgs(id));
+ },null);
}
}
}
-
}
diff --git a/Mono.Addins/Mono.Addins/AddinManager.cs b/Mono.Addins/Mono.Addins/AddinManager.cs
index b5bef3f..823bccc 100644
--- a/Mono.Addins/Mono.Addins/AddinManager.cs
+++ b/Mono.Addins/Mono.Addins/AddinManager.cs
@@ -688,7 +688,7 @@ namespace Mono.Addins
AddinEngine.CheckInitialized ();
return AddinEngine.GetExtensionObjects<T> (path, reuseCachedInstance);
}
-
+
/// <summary>
/// Extension change event.
/// </summary>
@@ -698,6 +698,9 @@ namespace Mono.Addins
/// it does not provide information about what changed. Hosts subscribing to
/// this event should get the new list of nodes using a query method such as
/// AddinManager.GetExtensionNodes() and then update whatever needs to be updated.
+ ///
+ /// Threading information: the thread on which the event is raised is undefined.
+ /// Events in this class are guaranteed to be raised sequentially, never concurrently.
/// </remarks>
public static event ExtensionEventHandler ExtensionChanged {
add { AddinEngine.CheckInitialized(); AddinEngine.ExtensionChanged += value; }
@@ -720,6 +723,9 @@ namespace Mono.Addins
/// (Add or Remove) and the extension node added or removed.
///
/// NOTE: The handler will be called for all nodes existing in the path at the moment of registration.
+ ///
+ /// Threading information: the thread on which the handler is executed is undefined.
+ /// Handlers and events are guaranteed to be executed sequentially, never concurrently.
/// </remarks>
public static void AddExtensionNodeHandler (string path, ExtensionNodeEventHandler handler)
{
@@ -761,6 +767,9 @@ namespace Mono.Addins
/// (Add or Remove) and the extension node added or removed.
///
/// NOTE: The handler will be called for all nodes existing in the path at the moment of registration.
+ ///
+ /// Threading information: the thread on which the handler is executed is undefined.
+ /// Handlers and events are guaranteed to be executed sequentially, never concurrently.
/// </remarks>
public static void AddExtensionNodeHandler (Type instanceType, ExtensionNodeEventHandler handler)
{
@@ -782,35 +791,44 @@ namespace Mono.Addins
AddinEngine.CheckInitialized ();
AddinEngine.RemoveExtensionNodeHandler (instanceType, handler);
}
-
+
/// <summary>
/// Add-in loading error event.
/// </summary>
/// <remarks>
/// This event is fired when there is an error when loading the extension
/// of an add-in, or any other kind of error that may happen when querying extension points.
+ ///
+ /// Threading information: the thread on which the event is raised is undefined.
+ /// Events in this class are guaranteed to be raised sequentially, never concurrently.
/// </remarks>
public static event AddinErrorEventHandler AddinLoadError {
add { AddinEngine.AddinLoadError += value; }
remove { AddinEngine.AddinLoadError -= value; }
}
-
+
/// <summary>
/// Add-in loaded event.
/// </summary>
/// <remarks>
/// Fired after loading an add-in in memory.
+ ///
+ /// Threading information: the thread on which the event is raised is undefined.
+ /// Events in this class are guaranteed to be raised sequentially, never concurrently.
/// </remarks>
public static event AddinEventHandler AddinLoaded {
add { AddinEngine.AddinLoaded += value; }
remove { AddinEngine.AddinLoaded -= value; }
}
-
+
/// <summary>
/// Add-in unload event.
/// </summary>
/// <remarks>
/// Fired when an add-in is unloaded from memory. It may happen an add-in is disabled or uninstalled.
+ ///
+ /// Threading information: the thread on which the event is raised is undefined.
+ /// Events in this class are guaranteed to be raised sequentially, never concurrently.
/// </remarks>
public static event AddinEventHandler AddinUnloaded {
add { AddinEngine.AddinUnloaded += value; }
@@ -822,6 +840,9 @@ namespace Mono.Addins
/// </summary>
/// <remarks>
/// Fired when the add-in assemblies are loaded.
+ ///
+ /// Threading information: the thread on which the event is raised is undefined.
+ /// Events in this class are guaranteed to be raised sequentially, never concurrently.
/// </remarks>
public static event AddinEventHandler AddinAssembliesLoaded {
add { AddinEngine.AddinAssembliesLoaded += value; }
diff --git a/Mono.Addins/Mono.Addins/AddinRegistry.cs b/Mono.Addins/Mono.Addins/AddinRegistry.cs
index 94e8123..1346883 100644
--- a/Mono.Addins/Mono.Addins/AddinRegistry.cs
+++ b/Mono.Addins/Mono.Addins/AddinRegistry.cs
@@ -639,6 +639,11 @@ namespace Mono.Addins
database.Update (monitor, currentDomain);
}
+ internal void Update(IProgressStatus monitor, ExtensionContextTransaction addinEngineTransaction)
+ {
+ database.Update(monitor, currentDomain, addinEngineTransaction:addinEngineTransaction);
+ }
+
/// <summary>
/// Regenerates the cached data of the add-in registry.
/// </summary>
diff --git a/Mono.Addins/Mono.Addins/ConditionType.cs b/Mono.Addins/Mono.Addins/ConditionType.cs
index 4f7b0db..d5bf6f6 100644
--- a/Mono.Addins/Mono.Addins/ConditionType.cs
+++ b/Mono.Addins/Mono.Addins/ConditionType.cs
@@ -210,7 +210,8 @@ namespace Mono.Addins
if (!string.IsNullOrEmpty (addin)) {
// Make sure the add-in that implements the condition is loaded
- addinEngine.LoadAddin (null, addin, true);
+ using var tr = addinEngine.BeginTransaction();
+ addinEngine.LoadAddin (tr, null, addin, true);
addin = null; // Don't try again
}
diff --git a/Mono.Addins/Mono.Addins/ExtensionContext.cs b/Mono.Addins/Mono.Addins/ExtensionContext.cs
index 5e719d4..722b23f 100644
--- a/Mono.Addins/Mono.Addins/ExtensionContext.cs
+++ b/Mono.Addins/Mono.Addins/ExtensionContext.cs
@@ -37,29 +37,51 @@ using Mono.Addins.Description;
namespace Mono.Addins
{
- /// <summary>
- /// An extension context.
- /// </summary>
- /// <remarks>
- /// Extension contexts can be used to query the extension tree
- /// using particular condition values. Extension points which
- /// declare the availability of a condition type can only be
- /// queryed using an extension context which provides an
- /// evaluator for that condition.
- /// </remarks>
- public class ExtensionContext
+ /// <summary>
+ /// An extension context.
+ /// </summary>
+ /// <remarks>
+ /// Extension contexts can be used to query the extension tree
+ /// using particular condition values. Extension points which
+ /// declare the availability of a condition type can only be
+ /// queryed using an extension context which provides an
+ /// evaluator for that condition.
+ /// </remarks>
+ public class ExtensionContext
{
internal object LocalLock = new object ();
- ImmutableDictionary<string, ConditionInfo> conditionTypes = ImmutableDictionary<string, ConditionInfo>.Empty;
- ImmutableDictionary<BaseCondition, ImmutableArray<TreeNode>> conditionsToNodes = ImmutableDictionary<BaseCondition, ImmutableArray<TreeNode>>.Empty;
ImmutableArray<WeakReference> childContexts = ImmutableArray<WeakReference>.Empty;
ExtensionContext parentContext;
ExtensionTree tree;
- ImmutableArray<string> runTimeEnabledAddins = ImmutableArray<string>.Empty;
- ImmutableArray<string> runTimeDisabledAddins = ImmutableArray<string>.Empty;
-
+ NotificationQueue notificationQueue;
+
+ // runTimeEnabledAddins and runTimeDisabledAddins are modified only within a transaction,
+ // so they don't need to be immutable and don't need to be in the snapshot
+ HashSet<string> runTimeEnabledAddins = new HashSet<string>();
+ HashSet<string> runTimeDisabledAddins = new HashSet<string>();
+
+ ExtensionContextSnapshot currentSnapshot = new ExtensionContextSnapshot();
+
+ internal class ExtensionContextSnapshot
+ {
+ public ImmutableDictionary<string, ConditionInfo> ConditionTypes;
+ public ImmutableDictionary<BaseCondition, ImmutableArray<TreeNode>> ConditionsToNodes;
+
+ public ExtensionContextSnapshot()
+ {
+ ConditionTypes = ImmutableDictionary<string, ConditionInfo>.Empty;
+ ConditionsToNodes = ImmutableDictionary<BaseCondition, ImmutableArray<TreeNode>>.Empty;
+ }
+
+ public virtual void CopyFrom(ExtensionContextSnapshot other)
+ {
+ ConditionTypes = other.ConditionTypes;
+ ConditionsToNodes = other.ConditionsToNodes;
+ }
+ }
+
/// <summary>
/// Extension change event.
/// </summary>
@@ -69,14 +91,31 @@ namespace Mono.Addins
/// it does not provide information about what changed. Hosts subscribing to
/// this event should get the new list of nodes using a query method such as
/// AddinManager.GetExtensionNodes() and then update whatever needs to be updated.
+ ///
+ /// Threading information: the thread on which the event is raised is undefined. Events are
+ /// guaranteed to be raised sequentially for a given extension context.
/// </remarks>
public event ExtensionEventHandler ExtensionChanged;
internal void Initialize (AddinEngine addinEngine)
{
+ notificationQueue = new NotificationQueue(addinEngine);
+ SetSnapshot(CreateSnapshot());
tree = new ExtensionTree (addinEngine, this);
}
+ internal virtual ExtensionContextSnapshot CreateSnapshot()
+ {
+ return new ExtensionContextSnapshot();
+ }
+
+ internal virtual void SetSnapshot(ExtensionContextSnapshot newSnapshot)
+ {
+ currentSnapshot = newSnapshot;
+ }
+
+ internal ExtensionContextSnapshot CurrentSnapshot => currentSnapshot;
+
#pragma warning disable 1591
[ObsoleteAttribute]
protected void Clear ()
@@ -84,18 +123,19 @@ namespace Mono.Addins
}
#pragma warning restore 1591
+ internal void InvokeCallback(Action action, object source)
+ {
+ notificationQueue.Invoke(action, source);
+ }
internal void ClearContext ()
{
- conditionTypes = conditionTypes.Clear ();
- conditionsToNodes.Clear ();
+ SetSnapshot(CreateSnapshot());
childContexts = ImmutableArray<WeakReference>.Empty;
parentContext = null;
tree = null;
- runTimeEnabledAddins = ImmutableArray<string>.Empty;
- runTimeDisabledAddins = ImmutableArray<string>.Empty;
}
-
+
internal AddinEngine AddinEngine {
get { return tree.AddinEngine; }
}
@@ -125,11 +165,16 @@ namespace Mono.Addins
}
}
}
-
- internal virtual void ResetCachedData ()
+
+ internal void ResetCachedData()
+ {
+ using (var transaction = BeginTransaction())
+ ResetCachedData(transaction);
+ }
+
+ internal virtual void ResetCachedData (ExtensionContextTransaction transaction)
{
- using (var transaction = BeginTransaction ())
- tree.ResetCachedData (transaction);
+ tree.ResetCachedData (transaction);
foreach (var ctx in GetActiveChildContexes())
ctx.ResetCachedData ();
@@ -191,12 +236,6 @@ namespace Mono.Addins
GetOrCreateConditionInfo (transaction, id, type);
}
- void RegisterCondition (ExtensionContextTransaction transaction, string id, Type type)
- {
- // Allows delayed creation of condition types
- GetOrCreateConditionInfo (transaction, id, type);
- }
-
internal void RegisterCondition (ExtensionContextTransaction transaction, string id, RuntimeAddin addin, string typeName)
{
// Allows delayed creation of condition types
@@ -206,12 +245,12 @@ namespace Mono.Addins
});
}
- ConditionInfo GetOrCreateConditionInfo (ExtensionContextTransaction transaction, string id, object conditionTypeObject)
+ internal ConditionInfo GetOrCreateConditionInfo (ExtensionContextTransaction transaction, string id, object conditionTypeObject)
{
- if (!conditionTypes.TryGetValue (id, out var info)) {
+ if (!transaction.Snapshot.ConditionTypes.TryGetValue (id, out var info)) {
info = new ConditionInfo ();
info.CondType = conditionTypeObject;
- conditionTypes = conditionTypes.Add (id, info);
+ transaction.Snapshot.ConditionTypes = transaction.Snapshot.ConditionTypes.Add (id, info);
} else {
// If CondType is not changing, nothing else to do
if (conditionTypeObject == null)
@@ -230,7 +269,7 @@ namespace Mono.Addins
internal ConditionType GetCondition (string id)
{
- if (conditionTypes.TryGetValue(id, out var info)) {
+ if (currentSnapshot.ConditionTypes.TryGetValue(id, out var info)) {
if (info.CondType is ConditionType condition) {
return condition;
}
@@ -269,87 +308,6 @@ namespace Mono.Addins
return null;
}
- /// <summary>
- /// Registers a set of node conditions
- /// </summary>
- internal void BulkRegisterNodeConditions (ExtensionContextTransaction transaction, IEnumerable<(TreeNode Node, BaseCondition Condition)> nodeConditions)
- {
- // We are going to do many changes, to create a builder for the dictionary
- var dictBuilder = conditionsToNodes.ToBuilder ();
- List<(string ConditionId, BaseCondition BoundCondition)> bindings = new ();
-
- // Group nodes by the conditions, so that all nodes for a conditions can be processed together
-
- foreach (var group in nodeConditions.GroupBy (c => c.Condition)) {
- var condition = group.Key;
-
- if (!dictBuilder.TryGetValue (condition, out var list)) {
-
- // Condition not yet registered, register it now
-
- // Get a list of conditions on which this one depends
- var conditionTypeIds = new List<string> ();
- condition.GetConditionTypes (conditionTypeIds);
-
- foreach (string cid in conditionTypeIds) {
- // For each condition on which 'condition' depends, register the dependency
- // so that it if the condition changes, the dependencies are notified
- bindings.Add ((cid, condition));
- }
- list = ImmutableArray<TreeNode>.Empty;
- }
-
- dictBuilder [condition] = list.AddRange (group.Select (item => item.Node));
- }
-
- foreach (var binding in bindings.GroupBy(b => b.ConditionId, b => b.BoundCondition)) {
- ConditionInfo info = GetOrCreateConditionInfo (transaction, binding.Key, null);
- info.BoundConditions = info.BoundConditions.AddRange (binding);
- }
-
- conditionsToNodes = dictBuilder.ToImmutable ();
- }
-
- /// <summary>
- /// Unregisters a set of node conditions
- /// </summary>
- internal void BulkUnregisterNodeConditions (ExtensionContextTransaction transaction, IEnumerable<(TreeNode Node, BaseCondition Condition)> nodeConditions)
- {
- ImmutableDictionary<BaseCondition, ImmutableArray<TreeNode>>.Builder dictBuilder = null;
-
- foreach (var group in nodeConditions.GroupBy (c => c.Condition)) {
- var condition = group.Key;
- if (!conditionsToNodes.TryGetValue (condition, out var list))
- continue;
-
- var newList = list.RemoveRange (group.Select (item => item.Node));
-
- // If there are no changes, continue, no need to create the dictionary builder
- if (newList == list)
- continue;
-
- if (dictBuilder == null)
- dictBuilder = conditionsToNodes.ToBuilder ();
-
- if (newList.Length == 0) {
-
- // The condition is not used anymore. Remove it from the dictionary
- // and unregister it from any condition it was bound to
-
- dictBuilder.Remove (condition);
- var conditionTypeIds = new List<string> ();
- condition.GetConditionTypes (conditionTypeIds);
- foreach (string cid in conditionTypeIds) {
- var info = conditionTypes [cid];
- if (info != null)
- info.BoundConditions = info.BoundConditions.Remove (condition);
- }
- } else
- dictBuilder [condition] = newList;
- }
- if (dictBuilder != null)
- conditionsToNodes = dictBuilder.ToImmutable ();
- }
/// <summary>
/// Returns the extension node in a path
@@ -507,7 +465,7 @@ namespace Mono.Addins
if (node == null || !node.HasExtensionNode)
return ExtensionNodeList.Empty;
- ExtensionNodeList list = node.ExtensionNode.ChildNodes;
+ ExtensionNodeList list = node.ExtensionNode.GetChildNodes();
if (expectedNodeType != null) {
bool foundError = false;
@@ -856,14 +814,16 @@ namespace Mono.Addins
NotifyConditionChanged (cond);
}
- internal void NotifyConditionChanged (ConditionType cond)
+ void NotifyConditionChanged (ConditionType cond)
{
HashSet<TreeNode> parentsToNotify = null;
- if (conditionTypes.TryGetValue (cond.Id, out var info) && info.BoundConditions != null) {
+ var snapshot = currentSnapshot;
+
+ if (snapshot.ConditionTypes.TryGetValue (cond.Id, out var info) && info.BoundConditions != null) {
parentsToNotify = new HashSet<TreeNode> ();
foreach (BaseCondition c in info.BoundConditions) {
- if (conditionsToNodes.TryGetValue(c, out var nodeList)) {
+ if (snapshot.ConditionsToNodes.TryGetValue(c, out var nodeList)) {
parentsToNotify.UnionWith (nodeList.Select (node => node.Parent));
}
}
@@ -894,7 +854,12 @@ namespace Mono.Addins
internal void NotifyExtensionsChanged (ExtensionEventArgs args)
{
if (ExtensionChanged != null)
- ExtensionChanged (this, args);
+ {
+ notificationQueue.Invoke(() =>
+ {
+ ExtensionChanged?.Invoke(this, args);
+ }, null);
+ }
}
internal void NotifyAddinLoaded (RuntimeAddin ad)
@@ -941,7 +906,7 @@ namespace Mono.Addins
// Add the new nodes
foreach (ExtensionPoint ep in eps.Keys) {
- ExtensionLoadData data = GetAddinExtensions (id, ep);
+ ExtensionLoadData data = GetAddinExtensions (transaction, id, ep);
if (data != null) {
foreach (Extension ext in data.Extensions) {
TreeNode node = GetNode (ext.Path);
@@ -984,7 +949,7 @@ namespace Mono.Addins
// event without first getting the list of nodes that may change).
// We get the runtime add-in because the add-in may already have been deleted from the registry
- RuntimeAddin addin = AddinEngine.GetAddin (id);
+ RuntimeAddin addin = AddinEngine.GetAddin (transaction, id);
if (addin != null) {
var paths = new List<string> ();
// Using addin.Module.ParentAddinDescription here because addin.Addin.Description may not
@@ -1002,21 +967,17 @@ namespace Mono.Addins
void RegisterRuntimeDisabledAddin (ExtensionContextTransaction transaction, string addinId)
{
- if (!runTimeDisabledAddins.Contains (addinId))
- runTimeDisabledAddins = runTimeDisabledAddins.Add (addinId);
-
- runTimeEnabledAddins = runTimeEnabledAddins.Remove (addinId);
+ runTimeDisabledAddins.Add (addinId);
+ runTimeEnabledAddins.Remove (addinId);
}
void RegisterRuntimeEnabledAddin (ExtensionContextTransaction transaction, string addinId)
{
- if (!runTimeEnabledAddins.Contains (addinId))
- runTimeEnabledAddins = runTimeEnabledAddins.Add (addinId);
-
- runTimeDisabledAddins = runTimeDisabledAddins.Remove (addinId);
+ runTimeEnabledAddins.Add (addinId);
+ runTimeDisabledAddins.Remove (addinId);
}
- internal List<string> GetAddinsForPath (List<string> col)
+ List<string> GetAddinsForPath (ExtensionContextTransaction transaction, List<string> col)
{
List<string> newlist = null;
@@ -1024,23 +985,20 @@ namespace Mono.Addins
// they may contain extension for this path.
// Ignore addins disabled at run-time.
- var enabledAddins = runTimeEnabledAddins;
-
- if (enabledAddins != null && enabledAddins.Length > 0) {
+ if (runTimeEnabledAddins.Count > 0) {
newlist = new List<string> ();
newlist.AddRange (col);
- foreach (string s in enabledAddins)
+ foreach (string s in runTimeEnabledAddins)
if (!newlist.Contains (s))
newlist.Add (s);
}
- var disabledAddins = runTimeDisabledAddins;
- if (disabledAddins != null && disabledAddins.Length > 0) {
+ if (runTimeDisabledAddins.Count > 0) {
if (newlist == null) {
newlist = new List<string> ();
newlist.AddRange (col);
}
- foreach (string s in disabledAddins)
+ foreach (string s in runTimeDisabledAddins)
newlist.Remove (s);
}
@@ -1063,11 +1021,11 @@ namespace Mono.Addins
// Collect extensions to be loaded from add-ins. Before loading the extensions,
// they must be sorted, that's why loading is split in two steps (collecting + loading).
- var addins = GetAddinsForPath (ep.Addins);
+ var addins = GetAddinsForPath (transaction, ep.Addins);
var loadData = new List<ExtensionLoadData> (addins.Count);
foreach (string addin in addins) {
- ExtensionLoadData ed = GetAddinExtensions (addin, ep);
+ ExtensionLoadData ed = GetAddinExtensions (transaction, addin, ep);
if (ed != null) {
// Insert the addin data taking into account dependencies.
// An add-in must be processed after all its dependencies.
@@ -1099,18 +1057,18 @@ namespace Mono.Addins
}
// Call the OnAddinLoaded method on nodes, if the add-in is already loaded
foreach (TreeNode nod in loadedNodes)
- nod.ExtensionNode.OnAddinLoaded ();
+ nod.ExtensionNode.NotifyAddinLoaded();
- NotifyExtensionsChanged (new ExtensionEventArgs (requestedExtensionPath));
- }
- }
+ transaction.NotifyExtensionsChangedEvent(requestedExtensionPath);
+ }
+ }
- ExtensionLoadData GetAddinExtensions (string id, ExtensionPoint ep)
+ ExtensionLoadData GetAddinExtensions (ExtensionContextTransaction transaction, string id, ExtensionPoint ep)
{
Addin pinfo = null;
// Root add-ins are not returned by GetInstalledAddin.
- RuntimeAddin addin = AddinEngine.GetAddin (id);
+ RuntimeAddin addin = AddinEngine.GetAddin (transaction, id);
if (addin != null)
pinfo = addin.Addin;
else
@@ -1162,7 +1120,7 @@ namespace Mono.Addins
var addedNodes = new List<TreeNode> ();
tree.LoadExtension (transaction, node, addinId, extension, addedNodes);
- RuntimeAddin ad = AddinEngine.GetAddin (addinId);
+ RuntimeAddin ad = AddinEngine.GetAddin (transaction, addinId);
if (ad != null) {
foreach (TreeNode nod in addedNodes) {
// Don't call OnAddinLoaded here. Do it when the entire extension point has been loaded.
@@ -1250,11 +1208,6 @@ namespace Mono.Addins
return dstNode;
}
-
- internal bool FindExtensionPathByType (IProgressStatus monitor, Type type, string nodeName, out string path, out string pathNodeName)
- {
- return tree.FindExtensionPathByType (monitor, type, nodeName, out path, out pathNodeName);
- }
}
class ConditionInfo
@@ -1410,4 +1363,82 @@ namespace Mono.Addins
public string TypeName { get; set; }
public RuntimeAddin Addin { get; set; }
}
+
+ /// <summary>
+ /// A queue that can be used to dispatch callbacks sequentially
+ /// </summary>
+ class NotificationQueue
+ {
+ readonly AddinEngine addinEngine;
+ readonly Queue<(Action Action,object Source)> notificationQueue = new Queue<(Action,object)>();
+
+ bool sending;
+
+ public NotificationQueue(AddinEngine addinEngine)
+ {
+ this.addinEngine = addinEngine;
+ }
+
+ internal void Invoke(Action action, object source)
+ {
+ lock (notificationQueue)
+ {
+ if (sending)
+ {
+ // Already sending, enqueue the action so whoever is sending will take it
+ notificationQueue.Enqueue((action,source));
+ return;
+ }
+ else
+ {
+ // Nobody is sending, do it now
+ sending = true;
+ }
+ }
+
+ SafeInvoke(action, source);
+
+ do
+ {
+ lock (notificationQueue)
+ {
+ if (notificationQueue.Count == 0)
+ {
+ sending = false;
+ return;
+ }
+ (action,source) = notificationQueue.Dequeue();
+ }
+ SafeInvoke(action, source);
+ }
+ while (true);
+ }
+
+ void SafeInvoke(Action action, object source)
+ {
+ try
+ {
+ action();
+ }
+ catch (Exception ex)
+ {
+ RuntimeAddin addin = null;
+
+ if (source is ExtensionNode node)
+ {
+ try
+ {
+ addin = node.Addin;
+ }
+ catch (Exception addinException)
+ {
+ addinEngine.ReportError(null, null, addinException, false);
+ addin = null;
+ }
+ }
+
+ addinEngine.ReportError("Callback invocation failed", addin?.Id, ex, false);
+ }
+ }
+ }
}
diff --git a/Mono.Addins/Mono.Addins/ExtensionContextTransaction.cs b/Mono.Addins/Mono.Addins/ExtensionContextTransaction.cs
index 18d0eb3..62b8d52 100644
--- a/Mono.Addins/Mono.Addins/ExtensionContextTransaction.cs
+++ b/Mono.Addins/Mono.Addins/ExtensionContextTransaction.cs
@@ -29,7 +29,10 @@
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
using System.Threading;
+using static Mono.Addins.ExtensionContext;
namespace Mono.Addins
{
@@ -59,36 +62,76 @@ namespace Mono.Addins
List<RuntimeAddin> addinLoadEvents;
List<string> addinUnloadEvents;
List<TreeNode> treeNodeTransactions;
+ ExtensionContextSnapshot snapshot;
+ bool snaphotChanged;
public ExtensionContextTransaction (ExtensionContext context)
{
Context = context;
Monitor.Enter (Context.LocalLock);
+ snapshot = context.CurrentSnapshot;
}
public ExtensionContext Context { get; }
public bool DisableEvents { get; set; }
+ public AddinEngine.AddinEngineSnapshot AddinEngineSnapshot => (AddinEngine.AddinEngineSnapshot)Snapshot;
+
+ public ExtensionContextSnapshot Snapshot => snapshot;
+
+ void EnsureNewSnapshot()
+ {
+ if (!snaphotChanged)
+ {
+ snaphotChanged = true;
+ var newSnapshot = Context.CreateSnapshot();
+ newSnapshot.CopyFrom(snapshot);
+ snapshot = newSnapshot;
+ }
+ }
+
public void Dispose ()
{
- try {
+ var engine = Context as AddinEngine;
+
+ try
+ {
// Update the context
if (nodeConditions != null) {
- Context.BulkRegisterNodeConditions (this, nodeConditions);
+ BulkRegisterNodeConditions (nodeConditions);
}
if (nodeConditionUnregistrations != null) {
- Context.BulkUnregisterNodeConditions (this, nodeConditionUnregistrations);
+ BulkUnregisterNodeConditions (nodeConditionUnregistrations);
+ }
+
+ if (engine != null)
+ {
+ if (registeredAutoExtensionPoints != null)
+ AddinEngineSnapshot.AutoExtensionTypes = AddinEngineSnapshot.AutoExtensionTypes.SetItems(registeredAutoExtensionPoints);
+
+ if (unregisteredAutoExtensionPoints != null)
+ AddinEngineSnapshot.AutoExtensionTypes = AddinEngineSnapshot.AutoExtensionTypes.RemoveRange(unregisteredAutoExtensionPoints);
+
+ if (registeredAssemblyResolvePaths != null)
+ AddinEngineSnapshot.AssemblyResolvePaths = AddinEngineSnapshot.AssemblyResolvePaths.SetItems(registeredAssemblyResolvePaths);
+
+ if (unregisteredAssemblyResolvePaths != null)
+ AddinEngineSnapshot.AssemblyResolvePaths = AddinEngineSnapshot.AssemblyResolvePaths.RemoveRange(unregisteredAssemblyResolvePaths);
}
// Commit tree node transactions
- if (treeNodeTransactions != null) {
+ if (treeNodeTransactions != null)
+ {
foreach (var node in treeNodeTransactions)
- node.CommitChildrenUpdateTransaction ();
+ node.CommitChildrenUpdateTransaction();
}
+ if (snaphotChanged)
+ Context.SetSnapshot(snapshot);
+
} finally {
Monitor.Exit (Context.LocalLock);
}
@@ -109,32 +152,101 @@ namespace Mono.Addins
foreach (var path in extensionsChanged)
Context.NotifyExtensionsChanged (new ExtensionEventArgs (path));
}
- if (registeredAutoExtensionPoints != null) {
- var engine = (AddinEngine)Context;
- engine.BulkRegisterAutoTypeExtensionPoint (registeredAutoExtensionPoints);
- }
- if (unregisteredAutoExtensionPoints != null) {
- var engine = (AddinEngine)Context;
- engine.BulkUnregisterAutoTypeExtensionPoint (unregisteredAutoExtensionPoints);
- }
- if (registeredAssemblyResolvePaths != null) {
- var engine = (AddinEngine)Context;
- engine.BulkRegisterAssemblyResolvePaths (registeredAssemblyResolvePaths);
+
+ if (engine != null) {
+ if (addinLoadEvents != null) {
+ foreach (var addin in addinLoadEvents)
+ engine.ReportAddinLoad (addin);
+ }
+ if (addinUnloadEvents != null) {
+ foreach (var id in addinUnloadEvents)
+ engine.ReportAddinUnload (id);
+ }
}
- if (unregisteredAssemblyResolvePaths != null) {
- var engine = (AddinEngine)Context;
- engine.BulkUnregisterAssemblyResolvePaths (unregisteredAssemblyResolvePaths);
+ }
+
+ void BulkRegisterNodeConditions(IEnumerable<(TreeNode Node, BaseCondition Condition)> nodeConditions)
+ {
+ // We are going to do many changes, so create a builder for the dictionary
+ var dictBuilder = Snapshot.ConditionsToNodes.ToBuilder();
+ List<(string ConditionId, BaseCondition BoundCondition)> bindings = new();
+
+ // Group nodes by the conditions, so that all nodes for a conditions can be processed together
+
+ foreach (var group in nodeConditions.GroupBy(c => c.Condition))
+ {
+ var condition = group.Key;
+
+ if (!dictBuilder.TryGetValue(condition, out var list))
+ {
+
+ // Condition not yet registered, register it now
+
+ // Get a list of conditions on which this one depends
+ var conditionTypeIds = new List<string>();
+ condition.GetConditionTypes(conditionTypeIds);
+
+ foreach (string cid in conditionTypeIds)
+ {
+ // For each condition on which 'condition' depends, register the dependency
+ // so that it if the condition changes, the dependencies are notified
+ bindings.Add((cid, condition));
+ }
+ list = ImmutableArray<TreeNode>.Empty;
+ }
+
+ dictBuilder[condition] = list.AddRange(group.Select(item => item.Node));
}
- if (addinLoadEvents != null) {
- var engine = (AddinEngine)Context;
- foreach (var addin in addinLoadEvents)
- engine.ReportAddinLoad (addin);
+
+ foreach (var binding in bindings.GroupBy(b => b.ConditionId, b => b.BoundCondition))
+ {
+ ConditionInfo info = Context.GetOrCreateConditionInfo(this, binding.Key, null);
+ info.BoundConditions = info.BoundConditions.AddRange(binding);
}
- if (addinUnloadEvents != null) {
- var engine = (AddinEngine)Context;
- foreach (var id in addinUnloadEvents)
- engine.ReportAddinUnload (id);
+
+ Snapshot.ConditionsToNodes = dictBuilder.ToImmutable();
+ }
+
+ void BulkUnregisterNodeConditions(IEnumerable<(TreeNode Node, BaseCondition Condition)> nodeConditions)
+ {
+ ImmutableDictionary<BaseCondition, ImmutableArray<TreeNode>>.Builder dictBuilder = null;
+
+ foreach (var group in nodeConditions.GroupBy(c => c.Condition))
+ {
+ var condition = group.Key;
+ if (!Snapshot.ConditionsToNodes.TryGetValue(condition, out var list))
+ continue;
+
+ var newList = list.RemoveRange(group.Select(item => item.Node));
+
+ // If there are no changes, continue, no need to create the dictionary builder
+ if (newList == list)
+ continue;
+
+ if (dictBuilder == null)
+ dictBuilder = Snapshot.ConditionsToNodes.ToBuilder();
+
+ if (newList.Length == 0)
+ {
+
+ // The condition is not used anymore. Remove it from the dictionary
+ // and unregister it from any condition it was bound to
+
+ dictBuilder.Remove(condition);
+ var conditionTypeIds = new List<string>();
+ condition.GetConditionTypes(conditionTypeIds);
+ foreach (string cid in conditionTypeIds)
+ {
+ var info = Snapshot.ConditionTypes[cid];
+ if (info != null)
+ info.BoundConditions = info.BoundConditions.Remove(condition);
+ }
+ }
+ else
+ dictBuilder[condition] = newList;
}
+ if (dictBuilder != null)
+ Snapshot.ConditionsToNodes = dictBuilder.ToImmutable();
}
public void ReportLoadedNode (TreeNode node)
@@ -164,6 +276,7 @@ namespace Mono.Addins
public void RegisterNodeCondition (TreeNode node, BaseCondition cond)
{
+ EnsureNewSnapshot();
if (nodeConditions == null)
nodeConditions = new List<(TreeNode Node, BaseCondition Condition)> ();
nodeConditions.Add ((node, cond));
@@ -171,6 +284,7 @@ namespace Mono.Addins
public void UnregisterNodeCondition (TreeNode node, BaseCondition cond)
{
+ EnsureNewSnapshot();
if (nodeConditionUnregistrations == null)
nodeConditionUnregistrations = new List<(TreeNode Node, BaseCondition Condition)> ();
nodeConditionUnregistrations.Add ((node, cond));
@@ -180,6 +294,7 @@ namespace Mono.Addins
public void RegisterAutoTypeExtensionPoint (string typeName, string path)
{
+ EnsureNewSnapshot();
if (registeredAutoExtensionPoints == null)
registeredAutoExtensionPoints = new List<KeyValuePair<string, string>> ();
registeredAutoExtensionPoints.Add (new KeyValuePair<string, string> (typeName, path));
@@ -187,6 +302,7 @@ namespace Mono.Addins
public void UnregisterAutoTypeExtensionPoint (string typeName)
{
+ EnsureNewSnapshot();
if (unregisteredAutoExtensionPoints == null)
unregisteredAutoExtensionPoints = new List<string> ();
unregisteredAutoExtensionPoints.Add (typeName);
@@ -194,6 +310,7 @@ namespace Mono.Addins
public void RegisterAssemblyResolvePaths (string assembly, RuntimeAddin addin)
{
+ EnsureNewSnapshot();
if (registeredAssemblyResolvePaths == null)
registeredAssemblyResolvePaths = new List<KeyValuePair<string, RuntimeAddin>> ();
registeredAssemblyResolvePaths.Add (new KeyValuePair<string, RuntimeAddin> (assembly, addin));
@@ -201,6 +318,7 @@ namespace Mono.Addins
public void UnregisterAssemblyResolvePaths (string assembly)
{
+ EnsureNewSnapshot();
if (unregisteredAssemblyResolvePaths == null)
unregisteredAssemblyResolvePaths = new List<string> ();
unregisteredAssemblyResolvePaths.Add (assembly);
@@ -226,5 +344,6 @@ namespace Mono.Addins
treeNodeTransactions = new List<TreeNode> ();
treeNodeTransactions.Add (node);
}
+
}
}
diff --git a/Mono.Addins/Mono.Addins/ExtensionNode.cs b/Mono.Addins/Mono.Addins/ExtensionNode.cs
index ffb9243..36f227d 100644
--- a/Mono.Addins/Mono.Addins/ExtensionNode.cs
+++ b/Mono.Addins/Mono.Addins/ExtensionNode.cs
@@ -157,9 +157,7 @@ namespace Mono.Addins
public RuntimeAddin Addin {
get {
if (addin == null && addinId != null) {
- if (!addinEngine.IsAddinLoaded (addinId))
- addinEngine.LoadAddin (null, addinId, true);
- addin = addinEngine.GetAddin (addinId);
+ addin = addinEngine.GetOrLoadAddin(addinId, true);
if (addin != null)
addin = addin.GetModule (module);
}
@@ -168,53 +166,87 @@ namespace Mono.Addins
return addin;
}
}
-
+
/// <summary>
/// Notifies that a child node of this node has been added or removed.
/// </summary>
/// <remarks>
/// The first time the event is subscribed, the handler will be called for each existing node.
+ ///
+ /// Threading information: the thread on which the event is raised is undefined. Events are
+ /// guaranteed to be raised sequentially for a given extension context.
/// </remarks>
public event ExtensionNodeEventHandler ExtensionNodeChanged {
- add {
- extensionNodeChanged += value;
- foreach (ExtensionNode node in ChildNodes) {
- try {
- value (this, new ExtensionNodeEventArgs (ExtensionChange.Add, node));
- } catch (Exception ex) {
- RuntimeAddin nodeAddin;
- try {
- nodeAddin = node.Addin;
- } catch (Exception addinException) {
- addinEngine.ReportError (null, null, addinException, false);
- nodeAddin = null;
- }
- addinEngine.ReportError (null, nodeAddin != null ? nodeAddin.Id : null, ex, false);
+ add
+ {
+ ExtensionContext.InvokeCallback(() =>
+ {
+ // The invocation here needs to be done right to make sure there are no duplicate or missing events.
+
+ // 1) Get the list of current nodes and store it in a variable. This is the list of nodes for which
+ // notifications will initially be sent.
+
+ var children = GetChildNodes();
+
+ // 2) Subscribe the real event. If nodes were added or removed since the above GetChildNodes
+ // call, callback invocations will now be queued (since we are inside InvokeCallback).
+
+ extensionNodeChanged += value;
+
+ // 3) Send the notifications for the events. Again, any change done after the above GetChildNodes
+ // call will have queued a notification.
+
+ foreach (ExtensionNode node in children)
+ {
+ var theNode = node;
+ value(this, new ExtensionNodeEventArgs(ExtensionChange.Add, theNode));
}
- }
+
+ // 4) We are done notifying the pre-existing nodes. If there was any further change in the children
+ // list, the corresponding events will be raised after this callback ends.
+
+ }, this);
}
remove {
- extensionNodeChanged -= value;
+ ExtensionContext.InvokeCallback(() =>
+ {
+ // This is done inside a InvokeCallback call for simetry with the 'add' block. Since the 'add'
+ // block runs on a callback that can be queued, doing the same here ensures that the unsubscription
+ // will always happen after the subscription
+ extensionNodeChanged -= value;
+ }, this);
}
}
-
+
/// <summary>
/// Child nodes of this extension node.
/// </summary>
- public ExtensionNodeList ChildNodes {
- get {
- if (!childrenLoaded) {
- lock (localLock) {
- if (!childrenLoaded) {
- childNodes = CreateChildrenList ();
- childrenLoaded = true;
- }
+ [Obsolete("Use GetChildNodes()")]
+ public ExtensionNodeList ChildNodes => GetChildNodes();
+
+ /// <summary>
+ /// Child nodes of this extension node.
+ ///
+ /// Threading information: the returned list is an snapshot of the current children list. The returned
+ /// list will not change if this node's children change as result of add-ins loading or conditions changing.
+ /// Successive calls to this method can return different list instances.
+ /// </summary>
+ public ExtensionNodeList GetChildNodes()
+ {
+ if (!childrenLoaded)
+ {
+ lock (localLock)
+ {
+ if (!childrenLoaded)
+ {
+ childNodes = CreateChildrenList();
+ childrenLoaded = true;
}
- }
- return childNodes;
+ }
}
+ return childNodes;
}
-
+
/// <summary>
/// Returns the child objects of a node.
/// </summary>
@@ -333,7 +365,9 @@ namespace Mono.Addins
Array GetChildObjectsInternal (Type arrayElementType, bool reuseCachedInstance)
{
- var children = ChildNodes;
+ // The ChildNodes collection can't change, but it can be replaced by another collection,
+ // so we keep a local reference, which won't change
+ var children = GetChildNodes();
ArrayList list = new ArrayList (children.Count);
@@ -527,57 +561,108 @@ namespace Mono.Addins
if (changes != null) {
foreach (var change in changes) {
+ var node = change.Node;
if (change.Added)
- OnChildNodeAdded (change.Node);
+ {
+ ExtensionContext.InvokeCallback(() =>
+ {
+ OnChildNodeAdded(node);
+ }, this);
+ }
else
- OnChildNodeRemoved (change.Node);
+ {
+ ExtensionContext.InvokeCallback(() =>
+ {
+ OnChildNodeRemoved(node);
+ }, this);
+ }
}
- OnChildrenChanged ();
+ ExtensionContext.InvokeCallback(OnChildrenChanged, this);
return true;
} else
return false;
}
-
+
+ internal void NotifyAddinLoaded()
+ {
+ ExtensionContext.InvokeCallback(OnAddinLoaded, this);
+ }
+
+ internal void NotifyAddinUnloaded()
+ {
+ ExtensionContext.InvokeCallback(OnAddinUnloaded, this);
+ }
+
/// <summary>
/// Called when the add-in that defined this extension node is actually loaded in memory.
/// </summary>
- internal protected virtual void OnAddinLoaded ()
+ /// <remarks>
+ /// Threading information: the thread on which the method is invoked is undefined.
+ /// Invocations to the virtual methods in this class are guaranteed to be done sequentially.
+ /// For example, OnAddinLoaded will always be called before OnAddinUnloaded, and never
+ /// concurrently.
+ /// </remarks>
+ protected virtual void OnAddinLoaded ()
{
}
-
+
/// <summary>
/// Called when the add-in that defined this extension node is being
/// unloaded from memory.
/// </summary>
- internal protected virtual void OnAddinUnloaded ()
+ /// <remarks>
+ /// Threading information: the thread on which the method is invoked is undefined.
+ /// Invocations to the virtual methods in this class are guaranteed to be done sequentially.
+ /// For example, OnAddinLoaded will always be called before OnAddinUnloaded, and never
+ /// concurrently.
+ /// </remarks>
+ protected virtual void OnAddinUnloaded ()
{
}
-
+
/// <summary>
/// Called when the children list of this node has changed. It may be due to add-ins
/// being loaded/unloaded, or to conditions being changed.
/// </summary>
+ /// <remarks>
+ /// Threading information: the thread on which the method is invoked is undefined.
+ /// Invocations to the virtual methods in this class are guaranteed to be done sequentially.
+ /// For example, OnChildNodeAdded will always be called before OnChildNodeRemoved for a given
+ /// child, and never concurrently.
+ /// </remarks>
protected virtual void OnChildrenChanged ()
{
}
-
+
/// <summary>
/// Called when a child node is added
/// </summary>
/// <param name="node">
/// Added node.
/// </param>
+ /// <remarks>
+ /// Threading information: the thread on which the method is invoked is undefined.
+ /// Invocations to the virtual methods in this class are guaranteed to be done sequentially.
+ /// For example, OnChildNodeAdded will always be called before OnChildNodeRemoved for a given
+ /// child, and never concurrently.
+ /// </remarks>
protected virtual void OnChildNodeAdded (ExtensionNode node)
{
extensionNodeChanged?.Invoke (this, new ExtensionNodeEventArgs (ExtensionChange.Add, node));
}
-
+
/// <summary>
/// Called when a child node is removed
/// </summary>
/// <param name="node">
/// Removed node.
/// </param>
+ /// <remarks>
+ /// Threading information: the thread on which the method is invoked is undefined.
+ /// Invocations to the virtual methods in this class are guaranteed to be done sequentially.
+ /// For example, OnChildNodeAdded will always be called before OnChildNodeRemoved for a given
+ /// child, and never concurrently.
+ /// </remarks>
protected virtual void OnChildNodeRemoved (ExtensionNode node)
{
extensionNodeChanged?.Invoke (this, new ExtensionNodeEventArgs (ExtensionChange.Remove, node));
diff --git a/Mono.Addins/Mono.Addins/ExtensionTree.cs b/Mono.Addins/Mono.Addins/ExtensionTree.cs
index 37a16ba..72f065d 100644
--- a/Mono.Addins/Mono.Addins/ExtensionTree.cs
+++ b/Mono.Addins/Mono.Addins/ExtensionTree.cs
@@ -112,7 +112,7 @@ namespace Mono.Addins
curPos = parentNode.Children.Count;
// Find the type of the node in this extension
- ExtensionNodeType ntype = addinEngine.FindType (parentNode.ExtensionNodeSet, elem.NodeName, addin);
+ ExtensionNodeType ntype = addinEngine.FindType (transaction, parentNode.ExtensionNodeSet, elem.NodeName, addin);
if (ntype == null) {
addinEngine.ReportError ("Node '" + elem.NodeName + "' not allowed in extension: " + parentNode.GetPath (), addin, null, false);
@@ -125,7 +125,7 @@ namespace Mono.Addins
TreeNode childNode = new TreeNode (addinEngine, id);
- ExtensionNode enode = ReadNode (childNode, addin, ntype, elem, module);
+ ExtensionNode enode = ReadNode (childNode, addin, ntype, elem, module, transaction);
if (enode == null)
continue;
@@ -173,11 +173,11 @@ namespace Mono.Addins
return new NullCondition ();
}
- public ExtensionNode ReadNode (TreeNode tnode, string addin, ExtensionNodeType ntype, ExtensionNodeDescription elem, ModuleDescription module)
+ public ExtensionNode ReadNode (TreeNode tnode, string addin, ExtensionNodeType ntype, ExtensionNodeDescription elem, ModuleDescription module, ExtensionContextTransaction transaction)
{
try {
if (ntype.Type == null) {
- if (!InitializeNodeType (ntype))
+ if (!InitializeNodeType (ntype, transaction))
return null;
}
@@ -199,19 +199,17 @@ namespace Mono.Addins
}
}
- bool InitializeNodeType (ExtensionNodeType ntype)
+ bool InitializeNodeType (ExtensionNodeType ntype, ExtensionContextTransaction transaction)
{
- RuntimeAddin p = addinEngine.GetAddin (ntype.AddinId);
- if (p == null) {
- if (!addinEngine.IsAddinLoaded (ntype.AddinId)) {
- if (!addinEngine.LoadAddin (null, ntype.AddinId, false))
- return false;
- p = addinEngine.GetAddin (ntype.AddinId);
- if (p == null) {
- addinEngine.ReportError ("Add-in not found", ntype.AddinId, null, false);
- return false;
- }
+ RuntimeAddin p = addinEngine.GetAddin(transaction, ntype.AddinId);
+ if (p == null)
+ {
+ if (!addinEngine.LoadAddin(transaction, null, ntype.AddinId, false))
+ {
+ addinEngine.ReportError("Add-in not found", ntype.AddinId, null, false);
+ return false;
}
+ p = addinEngine.GetAddin(transaction, ntype.AddinId);
}
// If no type name is provided, use TypeExtensionNode by default
diff --git a/Mono.Addins/Mono.Addins/RuntimeAddin.cs b/Mono.Addins/Mono.Addins/RuntimeAddin.cs
index a23c9b8..abbbf1f 100644
--- a/Mono.Addins/Mono.Addins/RuntimeAddin.cs
+++ b/Mono.Addins/Mono.Addins/RuntimeAddin.cs
@@ -731,10 +731,11 @@ namespace Mono.Addins
AddinDependency pdep = dep as AddinDependency;
if (pdep == null)
continue;
- if (!addinEngine.IsAddinLoaded (pdep.FullAddinId))
+ var addin = addinEngine.GetAddin(pdep.FullAddinId);
+ if (addin == null)
return false;
if (forceLoadAssemblies)
- addinEngine.GetAddin (pdep.FullAddinId).EnsureAssembliesLoaded ();
+ addin.EnsureAssembliesLoaded ();
}
return true;
}
diff --git a/Mono.Addins/Mono.Addins/TreeNode.cs b/Mono.Addins/Mono.Addins/TreeNode.cs
index 1c60176..533e092 100644
--- a/Mono.Addins/Mono.Addins/TreeNode.cs
+++ b/Mono.Addins/Mono.Addins/TreeNode.cs
@@ -97,12 +97,12 @@ namespace Mono.Addins
public void NotifyAddinUnloaded ()
{
- extensionNode?.OnAddinUnloaded ();
+ extensionNode?.NotifyAddinUnloaded ();
}
public void NotifyAddinLoaded ()
{
- extensionNode?.OnAddinLoaded ();
+ extensionNode?.NotifyAddinLoaded ();
}
public bool HasExtensionNode {
@@ -344,10 +344,10 @@ namespace Mono.Addins
}
}
} finally {
+ childrenFromExtensionsLoaded = true;
if (disposeTransaction)
transaction.Dispose ();
}
- childrenFromExtensionsLoaded = true;
}
return (IReadOnlyList<TreeNode>)childrenBuilder ?? (IReadOnlyList<TreeNode>)children;
}
@@ -427,7 +427,7 @@ namespace Mono.Addins
public void NotifyAddinLoaded (RuntimeAddin ad, bool recursive)
{
if (extensionNode != null && extensionNode.AddinId == ad.Addin.Id)
- extensionNode.OnAddinLoaded ();
+ extensionNode.NotifyAddinLoaded ();
if (recursive && childrenFromExtensionsLoaded) {
foreach (TreeNode node in Children)
node.NotifyAddinLoaded (ad, true);
@@ -475,36 +475,6 @@ namespace Mono.Addins
nodes.Add (this);
}
- public bool FindExtensionPathByType (IProgressStatus monitor, Type type, string nodeName, out string path, out string pathNodeName)
- {
- if (extensionPoint != null) {
- foreach (ExtensionNodeType nt in extensionPoint.NodeSet.NodeTypes) {
- if (nt.ObjectTypeName.Length > 0 && (nodeName.Length == 0 || nodeName == nt.Id)) {
- RuntimeAddin addin = addinEngine.GetAddin (extensionPoint.RootAddin);
- Type ot = addin.GetType (nt.ObjectTypeName, true);
- if (ot != null) {
- if (ot.IsAssignableFrom (type)) {
- path = extensionPoint.Path;
- pathNodeName = nt.Id;
- return true;
- }
- }
- else
- monitor.ReportError ("Type '" + nt.ObjectTypeName + "' not found in add-in '" + Id + "'", null);
- }
- }
- }
- else {
- foreach (TreeNode node in Children) {
- if (node.FindExtensionPathByType (monitor, type, nodeName, out path, out pathNodeName))
- return true;
- }
- }
- path = null;
- pathNodeName = null;
- return false;
- }
-
public bool NotifyChildrenChanged ()
{
if (extensionNode != null)
@@ -517,7 +487,7 @@ namespace Mono.Addins
{
if (extensionPoint != null) {
string aid = Addin.GetIdName (extensionPoint.ParentAddinDescription.AddinId);
- RuntimeAddin ad = addinEngine.GetAddin (aid);
+ RuntimeAddin ad = addinEngine.GetAddin (transaction, aid);
if (ad != null)
extensionPoint = ad.Addin.Description.ExtensionPoints [GetPath ()];
}
diff --git a/Version.props b/Version.props
index 90c3a01..8df8ed8 100644
--- a/Version.props
+++ b/Version.props
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
- <PackageVersion>1.3.15-alpha</PackageVersion>
+ <PackageVersion>1.3.15-alpha.2</PackageVersion>
<Authors>Microsoft</Authors>
<Owners>microsoft, xamarin</Owners>
<PackageLicenseUrl>https://github.com/mono/mono-addins/blob/main/COPYING</PackageLicenseUrl>