diff --git a/Directory.Build.targets b/Directory.Build.targets
index d08b29eb3e..85fdcc8f0d 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -112,7 +112,6 @@
$(DefineConstants);FEATURE_ASSEMBLY_GETCALLINGASSEMBLY
$(DefineConstants);FEATURE_FILESTREAM_LOCK
$(DefineConstants);FEATURE_TEXTWRITER_CLOSE
- $(DefineConstants);FEATURE_THREADPOOL_UNSAFEQUEUEWORKITEM
$(DefineConstants);FEATURE_TYPE_GETMETHOD__BINDINGFLAGS_PARAMS
diff --git a/src/Lucene.Net.Tests/Support/Threading/JSR166TestCase.cs b/src/Lucene.Net.Tests/Support/Threading/JSR166TestCase.cs
index 990b2cd357..e892d8854b 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:
- *
- *
- *
- * - All assertions in code running in generated threads must use
- * the forms {@link #threadFail}, {@link #threadAssertTrue}, {@link
- * #threadAssertEquals}, or {@link #threadAssertNull}, (not
- * fail, assertTrue, 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.
- *
- * - If you override {@link #setUp} or {@link #tearDown}, make sure
- * to invoke super.setUp and super.tearDown within
- * them. These methods are used to clear and check for thread
- * assertion failures.
- *
- * - All delays and timeouts must use one of the constants
- * SHORT_DELAY_MS, SMALL_DELAY_MS, MEDIUM_DELAY_MS,
- * 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.
- *
- * - All threads generated must be joined inside each test case
- * method (or fail to do so) before returning from the
- * method. The joinPool method can be used to do this when
- * using Executors.
- *
- *
- *
- * Other notes
- *
- *
- * - 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.
- *
- * - 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.
- *
- * - 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.
- *
- * - The test classes currently do not declare inclusion in
- * any particular package to simplify things for people integrating
- * them in TCK test suites.
- *
- * - As a convenience, the main of this class (JSR166TestCase)
- * runs all JSR166 unit tests.
- *
- *
- */
+ ///
+ /// LUCENENET NOTE: This class has been adapted from the Apache Harmony
+ /// tests. The original javadoc is included below, and adapted where necessary.
+ ///
+ ///
+ /// 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:
+ ///
+ ///
+ ///
+ /// - All assertions in code running in generated threads must use
+ /// the forms , ,
+ /// ,
+ /// or , (not
+ /// fail, assertTrue, 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.
+ ///
+ /// - If you override or , make sure
+ /// to invoke base.SetUp and base.TearDown within
+ /// them. These methods are used to clear and check for thread
+ /// assertion failures.
+ ///
+ /// - All delays and timeouts must use one of the constants
+ /// , , ,
+ /// . 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.
+ ///
+ /// - All threads generated must be joined inside each test case
+ /// method (or fail to do so) before returning from the
+ /// method. The method can be used to do this when
+ /// using Executors.
+ ///
+ ///
+ ///
+ ///
+ /// Other notes
+ ///
+ ///
+ /// - 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.
+ ///
+ /// - 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.
+ ///
+ /// - 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.
+ ///
+ /// - The test classes currently do not declare inclusion in
+ /// any particular package to simplify things for people integrating
+ /// them in TCK test suites.
+ ///
+ ///
+ ///
+ ///
+ ///
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
}
+
+ ///
+ /// LUCENENET specific - fake support for an API that feels like ThreadPoolExecutor.
+ ///
+ internal static class JSR166TestCaseExtensions
+ {
+ ///
+ /// LUCENENET specific - state to keep track of tasks.
+ /// removes tasks from the list when they complete,
+ /// so this class is needed to keep track of them.
+ ///
+ private class TaskState
+ {
+ private readonly TaskFactory _factory;
+ private readonly List _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 _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 0000000000..2952ce2590
--- /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.
+ */
+ ///
+ /// Tests for .
+ /// Adapted from Apache Harmony test suite ThreadPoolExecutorTest.
+ ///
+ [TestFixture]
+ public class TestLimitedConcurrencyLevelTaskScheduler : JSR166TestCase
+ {
+ ///
+ /// execute successfully executes a runnable
+ ///
+ ///
+ /// LUCENENET Note: Execute is provided in
+ /// to emulate the behavior of the Java method; it is not in the public
+ /// API for . This
+ /// just helps ensure the class is working as expected.
+ ///
+ [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);
+ }
+
+ ///
+ /// getActiveCount increases but doesn't overestimate, when a
+ /// thread becomes active
+ ///
+ ///
+ /// LUCENENET Note: GetActiveCount is provided in
+ /// to emulate the behavior of the Java method; it is not in the public
+ /// API for . This
+ /// just helps ensure the class is working as expected.
+ ///
+ [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
+
+ ///
+ /// getCompletedTaskCount increases, but doesn't overestimate,
+ /// when tasks complete
+ ///
+ ///
+ /// LUCENENET Note: GetCompletedTaskCount is provided in
+ /// to emulate the behavior of the Java method; it is not in the public
+ /// API for . This
+ /// just helps ensure the class is working as expected.
+ ///
+ [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);
+ }
+
+ ///
+ /// Tests
+ /// returns size given in constructor if not otherwise set
+ ///
+ ///
+ /// LUCENENET Note: this is equivalent to the testGetCorePoolSize or
+ /// testGetMaximumPoolSize 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.
+ ///
+ [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
+
+ ///
+ /// getTaskCount increases, but doesn't overestimate, when tasks submitted
+ ///
+ ///
+ /// LUCENENET Note: GetTaskCount is provided in
+ /// to emulate the behavior of the Java method; it is not in the public
+ /// API for . This
+ /// just helps ensure the class is working as expected.
+ ///
+ [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);
+ }
+
+ ///
+ /// is false before shutdown, true after
+ ///
+ [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);
+ }
+
+ ///
+ /// isTerminated is false before termination, true after
+ ///
+ ///
+ /// LUCENENET Note: IsTerminated is provided in
+ /// to emulate the behavior of the Java method; it is not in the public
+ /// API for . This
+ /// just helps ensure the class is working as expected.
+ ///
+ [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 0717e957f6..07b236ceae 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 @@ to the software or derivative works that you create that run directly on a Micro
namespace Lucene.Net.Support.Threading
{
///
- /// 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
///
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 _tasks = new LinkedList(); // 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 GetScheduledTasks()
{
bool lockTaken = false;
@@ -208,10 +203,17 @@ protected sealed override IEnumerable GetScheduledTasks()
}
}
- // Stops this TaskScheduler from queuing new tasks.
+ ///
+ /// Stops this TaskScheduler from queuing new tasks.
+ ///
public void Shutdown()
{
shutDown.Value = true;
}
+
+ ///
+ /// Gets a value indicating whether this TaskScheduler has been shut down.
+ ///
+ public bool IsShutdown => shutDown;
}
}