This is an automated email from the ASF dual-hosted git repository.
paulirwin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/lucenenet.git
The following commit(s) were added to refs/heads/master by this push:
new b1d309fc6 Add unit tests for LimitedConcurrencyLevelTaskScheduler,
#1110 (#1119)
b1d309fc6 is described below
commit b1d309fc657b2ae7bb20fa3e0ab5300351b52f17
Author: Paul Irwin <[email protected]>
AuthorDate: Wed Jan 22 13:27:04 2025 -0700
Add unit tests for LimitedConcurrencyLevelTaskScheduler, #1110 (#1119)
* Add unit tests for LimitedConcurrencyLevelTaskScheduler, #1110
* Move TaskState into Extensions class and make private, #1110
---
Directory.Build.targets | 1 -
.../Support/Threading/JSR166TestCase.cs | 344 +++++++++++++++------
.../TestLimitedConcurrencyLevelTaskScheduler.cs | 237 ++++++++++++++
.../LimitedConcurrencyLevelTaskScheduler.cs | 98 +++---
4 files changed, 535 insertions(+), 145 deletions(-)
diff --git a/Directory.Build.targets b/Directory.Build.targets
index d08b29eb3..85fdcc8f0 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -112,7 +112,6 @@
<DefineConstants>$(DefineConstants);FEATURE_ASSEMBLY_GETCALLINGASSEMBLY</DefineConstants>
<DefineConstants>$(DefineConstants);FEATURE_FILESTREAM_LOCK</DefineConstants>
<DefineConstants>$(DefineConstants);FEATURE_TEXTWRITER_CLOSE</DefineConstants>
-
<DefineConstants>$(DefineConstants);FEATURE_THREADPOOL_UNSAFEQUEUEWORKITEM</DefineConstants>
<DefineConstants>$(DefineConstants);FEATURE_TYPE_GETMETHOD__BINDINGFLAGS_PARAMS</DefineConstants>
</PropertyGroup>
diff --git a/src/Lucene.Net.Tests/Support/Threading/JSR166TestCase.cs
b/src/Lucene.Net.Tests/Support/Threading/JSR166TestCase.cs
index 990b2cd35..e892d8854 100644
--- a/src/Lucene.Net.Tests/Support/Threading/JSR166TestCase.cs
+++ b/src/Lucene.Net.Tests/Support/Threading/JSR166TestCase.cs
@@ -1,5 +1,15 @@
-using Lucene.Net.Util;
+// From Apache Harmony tests:
+//
https://github.com/apache/harmony/blob/trunk/classlib/modules/concurrent/src/test/java/JSR166TestCase.java
+using Lucene.Net.Util;
using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using System.Threading.Tasks;
+using ThreadInterruptedException = System.Threading.ThreadInterruptedException;
+
+#nullable enable
namespace Lucene.Net.Support.Threading
{
@@ -20,82 +30,90 @@ namespace Lucene.Net.Support.Threading
* limitations under the License.
*/
- /**
- * Base class for JSR166 Junit TCK tests. Defines some constants,
- * utility methods and classes, as well as a simple framework for
- * helping to make sure that assertions failing in generated threads
- * cause the associated test that generated them to itself fail (which
- * JUnit does not otherwise arrange). The rules for creating such
- * tests are:
- *
- * <ol>
- *
- * <li> All assertions in code running in generated threads must use
- * the forms {@link #threadFail}, {@link #threadAssertTrue}, {@link
- * #threadAssertEquals}, or {@link #threadAssertNull}, (not
- * <tt>fail</tt>, <tt>assertTrue</tt>, etc.) It is OK (but not
- * particularly recommended) for other code to use these forms too.
- * Only the most typically used JUnit assertion methods are defined
- * this way, but enough to live with.</li>
- *
- * <li> If you override {@link #setUp} or {@link #tearDown}, make sure
- * to invoke <tt>super.setUp</tt> and <tt>super.tearDown</tt> within
- * them. These methods are used to clear and check for thread
- * assertion failures.</li>
- *
- * <li>All delays and timeouts must use one of the constants <tt>
- * SHORT_DELAY_MS</tt>, <tt> SMALL_DELAY_MS</tt>, <tt>
MEDIUM_DELAY_MS</tt>,
- * <tt> LONG_DELAY_MS</tt>. The idea here is that a SHORT is always
- * discriminable from zero time, and always allows enough time for the
- * small amounts of computation (creating a thread, calling a few
- * methods, etc) needed to reach a timeout point. Similarly, a SMALL
- * is always discriminable as larger than SHORT and smaller than
- * MEDIUM. And so on. These constants are set to conservative values,
- * but even so, if there is ever any doubt, they can all be increased
- * in one spot to rerun tests on slower platforms.</li>
- *
- * <li> All threads generated must be joined inside each test case
- * method (or <tt>fail</tt> to do so) before returning from the
- * method. The <tt> joinPool</tt> method can be used to do this when
- * using Executors.</li>
- *
- * </ol>
- *
- * <p> <b>Other notes</b>
- * <ul>
- *
- * <li> Usually, there is one testcase method per JSR166 method
- * covering "normal" operation, and then as many exception-testing
- * methods as there are exceptions the method can throw. Sometimes
- * there are multiple tests per JSR166 method when the different
- * "normal" behaviors differ significantly. And sometimes testcases
- * cover multiple methods when they cannot be tested in
- * isolation.</li>
- *
- * <li> The documentation style for testcases is to provide as javadoc
- * a simple sentence or two describing the property that the testcase
- * method purports to test. The javadocs do not say anything about how
- * the property is tested. To find out, read the code.</li>
- *
- * <li> These tests are "conformance tests", and do not attempt to
- * test throughput, latency, scalability or other performance factors
- * (see the separate "jtreg" tests for a set intended to check these
- * for the most central aspects of functionality.) So, most tests use
- * the smallest sensible numbers of threads, collection sizes, etc
- * needed to check basic conformance.</li>
- *
- * <li>The test classes currently do not declare inclusion in
- * any particular package to simplify things for people integrating
- * them in TCK test suites.</li>
- *
- * <li> As a convenience, the <tt>main</tt> of this class (JSR166TestCase)
- * runs all JSR166 unit tests.</li>
- *
- * </ul>
- */
+ /// <summary>
+ /// LUCENENET NOTE: This class has been adapted from the Apache Harmony
+ /// tests. The original javadoc is included below, and adapted where
necessary.
+ /// <para />
+ ///
+ /// Base class for JSR166 Junit TCK tests. Defines some constants,
+ /// utility methods and classes, as well as a simple framework for
+ /// helping to make sure that assertions failing in generated threads
+ /// cause the associated test that generated them to itself fail (which
+ /// JUnit does not otherwise arrange). The rules for creating such
+ /// tests are:
+ ///
+ /// <list type="bullets">
+ ///
+ /// <item> All assertions in code running in generated threads must use
+ /// the forms <see cref="threadFail"/>, <see cref="threadAssertTrue"/>,
+ /// <see cref="threadAssertEquals(long,long)"/>, <see
cref="threadAssertEquals(object,object)"/>
+ /// or <see cref="threadAssertNull"/>, (not
+ /// <c>fail</c>, <c>assertTrue</c>, etc.) It is OK (but not
+ /// particularly recommended) for other code to use these forms too.
+ /// Only the most typically used JUnit assertion methods are defined
+ /// this way, but enough to live with.</item>
+ ///
+ /// <item> If you override <see cref="SetUp"/> or <see cref="TearDown"/>,
make sure
+ /// to invoke <c>base.SetUp</c> and <c>base.TearDown</c> within
+ /// them. These methods are used to clear and check for thread
+ /// assertion failures.</item>
+ ///
+ /// <item>All delays and timeouts must use one of the constants
+ /// <see cref="SHORT_DELAY_MS"/>, <see cref="SMALL_DELAY_MS"/>, <see
cref="MEDIUM_DELAY_MS"/>,
+ /// <see cref="LONG_DELAY_MS"/>. The idea here is that a SHORT is always
+ /// discriminable from zero time, and always allows enough time for the
+ /// small amounts of computation (creating a thread, calling a few
+ /// methods, etc) needed to reach a timeout point. Similarly, a SMALL
+ /// is always discriminable as larger than SHORT and smaller than
+ /// MEDIUM. And so on. These constants are set to conservative values,
+ /// but even so, if there is ever any doubt, they can all be increased
+ /// in one spot to rerun tests on slower platforms.</item>
+ ///
+ /// <item> All threads generated must be joined inside each test case
+ /// method (or <c>fail</c> to do so) before returning from the
+ /// method. The <see cref="joinPool"/> method can be used to do this when
+ /// using Executors.</item>
+ ///
+ /// </list>
+ ///
+ /// <para />
+ /// <b>Other notes</b>
+ /// <list type="bullet">
+ ///
+ /// <item> Usually, there is one testcase method per JSR166 method
+ /// covering "normal" operation, and then as many exception-testing
+ /// methods as there are exceptions the method can throw. Sometimes
+ /// there are multiple tests per JSR166 method when the different
+ /// "normal" behaviors differ significantly. And sometimes testcases
+ /// cover multiple methods when they cannot be tested in
+ /// isolation.</item>
+ ///
+ /// <item> The documentation style for testcases is to provide as javadoc
+ /// a simple sentence or two describing the property that the testcase
+ /// method purports to test. The javadocs do not say anything about how
+ /// the property is tested. To find out, read the code.</item>
+ ///
+ /// <item> These tests are "conformance tests", and do not attempt to
+ /// test throughput, latency, scalability or other performance factors
+ /// (see the separate "jtreg" tests for a set intended to check these
+ /// for the most central aspects of functionality.) So, most tests use
+ /// the smallest sensible numbers of threads, collection sizes, etc
+ /// needed to check basic conformance.</item>
+ ///
+ /// <item>The test classes currently do not declare inclusion in
+ /// any particular package to simplify things for people integrating
+ /// them in TCK test suites.</item>
+ ///
+ /// <!-- LUCENENET: not implemented
+ /// <item> As a convenience, the <c>main</c> of this class (JSR166TestCase)
+ /// runs all JSR166 unit tests.</item>
+ /// -->
+ ///
+ /// </list>
+ /// </summary>
public class JSR166TestCase : LuceneTestCase
{
- ///**
+ // /**
// * Runs all JSR166 unit tests using junit.textui.TestRunner
// */
//public static void main(String[] args)
@@ -255,7 +273,7 @@ public void threadAssertFalse(bool b)
* If argument not null, set status to indicate current testcase
* should fail
*/
- public void threadAssertNull(object x)
+ public void threadAssertNull(object? x)
{
if (x != null)
{
@@ -281,7 +299,7 @@ public void threadAssertEquals(long x, long y)
* If arguments not equal, set status to indicate current testcase
* should fail
*/
- public void threadAssertEquals(object x, object y)
+ public void threadAssertEquals(object? x, object? y)
{
if (x != y && (x == null || !x.equals(y)))
{
@@ -326,25 +344,25 @@ public void threadUnexpectedException(Exception ex)
fail("Unexpected exception: " + ex);
}
- ///**
- // * Wait out termination of a thread pool or fail doing so
- // */
- //public void joinPool(ExecutorService exec)
- //{
- // try
- // {
- // exec.shutdown();
- // assertTrue(exec.awaitTermination(LONG_DELAY_MS,
TimeUnit.MILLISECONDS));
- // }
- // catch (SecurityException ok)
- // {
- // // Allowed in case test doesn't have privs
- // }
- // catch (InterruptedException ie)
- // {
- // fail("Unexpected exception");
- // }
- //}
+ /**
+ * Wait out termination of a thread pool or fail doing so
+ */
+ public void joinPool(TaskScheduler exec)
+ {
+ try
+ {
+ exec.Shutdown();
+
assertTrue(exec.AwaitTermination(TimeSpan.FromMilliseconds(LONG_DELAY_MS)));
+ }
+ // catch (SecurityException ok) // LUCENENET - not needed
+ // {
+ // // Allowed in case test doesn't have privs
+ // }
+ catch (ThreadInterruptedException /*ie*/)
+ {
+ fail("Unexpected exception");
+ }
+ }
/**
@@ -363,7 +381,141 @@ public void unexpectedException()
fail("Unexpected exception");
}
+ internal void ShortRunnable()
+ {
+ try
+ {
+ Thread.Sleep(SHORT_DELAY_MS);
+ }
+ catch (Exception e)
+ {
+ threadUnexpectedException(e);
+ }
+ }
+
+ internal void MediumRunnable()
+ {
+ try
+ {
+ Thread.Sleep(MEDIUM_DELAY_MS);
+ }
+ catch (Exception e)
+ {
+ threadUnexpectedException(e);
+ }
+ }
// LUCENENET TODO: Complete port
}
+
+ /// <summary>
+ /// LUCENENET specific - fake support for an API that feels like
ThreadPoolExecutor.
+ /// </summary>
+ internal static class JSR166TestCaseExtensions
+ {
+ /// <summary>
+ /// LUCENENET specific - state to keep track of tasks.
+ /// <see cref="LimitedConcurrencyLevelTaskScheduler"/> removes tasks
from the list when they complete,
+ /// so this class is needed to keep track of them.
+ /// </summary>
+ private class TaskState
+ {
+ private readonly TaskFactory _factory;
+ private readonly List<Task> _tasks = new();
+
+ public TaskState(TaskScheduler scheduler)
+ {
+ _factory = new TaskFactory(scheduler);
+ }
+
+ public void NewTask(Action action)
+ {
+ var task = _factory.StartNew(action);
+ _tasks.Add(task);
+ }
+
+ public int ActiveCount => _tasks.Count(t => t.Status ==
TaskStatus.Running);
+
+ public int CompletedCount => _tasks.Count(t => t.IsCompleted);
+
+ public int TaskCount => _tasks.Count;
+
+ public bool AllCompleted => _tasks.All(t => t.IsCompleted);
+
+ public bool JoinAll(TimeSpan timeout) =>
Task.WhenAll(_tasks).Wait(timeout);
+ }
+
+ private static readonly ConditionalWeakTable<TaskScheduler, TaskState>
_taskFactories = new();
+
+ public static void Execute(this TaskScheduler scheduler, Action action)
+ {
+ if (!_taskFactories.TryGetValue(scheduler, out TaskState? state))
+ {
+ state = new TaskState(scheduler);
+ _taskFactories.Add(scheduler, state);
+ }
+
+ state.NewTask(action);
+ }
+
+ public static bool AwaitTermination(this TaskScheduler scheduler,
TimeSpan timeout)
+ {
+ if (_taskFactories.TryGetValue(scheduler, out TaskState? state))
+ {
+ return state.JoinAll(timeout);
+ }
+
+ return true;
+ }
+
+ public static int GetActiveCount(this TaskScheduler scheduler)
+ {
+ if (_taskFactories.TryGetValue(scheduler, out TaskState? state))
+ {
+ // Approximate the number of running threads, which shouldn't
exceed the concurrency level
+ return Math.Min(scheduler.MaximumConcurrencyLevel,
state.ActiveCount);
+ }
+
+ return 0;
+ }
+
+ public static int GetCompletedTaskCount(this TaskScheduler scheduler)
+ {
+ if (_taskFactories.TryGetValue(scheduler, out TaskState? state))
+ {
+ return state.CompletedCount;
+ }
+
+ return 0;
+ }
+
+ public static int GetTaskCount(this TaskScheduler scheduler)
+ {
+ if (_taskFactories.TryGetValue(scheduler, out TaskState? state))
+ {
+ return state.TaskCount;
+ }
+
+ return 0;
+ }
+
+ public static void Shutdown(this TaskScheduler scheduler)
+ {
+ if (scheduler is LimitedConcurrencyLevelTaskScheduler lcl)
+ {
+ lcl.Shutdown();
+ }
+ }
+
+ public static bool IsTerminated(this TaskScheduler scheduler)
+ {
+ if (scheduler is LimitedConcurrencyLevelTaskScheduler lcl
+ && _taskFactories.TryGetValue(scheduler, out TaskState? state))
+ {
+ return lcl.IsShutdown && state.AllCompleted;
+ }
+
+ return false; // can't be shut down, so can't be terminated
+ }
+ }
}
diff --git
a/src/Lucene.Net.Tests/Support/Threading/TestLimitedConcurrencyLevelTaskScheduler.cs
b/src/Lucene.Net.Tests/Support/Threading/TestLimitedConcurrencyLevelTaskScheduler.cs
new file mode 100644
index 000000000..2952ce259
--- /dev/null
+++
b/src/Lucene.Net.Tests/Support/Threading/TestLimitedConcurrencyLevelTaskScheduler.cs
@@ -0,0 +1,237 @@
+// Based on tests from Apache Harmony:
+//
https://github.com/apache/harmony/blob/02970cb7227a335edd2c8457ebdde0195a735733/classlib/modules/concurrent/src/test/java/ThreadPoolExecutorTest.java
+
+using NUnit.Framework;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+#nullable enable
+
+namespace Lucene.Net.Support.Threading
+{
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ /// <summary>
+ /// Tests for <see cref="LimitedConcurrencyLevelTaskScheduler"/>.
+ /// Adapted from Apache Harmony test suite <c>ThreadPoolExecutorTest</c>.
+ /// </summary>
+ [TestFixture]
+ public class TestLimitedConcurrencyLevelTaskScheduler : JSR166TestCase
+ {
+ /// <summary>
+ /// execute successfully executes a runnable
+ /// </summary>
+ /// <remarks>
+ /// LUCENENET Note: Execute is provided in <see
cref="JSR166TestCaseExtensions"/>
+ /// to emulate the behavior of the Java method; it is not in the public
+ /// API for <see cref="LimitedConcurrencyLevelTaskScheduler"/>. This
+ /// just helps ensure the class is working as expected.
+ /// </remarks>
+ [Test]
+ public void TestExecute()
+ {
+ TaskScheduler p1 = new LimitedConcurrencyLevelTaskScheduler(1);
+
+ try
+ {
+ p1.Execute(() =>
+ {
+ try
+ {
+ Thread.Sleep(SHORT_DELAY_MS);
+ }
+ catch (ThreadInterruptedException /*e*/)
+ {
+ threadUnexpectedException();
+ }
+ });
+ Thread.Sleep(SMALL_DELAY_MS);
+ }
+ catch (ThreadInterruptedException /*e*/)
+ {
+ unexpectedException();
+ }
+
+ joinPool(p1);
+ }
+
+ /// <summary>
+ /// getActiveCount increases but doesn't overestimate, when a
+ /// thread becomes active
+ /// </summary>
+ /// <remarks>
+ /// LUCENENET Note: GetActiveCount is provided in <see
cref="JSR166TestCaseExtensions"/>
+ /// to emulate the behavior of the Java method; it is not in the public
+ /// API for <see cref="LimitedConcurrencyLevelTaskScheduler"/>. This
+ /// just helps ensure the class is working as expected.
+ /// </remarks>
+ [Test]
+ public void TestGetActiveCount()
+ {
+ TaskScheduler p2 = new LimitedConcurrencyLevelTaskScheduler(2);
+ assertEquals(0, p2.GetActiveCount());
+ p2.Execute(MediumRunnable);
+
+ try
+ {
+ Thread.Sleep(SHORT_DELAY_MS);
+ }
+ catch (Exception /*e*/)
+ {
+ unexpectedException();
+ }
+
+ assertEquals(1, p2.GetActiveCount());
+ joinPool(p2);
+ }
+
+ // LUCENENET NOTE: testPrestartCoreThread and
testPrestartAllCoreThreads omitted; they are not relevant
+
+ /// <summary>
+ /// getCompletedTaskCount increases, but doesn't overestimate,
+ /// when tasks complete
+ /// </summary>
+ /// <remarks>
+ /// LUCENENET Note: GetCompletedTaskCount is provided in <see
cref="JSR166TestCaseExtensions"/>
+ /// to emulate the behavior of the Java method; it is not in the public
+ /// API for <see cref="LimitedConcurrencyLevelTaskScheduler"/>. This
+ /// just helps ensure the class is working as expected.
+ /// </remarks>
+ [Test]
+ public void TestGetCompletedTaskCount()
+ {
+ TaskScheduler p2 = new LimitedConcurrencyLevelTaskScheduler(2);
+ assertEquals(0, p2.GetCompletedTaskCount());
+ p2.Execute(ShortRunnable);
+
+ try
+ {
+ Thread.Sleep(SMALL_DELAY_MS);
+ }
+ catch (Exception /*e*/)
+ {
+ unexpectedException();
+ }
+
+ assertEquals(1, p2.GetCompletedTaskCount());
+ // LUCENENET NOTE: not catching SecurityException because that's
not relevant here
+ p2.Shutdown();
+ joinPool(p2);
+ }
+
+ /// <summary>
+ /// Tests <see
cref="LimitedConcurrencyLevelTaskScheduler.MaximumConcurrencyLevel"/>
+ /// returns size given in constructor if not otherwise set
+ /// </summary>
+ /// <remarks>
+ /// LUCENENET Note: this is equivalent to the
<c>testGetCorePoolSize</c> or
+ /// <c>testGetMaximumPoolSize</c> methods in the Harmony tests, but we
don't
+ /// have the same concepts or distinction, so just testing to make
+ /// sure that the maximum concurrency level is set correctly.
+ /// </remarks>
+ [Test]
+ public void TestMaximumConcurrencyLevel()
+ {
+ TaskScheduler p1 = new LimitedConcurrencyLevelTaskScheduler(1);
+ assertEquals(1, p1.MaximumConcurrencyLevel);
+ joinPool(p1);
+ }
+
+ // LUCENENET NOTE: testGetKeepAliveTime, testGetThreadFactory,
testSetThreadFactory,
+ // testSetThreadFactoryNull, testGetRejectedExecutionHandler,
testSetRejectedExecutionHandler,
+ // testSetRejectedExecutionHandlerNull, testGetLargestPoolSize, and
testGetPoolSize omitted; they are not relevant
+
+ /// <summary>
+ /// getTaskCount increases, but doesn't overestimate, when tasks
submitted
+ /// </summary>
+ /// <remarks>
+ /// LUCENENET Note: GetTaskCount is provided in <see
cref="JSR166TestCaseExtensions"/>
+ /// to emulate the behavior of the Java method; it is not in the public
+ /// API for <see cref="LimitedConcurrencyLevelTaskScheduler"/>. This
+ /// just helps ensure the class is working as expected.
+ /// </remarks>
+ [Test]
+ public void TestGetTaskCount()
+ {
+ TaskScheduler p1 = new LimitedConcurrencyLevelTaskScheduler(1);
+
+ try
+ {
+ assertEquals(0, p1.GetTaskCount());
+ p1.Execute(MediumRunnable);
+ Thread.Sleep(SHORT_DELAY_MS);
+ assertEquals(1, p1.GetTaskCount());
+ }
+ catch (Exception /*e*/)
+ {
+ unexpectedException();
+ }
+
+ joinPool(p1);
+ }
+
+ /// <summary>
+ /// <see cref="LimitedConcurrencyLevelTaskScheduler.IsShutdown"/> is
false before shutdown, true after
+ /// </summary>
+ [Test]
+ public void TestIsShutdown()
+ {
+ var p1 = new LimitedConcurrencyLevelTaskScheduler(1);
+ assertFalse(p1.IsShutdown);
+ p1.Shutdown(); // LUCENENET NOTE: not catching SecurityException
because that's not relevant here
+ assertTrue(p1.IsShutdown);
+ joinPool(p1);
+ }
+
+ /// <summary>
+ /// isTerminated is false before termination, true after
+ /// </summary>
+ /// <remarks>
+ /// LUCENENET Note: IsTerminated is provided in <see
cref="JSR166TestCaseExtensions"/>
+ /// to emulate the behavior of the Java method; it is not in the public
+ /// API for <see cref="LimitedConcurrencyLevelTaskScheduler"/>. This
+ /// just helps ensure the class is working as expected.
+ /// </remarks>
+ [Test]
+ public void TestIsTerminated()
+ {
+ TaskScheduler p1 = new LimitedConcurrencyLevelTaskScheduler(1);
+ assertFalse(p1.IsTerminated());
+
+ try
+ {
+ p1.Execute(MediumRunnable);
+ }
+ finally
+ {
+ p1.Shutdown(); // LUCENENET NOTE: not catching
SecurityException because that's not relevant here
+ }
+
+ try
+ {
+
assertTrue(p1.AwaitTermination(TimeSpan.FromMilliseconds(LONG_DELAY_MS)));
+ assertTrue(p1.IsTerminated());
+ }
+ catch (Exception /*e*/)
+ {
+ unexpectedException();
+ }
+ }
+
+ // LUCENENET NOTE: remainder of methods omitted, could be added as
needed.
+ }
+}
diff --git
a/src/Lucene.Net/Support/Threading/LimitedConcurrencyLevelTaskScheduler.cs
b/src/Lucene.Net/Support/Threading/LimitedConcurrencyLevelTaskScheduler.cs
index 0717e957f..07b236cea 100644
--- a/src/Lucene.Net/Support/Threading/LimitedConcurrencyLevelTaskScheduler.cs
+++ b/src/Lucene.Net/Support/Threading/LimitedConcurrencyLevelTaskScheduler.cs
@@ -1,50 +1,50 @@
/*
MICROSOFT LIMITED PUBLIC LICENSE version 1.1
-This license governs use of code marked as "sample" or "example" available on
this web site
-without a license agreement, as provided under the section above titled
-"NOTICE SPECIFIC TO SOFTWARE AVAILABLE ON THIS WEB SITE." If you use such
-code (the "software"), you accept this license. If you do not accept the
+This license governs use of code marked as "sample" or "example" available on
this web site
+without a license agreement, as provided under the section above titled
+"NOTICE SPECIFIC TO SOFTWARE AVAILABLE ON THIS WEB SITE." If you use such
+code (the "software"), you accept this license. If you do not accept the
license, do not use the software.
1. Definitions
-The terms "reproduce," "reproduction," "derivative works," and "distribution"
have the
+The terms "reproduce," "reproduction," "derivative works," and "distribution"
have the
same meaning here as under U.S. copyright law.
A "contribution" is the original software, or any additions or changes to the
software.
A "contributor" is any person that distributes its contribution under this
license.
"Licensed patents" are a contributor’s patent claims that read directly on its
contribution.
2. Grant of Rights
-(A) Copyright Grant - Subject to the terms of this license, including the
license conditions
-and limitations in section 3, each contributor grants you a non-exclusive,
worldwide,
-royalty-free copyright license to reproduce its contribution, prepare
derivative works
+(A) Copyright Grant - Subject to the terms of this license, including the
license conditions
+and limitations in section 3, each contributor grants you a non-exclusive,
worldwide,
+royalty-free copyright license to reproduce its contribution, prepare
derivative works
of its contribution, and distribute its contribution or any derivative works
that you create.
-(B) Patent Grant - Subject to the terms of this license, including the license
conditions
-and limitations in section 3, each contributor grants you a non-exclusive,
worldwide,
-royalty-free license under its licensed patents to make, have made, use, sell,
-offer for sale, import, and/or otherwise dispose of its contribution in the
+(B) Patent Grant - Subject to the terms of this license, including the license
conditions
+and limitations in section 3, each contributor grants you a non-exclusive,
worldwide,
+royalty-free license under its licensed patents to make, have made, use, sell,
+offer for sale, import, and/or otherwise dispose of its contribution in the
software or derivative works of the contribution in the software.
3. Conditions and Limitations
-(A) No Trademark License- This license does not grant you rights to use any
contributors’
+(A) No Trademark License- This license does not grant you rights to use any
contributors’
name, logo, or trademarks.
-(B) If you bring a patent claim against any contributor over patents that you
claim are
-infringed by the software, your patent license from such contributor to the
software
+(B) If you bring a patent claim against any contributor over patents that you
claim are
+infringed by the software, your patent license from such contributor to the
software
ends automatically.
-(C) If you distribute any portion of the software, you must retain all
copyright, patent,
+(C) If you distribute any portion of the software, you must retain all
copyright, patent,
trademark, and attribution notices that are present in the software.
-(D) If you distribute any portion of the software in source code form, you may
do so only
-under this license by including a complete copy of this license with your
distribution.
-If you distribute any portion of the software in compiled or object code form,
you may
+(D) If you distribute any portion of the software in source code form, you may
do so only
+under this license by including a complete copy of this license with your
distribution.
+If you distribute any portion of the software in compiled or object code form,
you may
only do so under a license that complies with this license.
-(E) The software is licensed "as-is." You bear the risk of using it. The
contributors
-give no express warranties, guarantees or conditions. You may have additional
consumer
-rights under your local laws which this license cannot change. To the extent
permitted
-under your local laws, the contributors exclude the implied warranties of
merchantability,
+(E) The software is licensed "as-is." You bear the risk of using it. The
contributors
+give no express warranties, guarantees or conditions. You may have additional
consumer
+rights under your local laws which this license cannot change. To the extent
permitted
+under your local laws, the contributors exclude the implied warranties of
merchantability,
fitness for a particular purpose and non-infringement.
-(F) Platform Limitation - The licenses granted in sections 2(A) and 2(B)
extend only
-to the software or derivative works that you create that run directly on a
Microsoft
-Windows operating system product, Microsoft run-time technology (such as the
.NET
-Framework or Silverlight), or Microsoft application platform (such as
Microsoft
+(F) Platform Limitation - The licenses granted in sections 2(A) and 2(B)
extend only
+to the software or derivative works that you create that run directly on a
Microsoft
+Windows operating system product, Microsoft run-time technology (such as the
.NET
+Framework or Silverlight), or Microsoft application platform (such as Microsoft
Office or Microsoft Dynamics).
*/
@@ -57,9 +57,9 @@ ends automatically.
namespace Lucene.Net.Support.Threading
{
/// <summary>
- /// Provides a task scheduler that ensures a maximum concurrency level
while
+ /// Provides a task scheduler that ensures a maximum concurrency level
while
/// running on top of the thread pool.
- ///
+ ///
/// Source:
https://msdn.microsoft.com/en-us/library/system.threading.tasks.taskscheduler(v=vs.110).aspx
/// </summary>
internal class LimitedConcurrencyLevelTaskScheduler : TaskScheduler
@@ -70,29 +70,29 @@ internal class LimitedConcurrencyLevelTaskScheduler :
TaskScheduler
[ThreadStatic]
private static bool _currentThreadIsProcessingItems;
- // The list of tasks to be executed
+ // The list of tasks to be executed
private readonly LinkedList<Task> _tasks = new LinkedList<Task>(); //
protected by lock(_tasks)
- // The maximum concurrency level allowed by this scheduler.
+ // The maximum concurrency level allowed by this scheduler.
private readonly int _maxDegreeOfParallelism;
- // Indicates whether the scheduler is currently processing work items.
+ // Indicates whether the scheduler is currently processing work items.
private int _delegatesQueuedOrRunning = 0;
- // Creates a new instance with the specified degree of parallelism.
+ // Creates a new instance with the specified degree of parallelism.
public LimitedConcurrencyLevelTaskScheduler(int maxDegreeOfParallelism)
{
if (maxDegreeOfParallelism < 1) throw new
ArgumentOutOfRangeException(nameof(maxDegreeOfParallelism));
_maxDegreeOfParallelism = maxDegreeOfParallelism;
}
- // Queues a task to the scheduler.
+ // Queues a task to the scheduler.
protected sealed override void QueueTask(Task task)
{
// Don't queue any more work.
if (shutDown) return;
- // Add the task to the list of tasks to be processed. If there
aren't enough
+ // Add the task to the list of tasks to be processed. If there
aren't enough
// delegates currently queued or running to process tasks,
schedule another.
UninterruptableMonitor.Enter(_tasks);
try
@@ -110,15 +110,10 @@ protected sealed override void QueueTask(Task task)
}
}
- // Inform the ThreadPool that there's work to be executed for this
scheduler.
+ // Inform the ThreadPool that there's work to be executed for this
scheduler.
private void NotifyThreadPoolOfPendingWork()
{
-#if FEATURE_THREADPOOL_UNSAFEQUEUEWORKITEM
- ThreadPool.UnsafeQueueUserWorkItem(
-#else
- ThreadPool.QueueUserWorkItem(
-#endif
- _ =>
+ ThreadPool.UnsafeQueueUserWorkItem(_ =>
{
// Note that the current thread is now processing work items.
// This is necessary to enable inlining of tasks into this
thread.
@@ -158,7 +153,7 @@ private void NotifyThreadPoolOfPendingWork()
}, null);
}
- // Attempts to execute the specified task on the current thread.
+ // Attempts to execute the specified task on the current thread.
protected sealed override bool TryExecuteTaskInline(Task task, bool
taskWasPreviouslyQueued)
{
// If this thread isn't already processing a task, we don't
support inlining
@@ -166,7 +161,7 @@ protected sealed override bool TryExecuteTaskInline(Task
task, bool taskWasPrevi
// If the task was previously queued, remove it from the queue
if (taskWasPreviouslyQueued)
- // Try to run the task.
+ // Try to run the task.
if (TryDequeue(task))
return base.TryExecuteTask(task);
else
@@ -175,7 +170,7 @@ protected sealed override bool TryExecuteTaskInline(Task
task, bool taskWasPrevi
return base.TryExecuteTask(task);
}
- // Attempt to remove a previously scheduled task from the scheduler.
+ // Attempt to remove a previously scheduled task from the scheduler.
protected sealed override bool TryDequeue(Task task)
{
UninterruptableMonitor.Enter(_tasks);
@@ -189,10 +184,10 @@ protected sealed override bool TryDequeue(Task task)
}
}
- // Gets the maximum concurrency level supported by this scheduler.
+ // Gets the maximum concurrency level supported by this scheduler.
public sealed override int MaximumConcurrencyLevel =>
_maxDegreeOfParallelism;
- // Gets an enumerable of the tasks currently scheduled on this
scheduler.
+ // Gets an enumerable of the tasks currently scheduled on this
scheduler.
protected sealed override IEnumerable<Task> GetScheduledTasks()
{
bool lockTaken = false;
@@ -208,10 +203,17 @@ protected sealed override IEnumerable<Task>
GetScheduledTasks()
}
}
- // Stops this TaskScheduler from queuing new tasks.
+ /// <summary>
+ /// Stops this TaskScheduler from queuing new tasks.
+ /// </summary>
public void Shutdown()
{
shutDown.Value = true;
}
+
+ /// <summary>
+ /// Gets a value indicating whether this TaskScheduler has been shut
down.
+ /// </summary>
+ public bool IsShutdown => shutDown;
}
}