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-14 17:45:28 +0300
committerLluis Sanchez <llsan@microsoft.com>2022-09-14 17:45:28 +0300
commitfcd6b0324ceaad72978fc95d7df1ef70c492d658 (patch)
treeab74762ea98462775683a8f7fa0ffd854980d231
parentbca58a65e72d3bc418c1dc5cdc5dd63c911b2eea (diff)
Fix threading issue
Split the context transaction class in two classes, one for extension context and one for add-in engine (which is a context by itself). In this was there is no danger of providing a context transaction to an add-in engine method and expect it to work. Added method for stating an engine transaction from a context transaction. Fixes tests.
-rw-r--r--Mono.Addins/Mono.Addins.Database/AddinDatabase.cs16
-rw-r--r--Mono.Addins/Mono.Addins/AddinEngine.cs59
-rw-r--r--Mono.Addins/Mono.Addins/AddinRegistry.cs2
-rw-r--r--Mono.Addins/Mono.Addins/ConditionType.cs2
-rw-r--r--Mono.Addins/Mono.Addins/ExtensionContext.cs8
-rw-r--r--Mono.Addins/Mono.Addins/ExtensionContextTransaction.cs230
-rw-r--r--Mono.Addins/Mono.Addins/ExtensionTree.cs7
-rw-r--r--Mono.Addins/Mono.Addins/TreeNode.cs2
-rw-r--r--Test/UnitTests/TestMultithreading.cs121
-rw-r--r--Test/UnitTests/UnitTests.csproj1
10 files changed, 257 insertions, 191 deletions
diff --git a/Mono.Addins/Mono.Addins.Database/AddinDatabase.cs b/Mono.Addins/Mono.Addins.Database/AddinDatabase.cs
index 73cb55c..842c12b 100644
--- a/Mono.Addins/Mono.Addins.Database/AddinDatabase.cs
+++ b/Mono.Addins/Mono.Addins.Database/AddinDatabase.cs
@@ -75,7 +75,7 @@ namespace Mono.Addins.Database
fileDatabase = new FileDatabase (AddinDbDir);
}
- public AddinDatabaseTransaction BeginTransaction (ExtensionContextTransaction addinEngineTransaction = null)
+ public AddinDatabaseTransaction BeginTransaction (AddinEngineTransaction addinEngineTransaction = null)
{
return new AddinDatabaseTransaction (this, localLock, addinEngineTransaction);
}
@@ -1096,7 +1096,7 @@ namespace Mono.Addins.Database
Update (monitor, domain, context);
}
- public void Update(IProgressStatus monitor, string domain, ScanOptions context = null, ExtensionContextTransaction addinEngineTransaction = null)
+ public void Update(IProgressStatus monitor, string domain, ScanOptions context = null, AddinEngineTransaction addinEngineTransaction = null)
{
if (monitor == null)
monitor = new ConsoleProgressStatus(false);
@@ -1108,7 +1108,7 @@ namespace Mono.Addins.Database
DateTime tim = DateTime.Now;
- var dbTransaction = BeginTransaction(addinEngineTransaction);
+ using var dbTransaction = BeginTransaction(addinEngineTransaction);
RunPendingUninstalls(dbTransaction, monitor);
@@ -2020,10 +2020,10 @@ namespace Mono.Addins.Database
{
readonly AddinDatabase addinDatabase;
readonly object localLock;
- ExtensionContextTransaction addinEngineTransaction;
+ AddinEngineTransaction addinEngineTransaction;
bool addinEngineTransactionStarted;
- public AddinDatabaseTransaction (AddinDatabase addinDatabase, object localLock, ExtensionContextTransaction addinEngineTransaction)
+ public AddinDatabaseTransaction (AddinDatabase addinDatabase, object localLock, AddinEngineTransaction addinEngineTransaction)
{
this.addinDatabase = addinDatabase;
this.localLock = localLock;
@@ -2031,19 +2031,19 @@ namespace Mono.Addins.Database
Monitor.Enter (localLock);
}
- public ExtensionContextTransaction GetAddinEngineTransaction()
+ public AddinEngineTransaction GetAddinEngineTransaction()
{
if (addinEngineTransaction != null)
return addinEngineTransaction;
addinEngineTransactionStarted = true;
- return addinEngineTransaction = addinDatabase.AddinEngine.BeginTransaction();
+ return addinEngineTransaction = addinDatabase.AddinEngine.BeginEngineTransaction();
}
public void Dispose ()
{
+ Monitor.Exit(localLock);
if (addinEngineTransactionStarted)
addinEngineTransaction.Dispose();
- Monitor.Exit (localLock);
}
}
diff --git a/Mono.Addins/Mono.Addins/AddinEngine.cs b/Mono.Addins/Mono.Addins/AddinEngine.cs
index 507d973..3bfe794 100644
--- a/Mono.Addins/Mono.Addins/AddinEngine.cs
+++ b/Mono.Addins/Mono.Addins/AddinEngine.cs
@@ -130,6 +130,16 @@ namespace Mono.Addins
base.SetSnapshot(newSnapshot);
}
+ internal override ExtensionContextTransaction BeginTransaction()
+ {
+ return new AddinEngineTransaction(this);
+ }
+
+ internal AddinEngineTransaction BeginEngineTransaction()
+ {
+ return (AddinEngineTransaction)BeginTransaction();
+ }
+
/// <summary>
/// Initializes the add-in engine
/// </summary>
@@ -284,7 +294,7 @@ namespace Mono.Addins
else
registry = new AddinRegistry (this, configDir, startupDirectory, addinsDir, databaseDir, null);
- using var transaction = BeginTransaction();
+ using var transaction = BeginEngineTransaction();
if ((asmFile != null && registry.CreateHostAddinsFile (asmFile)) || registry.UnknownDomain)
registry.Update (new ConsoleProgressStatus (false), transaction);
@@ -495,14 +505,17 @@ namespace Mono.Addins
/// </returns>
public bool IsAddinLoaded (string id)
{
- return IsAddinLoaded(currentSnapshot, id);
+ return IsAddinLoaded(null, id);
}
- bool IsAddinLoaded(AddinEngineSnapshot snapshot, string id)
+ bool IsAddinLoaded(AddinEngineTransaction transaction, string id)
{
CheckInitialized();
ValidateAddinRoots();
- return snapshot.LoadedAddins.ContainsKey(Addin.GetIdName(id));
+ if (transaction?.Context == this)
+ return transaction.AddinEngineSnapshot.LoadedAddins.ContainsKey(Addin.GetIdName(id));
+ else
+ return currentSnapshot.LoadedAddins.ContainsKey(Addin.GetIdName(id));
}
internal RuntimeAddin GetAddin (string id)
@@ -513,7 +526,7 @@ namespace Mono.Addins
return a;
}
- internal RuntimeAddin GetAddin(ExtensionContextTransaction transaction, string id)
+ internal RuntimeAddin GetAddin(AddinEngineTransaction transaction, string id)
{
ValidateAddinRoots();
RuntimeAddin a;
@@ -521,7 +534,7 @@ namespace Mono.Addins
// If a transaction is provided, try using the snapshot from that transaction,
// but this is only possible if the transaction was created for this context
- if (transaction.Context == this)
+ if (transaction?.Context == this)
transaction.AddinEngineSnapshot.LoadedAddins.TryGetValue(Addin.GetIdName(id), out a);
else
currentSnapshot.LoadedAddins.TryGetValue(Addin.GetIdName(id), out a);
@@ -535,7 +548,7 @@ namespace Mono.Addins
currentSnapshot.LoadedAddins.TryGetValue(Addin.GetIdName(id), out a);
if (a == null)
{
- using var tr = BeginTransaction();
+ using var tr = BeginEngineTransaction();
LoadAddin(tr, null, id, throwExceptions);
tr.AddinEngineSnapshot.LoadedAddins.TryGetValue(Addin.GetIdName(id), out a);
}
@@ -547,7 +560,7 @@ namespace Mono.Addins
ActivateAddinExtensions (transaction, id);
}
- internal void UnloadAddin (ExtensionContextTransaction transaction, string id)
+ internal void UnloadAddin (AddinEngineTransaction transaction, string id)
{
RemoveAddinExtensions (transaction, id);
@@ -580,17 +593,17 @@ namespace Mono.Addins
public void LoadAddin (IProgressStatus statusMonitor, string id)
{
CheckInitialized ();
- using var transaction = BeginTransaction();
+ using var transaction = BeginEngineTransaction();
if (LoadAddin (transaction, statusMonitor, id, true)) {
var adn = GetAddin (transaction, id);
adn.EnsureAssembliesLoaded ();
}
}
- internal bool LoadAddin (ExtensionContextTransaction transaction, IProgressStatus statusMonitor, string id, bool throwExceptions)
+ internal bool LoadAddin (AddinEngineTransaction transaction, IProgressStatus statusMonitor, string id, bool throwExceptions)
{
try {
- if (IsAddinLoaded (transaction.AddinEngineSnapshot, id))
+ if (IsAddinLoaded (transaction, id))
return true;
if (!Registry.IsAddinEnabled (id)) {
@@ -616,7 +629,7 @@ namespace Mono.Addins
statusMonitor.SetProgress ((double)n / (double)addins.Count);
Addin iad = addins [n];
- if (IsAddinLoaded (transaction.AddinEngineSnapshot, iad.Id))
+ if (IsAddinLoaded (transaction, iad.Id))
continue;
if (statusMonitor != null)
@@ -645,7 +658,7 @@ namespace Mono.Addins
base.OnResetCachedData (transaction);
}
- bool InsertAddin (ExtensionContextTransaction transaction, IProgressStatus statusMonitor, Addin iad)
+ bool InsertAddin (AddinEngineTransaction transaction, IProgressStatus statusMonitor, Addin iad)
{
try {
RuntimeAddin runtimeAddin = new RuntimeAddin (this, iad);
@@ -680,7 +693,7 @@ namespace Mono.Addins
}
}
- void RegisterAssemblyResolvePaths (ExtensionContextTransaction transaction, RuntimeAddin addin, ModuleDescription description)
+ void RegisterAssemblyResolvePaths (AddinEngineTransaction transaction, RuntimeAddin addin, ModuleDescription description)
{
foreach (var asm in description.AssemblyNames) {
//Debug.Assert(assemblyResolvePaths[asm] == addin); This does not look right. Not called in old project since DEBUG symbol is not defined.
@@ -688,7 +701,7 @@ namespace Mono.Addins
}
}
- internal void InsertExtensionPoint (ExtensionContextTransaction transaction, RuntimeAddin addin, ExtensionPoint ep)
+ internal void InsertExtensionPoint (AddinEngineTransaction transaction, RuntimeAddin addin, ExtensionPoint ep)
{
CreateExtensionPoint (transaction, ep);
foreach (ExtensionNodeType nt in ep.NodeSet.NodeTypes) {
@@ -698,9 +711,9 @@ namespace Mono.Addins
}
}
- bool ResolveLoadDependencies (ExtensionContextTransaction transaction, List<Addin> addins, Stack depCheck, string id, bool optional)
+ bool ResolveLoadDependencies (AddinEngineTransaction transaction, List<Addin> addins, Stack depCheck, string id, bool optional)
{
- if (IsAddinLoaded (transaction.AddinEngineSnapshot, id))
+ if (IsAddinLoaded (transaction, id))
return true;
if (depCheck.Contains (id))
@@ -798,14 +811,14 @@ namespace Mono.Addins
return null;
}
- internal void RegisterAutoTypeExtensionPoint (ExtensionContextTransaction transaction, string typeName, string path)
+ internal void RegisterAutoTypeExtensionPoint (AddinEngineTransaction transaction, string typeName, string path)
{
if (Util.TryParseTypeName (typeName, out var t, out var _))
typeName = t;
transaction.RegisterAutoTypeExtensionPoint (typeName, path);
}
- internal void UnregisterAutoTypeExtensionPoint (ExtensionContextTransaction transaction, string typeName, string path)
+ internal void UnregisterAutoTypeExtensionPoint (AddinEngineTransaction transaction, string typeName, string path)
{
if (Util.TryParseTypeName (typeName, out var t, out var _))
typeName = t;
@@ -842,13 +855,13 @@ namespace Mono.Addins
}
}
if (copy != null) {
- using var tr = BeginTransaction();
+ using var tr = BeginEngineTransaction();
foreach (Assembly asm in copy)
CheckHostAssembly (tr, asm);
}
}
- internal void ActivateRoots (ExtensionContextTransaction transaction)
+ internal void ActivateRoots (AddinEngineTransaction transaction)
{
lock (pendingRootChecks)
pendingRootChecks.Clear ();
@@ -857,7 +870,7 @@ namespace Mono.Addins
CheckHostAssembly (transaction, asm);
}
- void CheckHostAssembly (ExtensionContextTransaction transaction, Assembly asm)
+ void CheckHostAssembly (AddinEngineTransaction transaction, Assembly asm)
{
if (AddinDatabase.RunningSetupProcess || asm is System.Reflection.Emit.AssemblyBuilder || asm.IsDynamic)
return;
@@ -884,7 +897,7 @@ namespace Mono.Addins
ainfo = Registry.GetAddinForHostAssembly (asmFile);
}
- if (ainfo != null && !IsAddinLoaded (transaction.AddinEngineSnapshot, ainfo.Id)) {
+ if (ainfo != null && !IsAddinLoaded (transaction, ainfo.Id)) {
AddinDescription adesc = null;
try {
adesc = ainfo.Description;
diff --git a/Mono.Addins/Mono.Addins/AddinRegistry.cs b/Mono.Addins/Mono.Addins/AddinRegistry.cs
index 1346883..944d96e 100644
--- a/Mono.Addins/Mono.Addins/AddinRegistry.cs
+++ b/Mono.Addins/Mono.Addins/AddinRegistry.cs
@@ -639,7 +639,7 @@ namespace Mono.Addins
database.Update (monitor, currentDomain);
}
- internal void Update(IProgressStatus monitor, ExtensionContextTransaction addinEngineTransaction)
+ internal void Update(IProgressStatus monitor, AddinEngineTransaction addinEngineTransaction)
{
database.Update(monitor, currentDomain, addinEngineTransaction:addinEngineTransaction);
}
diff --git a/Mono.Addins/Mono.Addins/ConditionType.cs b/Mono.Addins/Mono.Addins/ConditionType.cs
index d5bf6f6..c5937f6 100644
--- a/Mono.Addins/Mono.Addins/ConditionType.cs
+++ b/Mono.Addins/Mono.Addins/ConditionType.cs
@@ -210,7 +210,7 @@ namespace Mono.Addins
if (!string.IsNullOrEmpty (addin)) {
// Make sure the add-in that implements the condition is loaded
- using var tr = addinEngine.BeginTransaction();
+ using var tr = addinEngine.BeginEngineTransaction();
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 bb9c478..7d03a1e 100644
--- a/Mono.Addins/Mono.Addins/ExtensionContext.cs
+++ b/Mono.Addins/Mono.Addins/ExtensionContext.cs
@@ -809,7 +809,7 @@ namespace Mono.Addins
RemoveExtensionNodeHandler (path, handler);
}
- internal ExtensionContextTransaction BeginTransaction ()
+ internal virtual ExtensionContextTransaction BeginTransaction ()
{
return new ExtensionContextTransaction (this);
}
@@ -957,7 +957,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 (transaction, id);
+ RuntimeAddin addin = AddinEngine.GetAddin (transaction.GetAddinEngineTransaction(), id);
if (addin != null) {
var paths = new List<string> ();
// Using addin.Module.ParentAddinDescription here because addin.Addin.Description may not
@@ -1076,7 +1076,7 @@ namespace Mono.Addins
Addin pinfo = null;
// Root add-ins are not returned by GetInstalledAddin.
- RuntimeAddin addin = AddinEngine.GetAddin (transaction, id);
+ RuntimeAddin addin = AddinEngine.GetAddin (transaction.GetAddinEngineTransaction(), id);
if (addin != null)
pinfo = addin.Addin;
else
@@ -1128,7 +1128,7 @@ namespace Mono.Addins
var addedNodes = new List<TreeNode> ();
tree.LoadExtension (transaction, node, addinId, extension, addedNodes);
- RuntimeAddin ad = AddinEngine.GetAddin (transaction, addinId);
+ RuntimeAddin ad = AddinEngine.GetAddin (transaction.GetAddinEngineTransaction(), addinId);
if (ad != null) {
foreach (TreeNode nod in addedNodes) {
// Don't call OnAddinLoaded here. Do it when the entire extension point has been loaded.
diff --git a/Mono.Addins/Mono.Addins/ExtensionContextTransaction.cs b/Mono.Addins/Mono.Addins/ExtensionContextTransaction.cs
index 62b8d52..ba2a245 100644
--- a/Mono.Addins/Mono.Addins/ExtensionContextTransaction.cs
+++ b/Mono.Addins/Mono.Addins/ExtensionContextTransaction.cs
@@ -55,15 +55,10 @@ namespace Mono.Addins
HashSet<string> extensionsChanged;
List<(TreeNode Node, BaseCondition Condition)> nodeConditions;
List<(TreeNode Node, BaseCondition Condition)> nodeConditionUnregistrations;
- List<KeyValuePair<string, string>> registeredAutoExtensionPoints;
- List<string> unregisteredAutoExtensionPoints;
- List<KeyValuePair<string, RuntimeAddin>> registeredAssemblyResolvePaths;
- List<string> unregisteredAssemblyResolvePaths;
- List<RuntimeAddin> addinLoadEvents;
- List<string> addinUnloadEvents;
List<TreeNode> treeNodeTransactions;
ExtensionContextSnapshot snapshot;
bool snaphotChanged;
+ AddinEngineTransaction nestedAddinEngineTransaction;
public ExtensionContextTransaction (ExtensionContext context)
{
@@ -80,7 +75,32 @@ namespace Mono.Addins
public ExtensionContextSnapshot Snapshot => snapshot;
- void EnsureNewSnapshot()
+ /// <summary>
+ /// Gets an add-in engine transaction, if there is one in progress. Returns null if there isn't one.
+ /// </summary>
+ public AddinEngineTransaction GetAddinEngineTransaction()
+ {
+ if (this is AddinEngineTransaction et)
+ return et;
+ if (nestedAddinEngineTransaction != null)
+ return nestedAddinEngineTransaction;
+ return null;
+ }
+
+ /// <summary>
+ /// Gets or creates an add-in engine transaction, which will be committed together with this transaction
+ /// </summary>
+ public AddinEngineTransaction GetOrCreateAddinEngineTransaction()
+ {
+ if (this is AddinEngineTransaction et)
+ return et;
+ if (nestedAddinEngineTransaction != null)
+ return nestedAddinEngineTransaction;
+
+ return nestedAddinEngineTransaction = Context.AddinEngine.BeginEngineTransaction();
+ }
+
+ protected void EnsureNewSnapshot()
{
if (!snaphotChanged)
{
@@ -93,41 +113,11 @@ namespace Mono.Addins
public void Dispose ()
{
- var engine = Context as AddinEngine;
-
try
{
// Update the context
- if (nodeConditions != null) {
- BulkRegisterNodeConditions (nodeConditions);
- }
-
- if (nodeConditionUnregistrations != null) {
- 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)
- {
- foreach (var node in treeNodeTransactions)
- node.CommitChildrenUpdateTransaction();
- }
+ UpdateSnapshot();
if (snaphotChanged)
Context.SetSnapshot(snapshot);
@@ -136,33 +126,54 @@ namespace Mono.Addins
Monitor.Exit (Context.LocalLock);
}
+ // If there is a nested engine transaction, make sure it is disposed
+ using var _ = nestedAddinEngineTransaction;
+
// Do notifications outside the lock
- if (loadedNodes != null) {
- foreach (var node in loadedNodes)
- node.NotifyAddinLoaded ();
+ DispatchNotifications();
+ }
+
+ protected virtual void UpdateSnapshot()
+ {
+ if (nodeConditions != null)
+ {
+ BulkRegisterNodeConditions(nodeConditions);
}
- if (childrenChanged != null) {
- foreach (var node in childrenChanged) {
- if (node.NotifyChildrenChanged ())
- NotifyExtensionsChangedEvent (node.GetPath ());
- }
+
+ if (nodeConditionUnregistrations != null)
+ {
+ BulkUnregisterNodeConditions(nodeConditionUnregistrations);
}
- if (extensionsChanged != null) {
- foreach (var path in extensionsChanged)
- Context.NotifyExtensionsChanged (new ExtensionEventArgs (path));
+
+ // Commit tree node transactions
+ if (treeNodeTransactions != null)
+ {
+ foreach (var node in treeNodeTransactions)
+ node.CommitChildrenUpdateTransaction();
}
+ }
- 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);
+ protected virtual void DispatchNotifications()
+ {
+ if (loadedNodes != null)
+ {
+ foreach (var node in loadedNodes)
+ node.NotifyAddinLoaded();
+ }
+ if (childrenChanged != null)
+ {
+ foreach (var node in childrenChanged)
+ {
+ if (node.NotifyChildrenChanged())
+ NotifyExtensionsChangedEvent(node.GetPath());
}
}
+ if (extensionsChanged != null)
+ {
+ foreach (var path in extensionsChanged)
+ Context.NotifyExtensionsChanged(new ExtensionEventArgs(path));
+ }
}
void BulkRegisterNodeConditions(IEnumerable<(TreeNode Node, BaseCondition Condition)> nodeConditions)
@@ -292,58 +303,107 @@ namespace Mono.Addins
nodeConditions.Remove ((node, cond));
}
- public void RegisterAutoTypeExtensionPoint (string typeName, string path)
+ public void RegisterChildrenUpdateTransaction (TreeNode node)
{
- EnsureNewSnapshot();
- if (registeredAutoExtensionPoints == null)
- registeredAutoExtensionPoints = new List<KeyValuePair<string, string>> ();
- registeredAutoExtensionPoints.Add (new KeyValuePair<string, string> (typeName, path));
+ if (treeNodeTransactions == null)
+ treeNodeTransactions = new List<TreeNode> ();
+ treeNodeTransactions.Add (node);
}
- public void UnregisterAutoTypeExtensionPoint (string typeName)
- {
- EnsureNewSnapshot();
- if (unregisteredAutoExtensionPoints == null)
- unregisteredAutoExtensionPoints = new List<string> ();
- unregisteredAutoExtensionPoints.Add (typeName);
- }
+ }
+
+ class AddinEngineTransaction : ExtensionContextTransaction
+ {
+ List<KeyValuePair<string, string>> registeredAutoExtensionPoints;
+ List<string> unregisteredAutoExtensionPoints;
+ List<KeyValuePair<string, RuntimeAddin>> registeredAssemblyResolvePaths;
+ List<string> unregisteredAssemblyResolvePaths;
+ List<RuntimeAddin> addinLoadEvents;
+ List<string> addinUnloadEvents;
+
+ public AddinEngineTransaction (AddinEngine engine) : base (engine)
+ {
+ }
- public void RegisterAssemblyResolvePaths (string assembly, RuntimeAddin addin)
+ 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));
+ registeredAssemblyResolvePaths = new List<KeyValuePair<string, RuntimeAddin>>();
+ registeredAssemblyResolvePaths.Add(new KeyValuePair<string, RuntimeAddin>(assembly, addin));
}
- public void UnregisterAssemblyResolvePaths (string assembly)
+ public void UnregisterAssemblyResolvePaths(string assembly)
{
EnsureNewSnapshot();
if (unregisteredAssemblyResolvePaths == null)
- unregisteredAssemblyResolvePaths = new List<string> ();
- unregisteredAssemblyResolvePaths.Add (assembly);
+ unregisteredAssemblyResolvePaths = new List<string>();
+ unregisteredAssemblyResolvePaths.Add(assembly);
}
- public void ReportAddinLoad (RuntimeAddin addin)
+ public void ReportAddinLoad(RuntimeAddin addin)
{
if (addinLoadEvents == null)
- addinLoadEvents = new List<RuntimeAddin> ();
- addinLoadEvents.Add (addin);
+ addinLoadEvents = new List<RuntimeAddin>();
+ addinLoadEvents.Add(addin);
}
- public void ReportAddinUnload (string id)
+ public void ReportAddinUnload(string id)
{
if (addinUnloadEvents == null)
- addinUnloadEvents = new List<string> ();
- addinUnloadEvents.Add (id);
+ addinUnloadEvents = new List<string>();
+ addinUnloadEvents.Add(id);
}
- public void RegisterChildrenUpdateTransaction (TreeNode node)
+ public void RegisterAutoTypeExtensionPoint(string typeName, string path)
{
- if (treeNodeTransactions == null)
- treeNodeTransactions = new List<TreeNode> ();
- treeNodeTransactions.Add (node);
+ EnsureNewSnapshot();
+ if (registeredAutoExtensionPoints == null)
+ registeredAutoExtensionPoints = new List<KeyValuePair<string, string>>();
+ registeredAutoExtensionPoints.Add(new KeyValuePair<string, string>(typeName, path));
+ }
+
+ public void UnregisterAutoTypeExtensionPoint(string typeName)
+ {
+ EnsureNewSnapshot();
+ if (unregisteredAutoExtensionPoints == null)
+ unregisteredAutoExtensionPoints = new List<string>();
+ unregisteredAutoExtensionPoints.Add(typeName);
}
+ protected override void UpdateSnapshot()
+ {
+ base.UpdateSnapshot();
+
+ 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);
+ }
+
+ protected override void DispatchNotifications()
+ {
+ base.DispatchNotifications();
+
+ var engine = (AddinEngine)Context;
+
+ if (addinLoadEvents != null)
+ {
+ foreach (var addin in addinLoadEvents)
+ engine.ReportAddinLoad(addin);
+ }
+ if (addinUnloadEvents != null)
+ {
+ foreach (var id in addinUnloadEvents)
+ engine.ReportAddinUnload(id);
+ }
+ }
}
}
diff --git a/Mono.Addins/Mono.Addins/ExtensionTree.cs b/Mono.Addins/Mono.Addins/ExtensionTree.cs
index 72f065d..117eafe 100644
--- a/Mono.Addins/Mono.Addins/ExtensionTree.cs
+++ b/Mono.Addins/Mono.Addins/ExtensionTree.cs
@@ -201,15 +201,16 @@ namespace Mono.Addins
bool InitializeNodeType (ExtensionNodeType ntype, ExtensionContextTransaction transaction)
{
- RuntimeAddin p = addinEngine.GetAddin(transaction, ntype.AddinId);
+ RuntimeAddin p = addinEngine.GetAddin(transaction.GetAddinEngineTransaction(), ntype.AddinId);
if (p == null)
{
- if (!addinEngine.LoadAddin(transaction, null, ntype.AddinId, false))
+ var engineTransaction = transaction.GetOrCreateAddinEngineTransaction();
+ if (!addinEngine.LoadAddin(engineTransaction, null, ntype.AddinId, false))
{
addinEngine.ReportError("Add-in not found", ntype.AddinId, null, false);
return false;
}
- p = addinEngine.GetAddin(transaction, ntype.AddinId);
+ p = addinEngine.GetAddin(engineTransaction, ntype.AddinId);
}
// If no type name is provided, use TypeExtensionNode by default
diff --git a/Mono.Addins/Mono.Addins/TreeNode.cs b/Mono.Addins/Mono.Addins/TreeNode.cs
index 0c0d992..6beab23 100644
--- a/Mono.Addins/Mono.Addins/TreeNode.cs
+++ b/Mono.Addins/Mono.Addins/TreeNode.cs
@@ -512,7 +512,7 @@ namespace Mono.Addins
{
if (extensionPoint != null) {
string aid = Addin.GetIdName (extensionPoint.ParentAddinDescription.AddinId);
- RuntimeAddin ad = addinEngine.GetAddin (transaction, aid);
+ RuntimeAddin ad = addinEngine.GetAddin (transaction.GetAddinEngineTransaction(), aid);
if (ad != null)
extensionPoint = ad.Addin.Description.ExtensionPoints [GetPath ()];
}
diff --git a/Test/UnitTests/TestMultithreading.cs b/Test/UnitTests/TestMultithreading.cs
index f827788..f4eb63a 100644
--- a/Test/UnitTests/TestMultithreading.cs
+++ b/Test/UnitTests/TestMultithreading.cs
@@ -87,94 +87,85 @@ namespace UnitTests
}
}
- int EnableDisableStress_totalAdd;
- int EnableDisableStress_totalRemove;
- int EnableDisableStress_nodesCount;
- int EnableDisableStress_minCount;
- int EnableDisableStress_maxCount;
-
[Test]
public void EventsMultithread()
{
int threads = 50;
- int addinsLoaded = 0;
- int addinsUnloaded = 0;
+ int totalAdded = 0;
+ int totalRemoved = 0;
+ int nodesCount = 0;
+ int minCount = 0;
+ int maxCount = 0;
var node = AddinManager.GetExtensionNode("/SimpleApp/Writers");
- try
- {
- EnableDisableStress_nodesCount = 0;
+ nodesCount = 0;
- node.ExtensionNodeChanged += Node_ExtensionNodeChanged;
+ node.ExtensionNodeChanged += (s, args) =>
+ {
+ if (args.Change == ExtensionChange.Add)
+ {
+ nodesCount++;
+ totalAdded++;
+ }
+ else
+ {
+ nodesCount--;
+ totalRemoved++;
+ }
- Assert.AreEqual(4, EnableDisableStress_nodesCount);
+ if (nodesCount < minCount)
+ minCount = nodesCount;
+ if (nodesCount > maxCount)
+ maxCount = nodesCount;
+ };
- EnableDisableStress_minCount = 4;
- EnableDisableStress_maxCount = 4;
- EnableDisableStress_totalAdd = 0;
- EnableDisableStress_totalRemove = 0;
+ Assert.AreEqual(4, nodesCount);
- var ainfo1 = AddinManager.Registry.GetAddin("SimpleApp.HelloWorldExtension");
- var ainfo2 = AddinManager.Registry.GetAddin("SimpleApp.FileContentExtension");
+ minCount = 4;
+ maxCount = 4;
+ totalAdded = 0;
+ totalRemoved = 0;
- AddinManager.AddinLoaded += (s,a) => addinsLoaded++;
- AddinManager.AddinUnloaded += (s,a) => addinsUnloaded++;
+ var ainfo1 = AddinManager.Registry.GetAddin("SimpleApp.HelloWorldExtension");
+ var ainfo2 = AddinManager.Registry.GetAddin("SimpleApp.FileContentExtension");
- using var enablers = new TestData(threads);
+ using var enablers = new TestData(threads);
- enablers.StartThreads((index, data) =>
+ enablers.StartThreads((index, data) =>
+ {
+ var random = new Random(10000 + index);
+ int iterations = 100;
+ while (--iterations > 0)
{
- var random = new Random(10000 + index);
- while (!data.Stopped)
+ var action = random.Next(4);
+ switch (action)
{
- var action = random.Next(4);
- switch (action)
- {
- case 0: ainfo1.Enabled = false; break;
- case 1: ainfo1.Enabled = true; break;
- case 2: ainfo2.Enabled = false; break;
- case 3: ainfo2.Enabled = true; break;
- }
+ case 0: ainfo1.Enabled = false; break;
+ case 1: ainfo1.Enabled = true; break;
+ case 2: ainfo2.Enabled = false; break;
+ case 3: ainfo2.Enabled = true; break;
}
- });
- Thread.Sleep(3000);
- }
- finally
- {
- node.ExtensionNodeChanged -= Node_ExtensionNodeChanged;
- }
-
- // If all events have been sent correctly, the node count should have never gone below 2 and over 4.
+ }
+ data.Counters[index] = 1;
+ });
- Assert.That(EnableDisableStress_minCount, Is.AtLeast(2));
- Assert.That(EnableDisableStress_maxCount, Is.AtMost(4));
+ // Wait for the threads to do the work. 5 seconds should be enough
+ enablers.CheckCounters(1, 5000);
- // Every time one of these add-ins is enabled, a new node is added (likewise when removed), so
- // the total count of nodes added must match the number of times the addins were enabled.
+ // Go back to the initial status
- Assert.AreEqual(EnableDisableStress_totalAdd, addinsLoaded);
- Assert.AreEqual(EnableDisableStress_totalRemove, addinsUnloaded);
- }
+ ainfo1.Enabled = true;
+ ainfo2.Enabled = true;
- private void Node_ExtensionNodeChanged (object sender, ExtensionNodeEventArgs args)
- {
- if (args.Change == ExtensionChange.Add)
- {
- EnableDisableStress_nodesCount++;
- EnableDisableStress_totalAdd++;
- }
- else
- {
- EnableDisableStress_nodesCount--;
- EnableDisableStress_totalRemove++;
- }
+ // If all events have been sent correctly, the node count should have never gone below 2 and over 4.
- if (EnableDisableStress_nodesCount < EnableDisableStress_minCount)
- EnableDisableStress_minCount = EnableDisableStress_nodesCount;
- if (EnableDisableStress_nodesCount > EnableDisableStress_maxCount)
- EnableDisableStress_maxCount = EnableDisableStress_nodesCount;
+ Assert.That(nodesCount, Is.EqualTo(4));
+ Assert.That(totalAdded, Is.AtLeast(100));
+ Assert.That(totalAdded, Is.EqualTo(totalRemoved));
+ Assert.That(minCount, Is.AtLeast(2));
+ Assert.That(maxCount, Is.AtMost(4));
}
[Test]
diff --git a/Test/UnitTests/UnitTests.csproj b/Test/UnitTests/UnitTests.csproj
index aae8ac1..d7f43ca 100644
--- a/Test/UnitTests/UnitTests.csproj
+++ b/Test/UnitTests/UnitTests.csproj
@@ -30,6 +30,7 @@
<Reference Include="System.Core" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
+ <PackageReference Include="Microsoft.Net.Test.Sdk" Version="17.3.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Mono.Addins\Mono.Addins.csproj" />