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-06-16 13:51:43 +0300
committerLluis Sanchez <llsan@microsoft.com>2022-09-13 20:38:42 +0300
commit9ac75d4641bb7b58075c9d6fc8ac92dbb32a4fbf (patch)
treeb5169d113971893cb02d0e6dd09093716e7bf41e
parent6f0d811b6823b378d1db44f5d2bb6aab7d684a83 (diff)
Add multi-threading tests
And fixed threading issue.
-rw-r--r--Mono.Addins/Mono.Addins/TreeNode.cs13
-rw-r--r--Test/UnitTests/TestMultithreading.cs195
-rw-r--r--Test/UnitTests/UnitTests.csproj1
3 files changed, 205 insertions, 4 deletions
diff --git a/Mono.Addins/Mono.Addins/TreeNode.cs b/Mono.Addins/Mono.Addins/TreeNode.cs
index 3a79e49..1c60176 100644
--- a/Mono.Addins/Mono.Addins/TreeNode.cs
+++ b/Mono.Addins/Mono.Addins/TreeNode.cs
@@ -33,6 +33,7 @@ using System.Collections;
using Mono.Addins.Description;
using System.Collections.Generic;
using System.Collections.Immutable;
+using System.Threading;
namespace Mono.Addins
{
@@ -70,7 +71,11 @@ namespace Mono.Addins
internal void AttachExtensionNode (ExtensionNode enode)
{
- this.extensionNode = enode;
+ if (Interlocked.CompareExchange (ref extensionNode, enode, null) != null) {
+ // Another thread already assigned the node
+ return;
+ }
+
if (extensionNode != null)
extensionNode.SetTreeNode (this);
}
@@ -82,9 +87,9 @@ namespace Mono.Addins
public ExtensionNode ExtensionNode {
get {
if (extensionNode == null && extensionPoint != null) {
- extensionNode = new ExtensionNode ();
- extensionNode.SetData (addinEngine, extensionPoint.RootAddin, null, null);
- AttachExtensionNode (extensionNode);
+ var newNode = new ExtensionNode ();
+ newNode.SetData (addinEngine, extensionPoint.RootAddin, null, null);
+ AttachExtensionNode (newNode);
}
return extensionNode;
}
diff --git a/Test/UnitTests/TestMultithreading.cs b/Test/UnitTests/TestMultithreading.cs
new file mode 100644
index 0000000..fd3cbcf
--- /dev/null
+++ b/Test/UnitTests/TestMultithreading.cs
@@ -0,0 +1,195 @@
+
+using System;
+using System.IO;
+using NUnit.Framework;
+using Mono.Addins;
+using SimpleApp;
+using System.Threading;
+using System.Diagnostics;
+using System.Linq;
+
+namespace UnitTests
+{
+ [TestFixture ()]
+ public class TestMultiThreading: TestBase
+ {
+ public override void Setup ()
+ {
+ base.Setup ();
+ GlobalInfoCondition.Value = "res";
+ }
+
+ [Test]
+ public void ChildConditionAttributeMultithread ()
+ {
+ int threads = 50;
+ using var testData = new TestData(threads);
+
+ GlobalInfoCondition.Value = "";
+
+ testData.StartThreads (RunChildConditionAttributeTest);
+
+ for (int n = 0; n < 20; n++) {
+ Console.WriteLine ("Step " + n);
+ testData.CheckCounters (0, 10000);
+
+ GlobalInfoCondition.Value = "foo";
+
+ testData.CheckCounters (1, 1000);
+
+ GlobalInfoCondition.Value = "";
+ }
+ }
+
+ void RunChildConditionAttributeTest (int index, TestData data)
+ {
+ while (!data.Stopped) {
+ var writers = AddinManager.GetExtensionObjects<IWriter> ("/SimpleApp/ConditionedWriters");
+ data.Counters [index] = writers.Length;
+ }
+ }
+
+ [Test]
+ public void EnableDisableMultithread ()
+ {
+ int threads = 50;
+ int steps = 20;
+
+ using var testData = new TestData (threads);
+
+ testData.StartThreads ((index, data) => {
+ while (!data.Stopped) {
+ var writers = AddinManager.GetExtensionObjects<IWriter> ("/SimpleApp/Writers");
+ testData.Counters [index] = writers.Length;
+ }
+ });
+
+ for (int n = 0; n < steps; n++) {
+ Console.WriteLine ("Step " + n);
+
+ testData.CheckCounters (4, 10000);
+
+ var ainfo1 = AddinManager.Registry.GetAddin ("SimpleApp.HelloWorldExtension");
+ ainfo1.Enabled = false;
+
+ testData.CheckCounters (3, 1000);
+
+ var ainfo2 = AddinManager.Registry.GetAddin ("SimpleApp.FileContentExtension");
+ ainfo2.Enabled = false;
+
+ testData.CheckCounters (2, 1000);
+
+ ainfo1.Enabled = true;
+ ainfo2.Enabled = true;
+ }
+ }
+
+ [Test]
+ public void EventsMultithread ()
+ {
+ int threads = 50;
+ int steps = 20;
+
+ var testData = new TestData (threads);
+
+ GlobalInfoCondition.Value = "";
+
+ testData.StartThreads (RunEventsMultithread);
+
+ for (int n = 0; n < steps; n++) {
+ Console.WriteLine ("Step " + n);
+ testData.CheckCounters (0, 10000);
+
+ GlobalInfoCondition.Value = "foo";
+
+ testData.CheckCounters (1, 1000);
+
+ GlobalInfoCondition.Value = "";
+ }
+ }
+
+ void RunEventsMultithread (int index, TestData data)
+ {
+ while (!data.Stopped) {
+ int count = 0;
+ ExtensionNodeEventHandler handler = (s, args) => {
+ count++;
+ };
+ AddinManager.AddExtensionNodeHandler("/SimpleApp/ConditionedWriters", handler);
+ data.Counters [index] = count;
+ AddinManager.RemoveExtensionNodeHandler ("/SimpleApp/ConditionedWriters", handler);
+ }
+ }
+
+ [Test]
+ public void ChildConditionWithContextMultithread ()
+ {
+ int threads = 50;
+ using var testData = new TestData (threads);
+
+ GlobalInfoCondition.Value = "";
+
+ testData.StartThreads (RunChildConditionWithContextMultithread);
+
+ for (int n = 0; n < 20; n++) {
+ Console.WriteLine ("Step " + n);
+ testData.CheckCounters (0, 10000);
+
+ GlobalInfoCondition.Value = "foo";
+
+ testData.CheckCounters (1, 1000);
+
+ GlobalInfoCondition.Value = "";
+ }
+ }
+
+ void RunChildConditionWithContextMultithread (int index, TestData data)
+ {
+ var ctx = AddinManager.CreateExtensionContext ();
+ var writers = ctx.GetExtensionNode ("/SimpleApp/ConditionedWriters");
+ while (!data.Stopped) {
+ data.Counters [index] = writers.ChildNodes.Count;
+ }
+ }
+ class TestData: IDisposable
+ {
+ int numThreads;
+
+ public bool Stopped;
+ public int [] Counters;
+
+ public TestData (int numThreads)
+ {
+ this.numThreads = numThreads;
+ Counters = new int [numThreads];
+ }
+
+ public void CheckCounters (int expectedResult, int timeout)
+ {
+ var sw = Stopwatch.StartNew ();
+ do {
+ if (Counters.All (c => c == expectedResult))
+ return;
+ Thread.Sleep (10);
+ } while (sw.ElapsedMilliseconds < timeout);
+
+ Assert.Fail ();
+ }
+
+ public void Dispose ()
+ {
+ Stopped = true;
+ }
+
+ public void StartThreads (Action<int, TestData> threadAction)
+ {
+ for (int n = 0; n < numThreads; n++) {
+ Counters [n] = -1;
+ var index = n;
+ var t = new Thread (() => threadAction(index, this));
+ t.Start ();
+ }
+ }
+ }
+ }
+}
diff --git a/Test/UnitTests/UnitTests.csproj b/Test/UnitTests/UnitTests.csproj
index 28d5659..aae8ac1 100644
--- a/Test/UnitTests/UnitTests.csproj
+++ b/Test/UnitTests/UnitTests.csproj
@@ -13,6 +13,7 @@
<SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>..\..\mono-addins.snk</AssemblyOriginatorKeyFile>
<TargetFrameworks>net472;net6.0</TargetFrameworks>
+ <LangVersion>9.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>True</DebugSymbols>