diff --git a/README.md b/README.md index 54c1ab5..aa94395 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/sheinbergon/needle/multi-platform-ci?logo=github&style=for-the-badge)](https://github.com/sheinbergon/needle/actions?query=workflow%3Amulti-platform-ci) [![GitHub release (latest by date)](https://img.shields.io/github/v/release/sheinbergon/needle?color=%2340E0D0&logo=github&style=for-the-badge)](https://github.com/sheinbergon/needle/releases/latest) [![Maven Central](https://img.shields.io/maven-central/v/org.sheinbergon/needle-core?color=Crimson&logo=Apache%20Maven&style=for-the-badge)](https://search.maven.org/search?q=g:org.sheinbergon%20a:needle*) +[![Snyk](https://img.shields.io/snyk/vulnerabilities/github/sheinbergon/needle?color=%2340E0D0&label=Snyk&logo=snyk&logoColor=silver&style=for-the-badge)](https://snyk.io/test/github/sheinbergon/needle?targetFile=build.gradle) + **Needle** provides feature rich core affinity support for the JVM, focusing on ease of integration and extensibility diff --git a/agent/src/main/java/org/sheinbergon/needle/agent/NeedleAgentConfiguration.java b/agent/src/main/java/org/sheinbergon/needle/agent/NeedleAgentConfiguration.java index 3d99e0b..5e8c278 100644 --- a/agent/src/main/java/org/sheinbergon/needle/agent/NeedleAgentConfiguration.java +++ b/agent/src/main/java/org/sheinbergon/needle/agent/NeedleAgentConfiguration.java @@ -26,12 +26,53 @@ public class NeedleAgentConfiguration { */ public static final NeedleAgentConfiguration DEFAULT = new NeedleAgentConfiguration() .defaultAffinity(AffinityDescriptor.from(NumberUtils.LONG_ZERO)); + /** + * A collection of affinity group descriptors used to match different affinity descriptors to threads upon + * instantiation. + */ + @Nullable + private List affinityGroups; + /** + * The default affinity to use for all threads without a precise {@link AffinityGroup} match. + */ + @Nonnull + private AffinityDescriptor defaultAffinity; @Data @NoArgsConstructor @Accessors(fluent = true, chain = true) public static final class AffinityGroup { + /** + * This group inflated {@link AffinityDescriptor}, to be used to apply affinity settings via {@code Needle}. + * + * @see org.sheinbergon.needle.Needle + */ + @Nonnull + private AffinityDescriptor affinity; + /** + * The match target qualifier, used to extract the match target string from a given {@code Thread}. + */ + @Nullable + private Qualifier qualifier; + /** + * The matching logic encapsulation (determined upon deserialization). + */ + @Nullable + private Matcher matcher; + /** + * The affinity group's identifier, meant to be used for descriptive purposes only. + */ + @Nonnull + private String identifier; + + /** + * @param target the match target to be matched using this affinity group's {@link AffinityGroup#matcher}. + * @return A boolean value indicating whether or not this group matches the given target or not. + */ + public boolean matches(final @Nonnull String target) { + return matcher.matches(target); + } public enum Qualifier { /** @@ -53,6 +94,13 @@ public enum Qualifier { }) public interface Matcher { + /** + * @param target the match target to be matched + * @return Boolean value indicating whether or not this {@code Matcher} implementation matches the given + * match target. + */ + boolean matches(@Nonnull String target); + @Data @NoArgsConstructor @Accessors(fluent = true, chain = true) @@ -88,57 +136,6 @@ public boolean matches(final @Nonnull String target) { return pattern.matcher(target).matches(); } } - - /** - * @param target the match target to be matched - * @return Boolean value indicating whether or not this {@code Matcher} implementation matches the given - * match target. - */ - boolean matches(@Nonnull String target); - } - - /** - * This group inflated {@link AffinityDescriptor}, to be used to apply affinity settings via {@code Needle}. - * - * @see org.sheinbergon.needle.Needle - */ - @Nonnull - private AffinityDescriptor affinity; - /** - * The match target qualifier, used to extract the match target string from a given {@code Thread}. - */ - @Nullable - private Qualifier qualifier; - /** - * The matching logic encapsulation (determined upon deserialization). - */ - @Nullable - private Matcher matcher; - /** - * The affinity group's identifier, meant to be used for descriptive purposes only. - */ - @Nonnull - private String identifier; - - /** - * @param target the match target to be matched using this affinity group's {@link AffinityGroup#matcher}. - * @return A boolean value indicating whether or not this group matches the given target or not. - */ - public boolean matches(final @Nonnull String target) { - return matcher.matches(target); } } - - /** - * A collection of affinity group descriptors used to match different affinity descriptors to threads upon - * instantiation. - */ - @Nullable - private List affinityGroups; - - /** - * The default affinity to use for all threads without a precise {@link AffinityGroup} match. - */ - @Nonnull - private AffinityDescriptor defaultAffinity; } diff --git a/agent/src/main/java/org/sheinbergon/needle/agent/util/AffinityGroupMatcher.java b/agent/src/main/java/org/sheinbergon/needle/agent/util/AffinityGroupMatcher.java index 7b613b8..9109e54 100644 --- a/agent/src/main/java/org/sheinbergon/needle/agent/util/AffinityGroupMatcher.java +++ b/agent/src/main/java/org/sheinbergon/needle/agent/util/AffinityGroupMatcher.java @@ -59,6 +59,9 @@ public final class AffinityGroupMatcher { */ private static NeedleAgentConfiguration.AffinityGroup defaultAffinityGroup = null; + private AffinityGroupMatcher() { + } + /** * Match an affintiy group for a given {@code Thread} according to the following logic *

@@ -141,7 +144,4 @@ private static List affinityGroups( final @Nonnull NeedleAgentConfiguration configuration) { return ObjectUtils.defaultIfNull(configuration.affinityGroups(), List.of()); } - - private AffinityGroupMatcher() { - } } diff --git a/agent/src/main/java/org/sheinbergon/needle/agent/util/YamlCodec.java b/agent/src/main/java/org/sheinbergon/needle/agent/util/YamlCodec.java index 2f1bf2d..7815ac1 100644 --- a/agent/src/main/java/org/sheinbergon/needle/agent/util/YamlCodec.java +++ b/agent/src/main/java/org/sheinbergon/needle/agent/util/YamlCodec.java @@ -36,6 +36,9 @@ public final class YamlCodec { .addMixIn(Pattern.class, RegexPatternMixIn.class) .readerFor(NeedleAgentConfiguration.class); + private YamlCodec() { + } + /** * @param url Agent configuration file URL. * @return Deserialized {@link NeedleAgentConfiguration} instance. @@ -89,7 +92,4 @@ public Pattern deserialize( return Pattern.compile(node.asText()); } } - - private YamlCodec() { - } } diff --git a/agent/src/test/kotlin/org/sheinbergon/needle/agent/NeedleAgentTest.kt b/agent/src/test/kotlin/org/sheinbergon/needle/agent/NeedleAgentTest.kt index 9c2535b..7a3fcd9 100644 --- a/agent/src/test/kotlin/org/sheinbergon/needle/agent/NeedleAgentTest.kt +++ b/agent/src/test/kotlin/org/sheinbergon/needle/agent/NeedleAgentTest.kt @@ -4,7 +4,12 @@ import com.sun.tools.attach.VirtualMachine import org.amshove.kluent.shouldBeEqualTo import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import org.sheinbergon.needle.* +import org.sheinbergon.needle.AffinityDescriptor +import org.sheinbergon.needle.Needle +import org.sheinbergon.needle.PinnedThread +import org.sheinbergon.needle.default +import org.sheinbergon.needle.`1L` +import org.sheinbergon.needle.`2L` import org.sheinbergon.needle.util.NeedleAffinity import java.nio.file.Paths @@ -14,8 +19,8 @@ class AffinityAgentTest { private val AGENT_PATH = System.getProperty("test.agent.jar.path")!! private val CONFIGURATION_PATH = AffinityAgentTest::class.java - .getResource("/test-configuration.yml") - .toString() + .getResource("/test-configuration.yml") + .toString() private const val THREAD_NAME_PREFIX = "needle-agent-thread" diff --git a/build.gradle b/build.gradle index 64aa227..042c33b 100644 --- a/build.gradle +++ b/build.gradle @@ -31,7 +31,7 @@ allprojects { ext.nexus.url = nexus.target.equals('SNAPSHOT') ? ossSnapshotsRepositoryUrl : ossReleasesRepositoryUrl group "org.sheinbergon" - version "0.2.1" + version "0.3.0" sourceCompatibility = 11 diff --git a/codecov.yml b/codecov.yml index 082fa07..777ac56 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,6 +1,15 @@ coverage: precision: 2 round: up - range: "75..95" + range: 75..95 + status: + patch: + default: + target: auto + threshold: 2% + project: + default: + target: auto + threshold: 2% ignore: - "agent/**/*" \ No newline at end of file diff --git a/concurrent/src/main/java/org/sheinbergon/needle/concurrent/PinnedExecutors.java b/concurrent/src/main/java/org/sheinbergon/needle/concurrent/PinnedExecutors.java new file mode 100644 index 0000000..d3ec2c3 --- /dev/null +++ b/concurrent/src/main/java/org/sheinbergon/needle/concurrent/PinnedExecutors.java @@ -0,0 +1,72 @@ +package org.sheinbergon.needle.concurrent; + +import javax.annotation.Nonnull; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ScheduledExecutorService; + +public final class PinnedExecutors { + + /** + * Static factory methods for affinity aware single-thread {@code ExecutorService} inception. + * + * @param factory the {@code PinnedThreadFactory} used to create affinity aware {@code PinnedThread} instances + * @return the affinity aware {@code ExecutorService} + */ + public static ExecutorService newSinglePinnedThreadExecutor(final @Nonnull PinnedThreadFactory factory) { + return Executors.newSingleThreadExecutor(factory); + } + + /** + * Static factory methods for affinity aware fixed-size {@code ExecutorService} inception. + * + * @param size number of {@code PinnedThread} instances to maintain in the pool + * @param factory the {@code PinnedThreadFactory} used create affinity aware {@code PinnedThread} instances + * @return the affinity aware {@code ExecutorService} + */ + public static ExecutorService newFixedPinnedThreadPool(final int size, final @Nonnull PinnedThreadFactory factory) { + return Executors.newFixedThreadPool(size, factory); + } + + /** + * Static factory methods for affinity aware single-thread {@code ScheduledExecutorService} inception. + * + * @param factory the {@code PinnedThreadFactory} used create affinity aware {@code PinnedThread} instances + * @return the affinity aware {@code ScheduledExecutorService} + */ + public static ScheduledExecutorService newSinglePinnedThreadScheduledExecutor( + final @Nonnull PinnedThreadFactory factory) { + return Executors.newSingleThreadScheduledExecutor(factory); + } + + /** + * Static factory methods for affinity aware fixed-size {@code ScheduledExecutorService} inception. + * + * @param size number of {@code PinnedThread} instances to maintain in the pool + * @param factory the {@code PinnedThreadFactory} used create affinity aware {@code PinnedThread} instances + * @return the affinity aware {@code ScheduledExecutorService} + */ + public static ScheduledExecutorService newScheduledPinnedThreadPool( + final int size, + final @Nonnull PinnedThreadFactory factory) { + return Executors.newScheduledThreadPool(size, factory); + } + + /** + * Creates a new affinity aware {@code ForkJoinPool} with the given parameters. + * + * @param parallelism the parallelism level (amount of worker threads to be spawned) + * @param factory the {@code PinnedThread} factory to use when the executor + * creates new fork-join worker threads + * @return an affinity aware {@code ForkJoinPool} + */ + public static ForkJoinPool newPinnedWorkStealingPool( + final int parallelism, + final @Nonnull PinnedThreadFactory factory) { + return new ForkJoinPool(parallelism, factory, null, true); + } + + private PinnedExecutors() { + } +} diff --git a/concurrent/src/main/java/org/sheinbergon/needle/concurrent/PinnedForkJoinPool.java b/concurrent/src/main/java/org/sheinbergon/needle/concurrent/PinnedForkJoinPool.java deleted file mode 100644 index 86f669b..0000000 --- a/concurrent/src/main/java/org/sheinbergon/needle/concurrent/PinnedForkJoinPool.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.sheinbergon.needle.concurrent; - -import javax.annotation.Nonnull; -import java.io.Closeable; -import java.util.concurrent.ForkJoinPool; - -public final class PinnedForkJoinPool extends ForkJoinPool implements Closeable { - - /** - * Creates a new affinity aware {@code PinnedForkJoinPool} with the given initial - * parameters. - * - * @param parallelism the parallelism level (amount of worker threads to be spawned) - * @param factory the {@code PinnedThread} factory to use when the executor - * creates new fork-join worker threads - */ - public PinnedForkJoinPool( - final int parallelism, - final @Nonnull PinnedThreadFactory factory) { - super(parallelism, factory, null, false); - } - - @Override - public void close() { - shutdown(); - } -} diff --git a/concurrent/src/main/java/org/sheinbergon/needle/concurrent/PinnedThreadPoolExecutor.java b/concurrent/src/main/java/org/sheinbergon/needle/concurrent/PinnedThreadPoolExecutor.java deleted file mode 100644 index 4db9281..0000000 --- a/concurrent/src/main/java/org/sheinbergon/needle/concurrent/PinnedThreadPoolExecutor.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.sheinbergon.needle.concurrent; - -import org.apache.commons.lang3.math.NumberUtils; - -import javax.annotation.Nonnull; -import java.io.Closeable; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -public final class PinnedThreadPoolExecutor extends ThreadPoolExecutor implements Closeable { - - /** - * Creates a new affinity aware {@code PinnedThreadPoolExecutor} with the given initial - * parameters. - * - * @param corePoolSize the number of {@code PinnedThread} instances to keep in the pool, - * even if they are idle, unless {@code allowCoreThreadTimeOut} - * is set - * @param maximumPoolSize the maximum number of {@code PinnedThread} instances to allow in the - * pool - * @param keepAliveTime when the number of threads is greater than - * the core, this is the maximum time that excess idle threads - * will wait for new tasks before terminating. - * @param unit the time unit for the {@code keepAliveTime} argument - * @param workQueue the queue to use for holding tasks before they are - * executed. This queue will hold only the {@code Runnable} - * tasks submitted by the {@code execute} method. - * @param factory the {@code PinnedThreadFactory} used create affinity aware {@code PinnedThread} instances - */ - public PinnedThreadPoolExecutor( - final int corePoolSize, - final int maximumPoolSize, - final long keepAliveTime, - final @Nonnull TimeUnit unit, - final @Nonnull BlockingQueue workQueue, - final @Nonnull PinnedThreadFactory factory) { - super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, factory); - } - - /** - * Static factory methods for affinity aware single-thread {@code ExecutorService} inception. - * - * @param factory the {@code PinnedThreadFactory} used to create affinity aware {@code PinnedThread} instances - * @return the affinity aware {@code ExecutorService} - */ - public static ExecutorService newSinglePinnedThreadExecutor(final @Nonnull PinnedThreadFactory factory) { - return new PinnedThreadPoolExecutor(NumberUtils.INTEGER_ONE, - NumberUtils.INTEGER_ONE, - NumberUtils.LONG_ZERO, - TimeUnit.MILLISECONDS, - new LinkedBlockingQueue<>(), - factory); - } - - /** - * Static factory methods for affinity aware fixed-size {@code ExecutorService} inception. - * - * @param size number of {@code PinnedThread} instances to maintain in the pool - * @param factory the {@code PinnedThreadFactory} used create affinity aware {@code PinnedThread} instances - * @return the affinity aware {@code ExecutorService} - */ - public static ExecutorService newFixedPinnedThreadPool(final int size, final @Nonnull PinnedThreadFactory factory) { - return new PinnedThreadPoolExecutor(size, - size, - NumberUtils.LONG_ZERO, - TimeUnit.MILLISECONDS, - new LinkedBlockingQueue<>(), - factory); - } - - @Override - public void close() { - shutdown(); - } -} diff --git a/concurrent/src/main/java/org/sheinbergon/needle/concurrent/ScheduledPinnedThreadPoolExecutor.java b/concurrent/src/main/java/org/sheinbergon/needle/concurrent/ScheduledPinnedThreadPoolExecutor.java deleted file mode 100644 index 92cb18c..0000000 --- a/concurrent/src/main/java/org/sheinbergon/needle/concurrent/ScheduledPinnedThreadPoolExecutor.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.sheinbergon.needle.concurrent; - -import org.apache.commons.lang3.math.NumberUtils; - -import javax.annotation.Nonnull; -import java.io.Closeable; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledThreadPoolExecutor; - -public final class ScheduledPinnedThreadPoolExecutor extends ScheduledThreadPoolExecutor implements Closeable { - - - /** - * Creates a new {@code ScheduledPinnedThreadPoolExecutor} with the - * given initial parameters. - * - * @param size the number of {@code PinnedThread} instances to keep in the pool - * @param factory the factory to use when the executor creates a new thread - */ - public ScheduledPinnedThreadPoolExecutor( - final int size, - final @Nonnull PinnedThreadFactory factory) { - super(size, factory); - } - - /** - * Static factory methods for affinity aware single-thread {@code ScheduledExecutorService} inception. - * - * @param factory the {@code PinnedThreadFactory} used create affinity aware {@code PinnedThread} instances - * @return the affinity aware {@code ScheduledExecutorService} - */ - public static ScheduledExecutorService newSinglePinnedThreadScheduledExecutor( - final @Nonnull PinnedThreadFactory factory) { - return new ScheduledPinnedThreadPoolExecutor(NumberUtils.INTEGER_ONE, factory); - } - - /** - * Static factory methods for affinity aware fixed-size {@code ScheduledExecutorService} inception. - * - * @param size number of {@code PinnedThread} instances to maintain in the pool - * @param factory the {@code PinnedThreadFactory} used create affinity aware {@code PinnedThread} instances - * @return the affinity aware {@code ScheduledExecutorService} - */ - public static ScheduledExecutorService newScheduledPinnedThreadPool( - final int size, - final @Nonnull PinnedThreadFactory factory) { - return new ScheduledPinnedThreadPoolExecutor(size, factory); - } - - @Override - public void close() { - shutdown(); - } -} diff --git a/concurrent/src/test/kotlin/org/sheinbergon/needle/concurrent/FixedAffinityPinnedThreadFactoryTest.kt b/concurrent/src/test/kotlin/org/sheinbergon/needle/concurrent/FixedAffinityPinnedThreadFactoryTest.kt index 7cfe7c3..622e11f 100644 --- a/concurrent/src/test/kotlin/org/sheinbergon/needle/concurrent/FixedAffinityPinnedThreadFactoryTest.kt +++ b/concurrent/src/test/kotlin/org/sheinbergon/needle/concurrent/FixedAffinityPinnedThreadFactoryTest.kt @@ -3,7 +3,11 @@ package org.sheinbergon.needle.concurrent import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldBeTrue import org.junit.jupiter.api.Test -import org.sheinbergon.needle.* +import org.sheinbergon.needle.`1` +import org.sheinbergon.needle.Pinned +import org.sheinbergon.needle.binaryTestMask +import org.sheinbergon.needle.testAffinityDescriptor +import org.sheinbergon.needle.textTestMask import java.util.concurrent.CountDownLatch import java.util.concurrent.RecursiveAction @@ -25,7 +29,7 @@ class FixedAffinityPinnedThreadFactoryTest { private fun testPinnedForkJoinWorkerThreadInception(factory: PinnedThreadFactory) { val latch = CountDownLatch(`1`) - PinnedForkJoinPool(`1`, factory).use { + PinnedExecutors.newPinnedWorkStealingPool(`1`, factory).let { val action = action(latch) it.submit(action) latch.await() diff --git a/concurrent/src/test/kotlin/org/sheinbergon/needle/concurrent/GovernedAffinityPinnedThreadFactoryTest.kt b/concurrent/src/test/kotlin/org/sheinbergon/needle/concurrent/GovernedAffinityPinnedThreadFactoryTest.kt index d0b9055..44cfcbe 100644 --- a/concurrent/src/test/kotlin/org/sheinbergon/needle/concurrent/GovernedAffinityPinnedThreadFactoryTest.kt +++ b/concurrent/src/test/kotlin/org/sheinbergon/needle/concurrent/GovernedAffinityPinnedThreadFactoryTest.kt @@ -3,8 +3,17 @@ package org.sheinbergon.needle.concurrent import org.amshove.kluent.shouldBeEqualTo import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import org.sheinbergon.needle.* +import org.sheinbergon.needle.`0` +import org.sheinbergon.needle.`1` +import org.sheinbergon.needle.`2` +import org.sheinbergon.needle.Pinned +import org.sheinbergon.needle.PinnedThread +import org.sheinbergon.needle.binaryTestMask import org.sheinbergon.needle.concurrent.util.ResettableOneOffLatch +import org.sheinbergon.needle.default +import org.sheinbergon.needle.negatedBinaryTestMask +import org.sheinbergon.needle.negatedTestAffinityDescriptor +import org.sheinbergon.needle.testAffinityDescriptor import java.util.concurrent.RecursiveAction class GovernedAffinityPinnedThreadFactoryTest { @@ -87,7 +96,7 @@ class GovernedAffinityPinnedThreadFactoryTest { @Test fun `Initialize the factory without a mask and alter the affinity of created pinned fork-join threads`() { val factory = GovernedAffinityPinnedThreadFactory() - PinnedForkJoinPool(`1`, factory).use { pool -> + PinnedExecutors.newPinnedWorkStealingPool(`1`, factory).let { pool -> val action = UnlatchAndSleepAction() pool.execute(action) latch.await(true) @@ -103,7 +112,7 @@ class GovernedAffinityPinnedThreadFactoryTest { @Test fun `Initialize the factory using a mask and alter the affinity of newly created pinned fork-join threads`() { val factory = GovernedAffinityPinnedThreadFactory(testAffinityDescriptor) - PinnedForkJoinPool(`2`, factory).use { pool -> + PinnedExecutors.newPinnedWorkStealingPool(`2`, factory).let { pool -> val action1 = UnlatchAndSleepAction() pool.execute(action1) latch.await(true) diff --git a/concurrent/src/test/kotlin/org/sheinbergon/needle/concurrent/PinnedExecutorsTest.kt b/concurrent/src/test/kotlin/org/sheinbergon/needle/concurrent/PinnedExecutorsTest.kt new file mode 100644 index 0000000..1931087 --- /dev/null +++ b/concurrent/src/test/kotlin/org/sheinbergon/needle/concurrent/PinnedExecutorsTest.kt @@ -0,0 +1,91 @@ +package org.sheinbergon.needle.concurrent + +import com.google.common.collect.Sets +import org.amshove.kluent.shouldBe +import org.amshove.kluent.shouldBeEqualTo +import org.junit.jupiter.api.Test +import org.sheinbergon.needle.`0` +import org.sheinbergon.needle.`1` +import org.sheinbergon.needle.Pinned +import org.sheinbergon.needle.availableCores +import java.util.concurrent.CountDownLatch +import java.util.concurrent.ExecutorService +import java.util.concurrent.ForkJoinPool +import java.util.concurrent.ScheduledExecutorService +import java.util.concurrent.TimeUnit + +class PinnedExecutorsTest { + + @Test + fun `Single pinnned thread executor`() { + PinnedExecutors + .newSinglePinnedThreadExecutor(TestMaskPinnedThreadFactory) + .let { testPinnedThreadExecutor(`1`, it) } + } + + @Test + fun `Fixed pinned thread pool executor`() { + PinnedExecutors + .newFixedPinnedThreadPool(availableCores, TestMaskPinnedThreadFactory) + .let { testPinnedThreadExecutor(availableCores, it) } + } + + @Test + fun `Single pinned thread scheduled executor`() { + PinnedExecutors + .newSinglePinnedThreadScheduledExecutor(TestMaskPinnedThreadFactory) + .let { testScheduledPinnedThreadExecutor(`1`, it) } + } + + @Test + fun `Pooled pinned thread scheduled executor`() { + PinnedExecutors + .newScheduledPinnedThreadPool(availableCores, TestMaskPinnedThreadFactory) + .let { testScheduledPinnedThreadExecutor(availableCores, it) } + } + + @Test + fun `Fixed affinity PinnedForkJoinPool behavior`() { + PinnedExecutors + .newPinnedWorkStealingPool(availableCores, TestMaskPinnedThreadFactory) + .let { testPinnedWorkStealingExecutor(availableCores, it) } + } + + private fun testPinnedThreadExecutor(concurrency: Int, pool: ExecutorService) { + val visited = Sets.newConcurrentHashSet() + val latch = CountDownLatch(concurrency) + val tasks = (`0` until concurrency).map { callableTask(latch, visited) } + val futures = pool.invokeAll(tasks) + latch.await() + Thread.sleep(5L) + futures.forEach { it.isDone shouldBe true } + visited.size shouldBeEqualTo concurrency + pool.shutdown() + } + + private fun testScheduledPinnedThreadExecutor(concurrency: Int, scheduler: ScheduledExecutorService) { + val visited = Sets.newConcurrentHashSet() + val latch = CountDownLatch(concurrency) + val futures = (`0` until concurrency) + .map { runnableTask(latch, visited) } + .map { scheduler.schedule(it, SCHEDULING_DELAY, TimeUnit.MILLISECONDS) } + latch.await() + Thread.sleep(5L) + visited.size shouldBeEqualTo concurrency + futures.forEach { it.isDone shouldBeEqualTo true } + scheduler.shutdown() + } + + private fun testPinnedWorkStealingExecutor(concurrency: Int, pool: ForkJoinPool) { + val visited = Sets.newConcurrentHashSet() + val latch = CountDownLatch(concurrency) + pool.parallelism shouldBeEqualTo concurrency + val actions = (`0` until concurrency).map { recursiveAction(latch, visited) } + val tasks = actions.map { pool.submit(it) } + latch.await() + Thread.sleep(5L) + tasks.forEach { it.isDone shouldBe true } + visited.size shouldBeEqualTo concurrency + pool.shutdown() + } +} diff --git a/concurrent/src/test/kotlin/org/sheinbergon/needle/concurrent/PinnedForkJoinPoolTest.kt b/concurrent/src/test/kotlin/org/sheinbergon/needle/concurrent/PinnedForkJoinPoolTest.kt deleted file mode 100644 index 2019df7..0000000 --- a/concurrent/src/test/kotlin/org/sheinbergon/needle/concurrent/PinnedForkJoinPoolTest.kt +++ /dev/null @@ -1,31 +0,0 @@ -package org.sheinbergon.needle.concurrent - -import com.google.common.collect.Sets -import org.amshove.kluent.shouldBe -import org.amshove.kluent.shouldBeEqualTo -import org.junit.jupiter.api.Test -import org.sheinbergon.needle.* -import java.util.concurrent.CountDownLatch - -class PinnedForkJoinPoolTest { - - @Test - fun `Fixed affinity PinnedForkJoinPool behavior`() { - val pool = PinnedForkJoinPool(availableCores, TestMaskPinnedThreadFactory) - pool.use { - testPinnedThreadExecutor(availableCores, pool) - } - } - - private fun testPinnedThreadExecutor(concurrency: Int, pool: PinnedForkJoinPool) = pool.use { - val visited = Sets.newConcurrentHashSet() - val latch = CountDownLatch(concurrency) - pool.parallelism shouldBeEqualTo concurrency - val actions = (`0` until concurrency).map { recursiveAction(latch, visited) } - val tasks = actions.map { pool.submit(it) } - latch.await() - Thread.sleep(5L) - tasks.forEach { it.isDone shouldBe true } - visited.size shouldBeEqualTo concurrency - } -} diff --git a/concurrent/src/test/kotlin/org/sheinbergon/needle/concurrent/PinnedThreadPoolExecutorTest.kt b/concurrent/src/test/kotlin/org/sheinbergon/needle/concurrent/PinnedThreadPoolExecutorTest.kt deleted file mode 100644 index 06f2bf6..0000000 --- a/concurrent/src/test/kotlin/org/sheinbergon/needle/concurrent/PinnedThreadPoolExecutorTest.kt +++ /dev/null @@ -1,35 +0,0 @@ -package org.sheinbergon.needle.concurrent - -import com.google.common.collect.Sets -import org.amshove.kluent.shouldBe -import org.amshove.kluent.shouldBeEqualTo -import org.junit.jupiter.api.Test -import org.sheinbergon.needle.* -import java.util.concurrent.CountDownLatch - -class PinnedThreadPoolExecutorTest { - - @Test - fun `Single pinnned thread executor`() { - val executor = PinnedThreadPoolExecutor.newSinglePinnedThreadExecutor(TestMaskPinnedThreadFactory) - testPinnedThreadExecutor(`1`, executor as PinnedThreadPoolExecutor) - } - - @Test - fun `Fixed pinned thread pool executor`() { - val executor = PinnedThreadPoolExecutor.newFixedPinnedThreadPool(availableCores, TestMaskPinnedThreadFactory) - testPinnedThreadExecutor(availableCores, executor as PinnedThreadPoolExecutor) - } - - private fun testPinnedThreadExecutor(concurrency: Int, pool: PinnedThreadPoolExecutor) = pool.use { - val visited = Sets.newConcurrentHashSet() - val latch = CountDownLatch(concurrency) - pool.corePoolSize shouldBeEqualTo concurrency - val tasks = (`0` until concurrency).map { callableTask(latch, visited) } - val futures = pool.invokeAll(tasks) - latch.await() - Thread.sleep(5L) - futures.forEach { it.isDone shouldBe true } - visited.size shouldBeEqualTo concurrency - } -} diff --git a/concurrent/src/test/kotlin/org/sheinbergon/needle/concurrent/ScheduledPinnedThreadPoolExecutorTest.kt b/concurrent/src/test/kotlin/org/sheinbergon/needle/concurrent/ScheduledPinnedThreadPoolExecutorTest.kt deleted file mode 100644 index 972e8fd..0000000 --- a/concurrent/src/test/kotlin/org/sheinbergon/needle/concurrent/ScheduledPinnedThreadPoolExecutorTest.kt +++ /dev/null @@ -1,41 +0,0 @@ -package org.sheinbergon.needle.concurrent - -import com.google.common.collect.Sets -import org.amshove.kluent.shouldBeEqualTo -import org.junit.jupiter.api.Test -import org.sheinbergon.needle.* -import java.util.concurrent.CountDownLatch -import java.util.concurrent.TimeUnit - -class ScheduledPinnedThreadPoolExecutorTest { - - @Test - fun `Single pinned thread scheduled executor`() { - ScheduledPinnedThreadPoolExecutor - .newSinglePinnedThreadScheduledExecutor(TestMaskPinnedThreadFactory) - .let { testPinnedThreadExecutor(`1`, it as ScheduledPinnedThreadPoolExecutor) } - } - - @Test - fun `Pooled pinned thread scheduled executor`() { - ScheduledPinnedThreadPoolExecutor - .newScheduledPinnedThreadPool(availableCores, TestMaskPinnedThreadFactory) - .let { testPinnedThreadExecutor(availableCores, it as ScheduledPinnedThreadPoolExecutor) } - } - - private fun testPinnedThreadExecutor( - concurrency: Int, - scheduler: ScheduledPinnedThreadPoolExecutor - ) = scheduler.use { - val visited = Sets.newConcurrentHashSet() - val latch = CountDownLatch(concurrency) - scheduler.corePoolSize shouldBeEqualTo concurrency - val futures = (`0` until concurrency) - .map { runnableTask(latch, visited) } - .map { scheduler.schedule(it, SCHEDULING_DELAY, TimeUnit.MILLISECONDS) } - latch.await() - Thread.sleep(5L) - visited.size shouldBeEqualTo concurrency - futures.forEach { it.isDone shouldBeEqualTo true } - } -} diff --git a/concurrent/src/test/kotlin/org/sheinbergon/needle/concurrent/Support.kt b/concurrent/src/test/kotlin/org/sheinbergon/needle/concurrent/Support.kt index e70a0b8..a8f4be6 100644 --- a/concurrent/src/test/kotlin/org/sheinbergon/needle/concurrent/Support.kt +++ b/concurrent/src/test/kotlin/org/sheinbergon/needle/concurrent/Support.kt @@ -2,8 +2,16 @@ package org.sheinbergon.needle.concurrent import org.amshove.kluent.shouldBe import org.amshove.kluent.shouldBeEqualTo -import org.sheinbergon.needle.* -import java.util.concurrent.* +import org.sheinbergon.needle.Pinned +import org.sheinbergon.needle.PinnedThread +import org.sheinbergon.needle.binaryTestMask +import org.sheinbergon.needle.testAffinityDescriptor +import org.sheinbergon.needle.textTestMask +import java.util.concurrent.Callable +import java.util.concurrent.CountDownLatch +import java.util.concurrent.Executors +import java.util.concurrent.ForkJoinPool +import java.util.concurrent.RecursiveAction internal const val SCHEDULING_DELAY = 500L @@ -15,7 +23,7 @@ internal object TestMaskPinnedThreadFactory : PinnedThreadFactory { @Suppress("UNCHECKED_CAST") internal fun callableTask(latch: CountDownLatch, visited: MutableSet): Callable = - Executors.callable { runnableTask(latch, visited).run() } as Callable + Executors.callable { runnableTask(latch, visited).run() } as Callable internal fun recursiveAction(latch: CountDownLatch, visited: MutableSet) = object : RecursiveAction() { override fun compute() = runnableTask(latch, visited).run() diff --git a/core/src/test/kotlin/org/sheinbergon/needle/PinnedThreadTest.kt b/core/src/test/kotlin/org/sheinbergon/needle/PinnedThreadTest.kt index d2e13cb..b737ae7 100644 --- a/core/src/test/kotlin/org/sheinbergon/needle/PinnedThreadTest.kt +++ b/core/src/test/kotlin/org/sheinbergon/needle/PinnedThreadTest.kt @@ -201,24 +201,24 @@ class PinnedThreadTest { } private class ExtendedPinnedThread( - affinity: AffinityDescriptor, - private val runnable: Runnable + affinity: AffinityDescriptor, + private val runnable: Runnable ) : PinnedThread(affinity) { override fun run() = runnable.run() } - private class SingleThreadedForkJoinWorkerThreadFactory(private val affinity: AffinityDescriptor? = null) - : ForkJoinPool.ForkJoinWorkerThreadFactory { + private class SingleThreadedForkJoinWorkerThreadFactory(private val affinity: AffinityDescriptor? = null) : + ForkJoinPool.ForkJoinWorkerThreadFactory { private val threads = mutableMapOf() operator fun get(pool: ForkJoinPool) = threads[pool] override fun newThread(pool: ForkJoinPool) = threads - .computeIfAbsent(pool) { - affinity - ?.let { PinnedThread.ForkJoinWorker(pool, it) } - ?: PinnedThread.ForkJoinWorker(pool) - } + .computeIfAbsent(pool) { + affinity + ?.let { PinnedThread.ForkJoinWorker(pool, it) } + ?: PinnedThread.ForkJoinWorker(pool) + } } } diff --git a/core/src/test/kotlin/org/sheinbergon/needle/Support.kt b/core/src/test/kotlin/org/sheinbergon/needle/Support.kt index dc20ea5..3f8eaa1 100644 --- a/core/src/test/kotlin/org/sheinbergon/needle/Support.kt +++ b/core/src/test/kotlin/org/sheinbergon/needle/Support.kt @@ -98,4 +98,4 @@ private fun setPlatform(resolver: AffinityResolver<*>) { modifiers.trySetAccessible() modifiers.setInt(affinityResolver, affinityResolver.getModifiers() and Modifier.FINAL.inv()) affinityResolver.set(null, resolver) -} +} \ No newline at end of file diff --git a/detekt.yml b/detekt.yml index 8f2e33d..a4a7481 100644 --- a/detekt.yml +++ b/detekt.yml @@ -1,21 +1,14 @@ build: maxIssues: 1 - excludeCorrectable: false weights: # complexity: 2 # LongParameterList: 1 # style: 1 # comments: 1 -config: - validation: true - # when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]' - excludes: '' - processors: active: true exclude: - - 'DetektProgressListener' # - 'FunctionCountProcessor' # - 'PropertyCountProcessor' # - 'ClassCountProcessor' @@ -25,25 +18,22 @@ processors: console-reports: active: true exclude: - - 'ProjectStatisticsReport' - - 'ComplexityReport' - - 'NotificationReport' - # - 'FindingsReport' - - 'FileBasedFindingsReport' + # - 'ProjectStatisticsReport' + # - 'ComplexityReport' + # - 'NotificationReport' + # - 'FindingsReport' + # - 'BuildFailureReport' comments: active: true - excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] - AbsentOrWrongFileLicense: - active: false - licenseTemplateFile: 'license.template' + excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" CommentOverPrivateFunction: active: false CommentOverPrivateProperty: active: false EndOfSentenceFormat: active: false - endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)' + endOfSentenceFormat: ([.?!][ \t\n\r\f<])|([.?!:]$) UndocumentedPublicClass: active: false searchInNestedClass: true @@ -52,8 +42,6 @@ comments: searchInInnerInterface: true UndocumentedPublicFunction: active: false - UndocumentedPublicProperty: - active: false complexity: active: true @@ -64,17 +52,14 @@ complexity: active: false threshold: 10 includeStaticDeclarations: false - includePrivateDeclarations: false ComplexMethod: active: true - threshold: 15 - ignoreSingleWhenExpression: false - ignoreSimpleWhenEntries: false - ignoreNestingFunctions: false - nestingFunctions: [ run, let, apply, with, also, use, forEach, isNotNull, ifNull ] + threshold: 10 + ignoreSingleWhenExpression: true + ignoreSimpleWhenEntries: true LabeledExpression: active: false - ignoredLabels: [ ] + ignoredLabels: "" LargeClass: active: true threshold: 600 @@ -82,29 +67,28 @@ complexity: active: true threshold: 60 LongParameterList: + excludes: "**/*Configuration.kt" active: true - functionThreshold: 7 - constructorThreshold: 7 + functionThreshold: 8 + constructorThreshold: 10 ignoreDefaultParameters: false - ignoreDataClasses: true - ignoreAnnotated: [ ] MethodOverloading: active: false threshold: 6 NestedBlockDepth: active: true + excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" threshold: 4 - excludes: [ '**/test/**' ] StringLiteralDuplication: active: false - excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" threshold: 3 ignoreAnnotation: true excludeStringsWithLessThan5Characters: true ignoreStringsRegex: '$^' TooManyFunctions: - active: true - excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + active: false + excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" thresholdInFiles: 11 thresholdInClasses: 11 thresholdInInterfaces: 11 @@ -114,18 +98,11 @@ complexity: ignorePrivate: false ignoreOverridden: false -coroutines: - active: true - GlobalCoroutineUsage: - active: false - RedundantSuspendModifier: - active: false - empty-blocks: active: true EmptyCatchBlock: active: true - allowedExceptionNameRegex: '_|(ignore|expected).*' + allowedExceptionNameRegex: "^(_|(ignore|expected).*)" EmptyClassBlock: active: true EmptyDefaultConstructor: @@ -149,8 +126,6 @@ empty-blocks: active: true EmptySecondaryConstructor: active: true - EmptyTryBlock: - active: true EmptyWhenBlock: active: true EmptyWhileBlock: @@ -160,10 +135,10 @@ exceptions: active: true ExceptionRaisedInUnexpectedLocation: active: false - methodNames: [ toString, hashCode, equals, finalize ] + methodNames: 'toString,hashCode,equals,finalize' InstanceOfCheckForException: active: false - excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" NotImplementedDeclaration: active: false PrintStackTrace: @@ -172,30 +147,21 @@ exceptions: active: false ReturnFromFinally: active: false - ignoreLabeled: false SwallowedException: active: false - ignoredExceptionTypes: - - InterruptedException - - NumberFormatException - - ParseException - - MalformedURLException - allowedExceptionNameRegex: '_|(ignore|expected).*' + ignoredExceptionTypes: 'InterruptedException,NumberFormatException,ParseException,MalformedURLException' ThrowingExceptionFromFinally: active: false ThrowingExceptionInMain: active: false ThrowingExceptionsWithoutMessageOrCause: active: false - exceptions: - - IllegalArgumentException - - IllegalStateException - - IOException + exceptions: 'IllegalArgumentException,IllegalStateException,IOException' ThrowingNewInstanceOfSameException: active: false TooGenericExceptionCaught: - active: true - excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + active: false + excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" exceptionNames: - ArrayIndexOutOfBoundsException - Error @@ -205,7 +171,7 @@ exceptions: - IndexOutOfBoundsException - RuntimeException - Throwable - allowedExceptionNameRegex: '_|(ignore|expected).*' + allowedExceptionNameRegex: "^(_|(ignore|expected).*)" TooGenericExceptionThrown: active: true exceptionNames: @@ -218,42 +184,31 @@ formatting: active: true android: false autoCorrect: true - AnnotationOnSeparateLine: - active: false - autoCorrect: true ChainWrapping: active: true autoCorrect: true CommentSpacing: active: true autoCorrect: true - EnumEntryNameCase: - active: false - autoCorrect: true Filename: active: true FinalNewline: active: true autoCorrect: true - insertFinalNewLine: true ImportOrdering: active: false - autoCorrect: true - layout: 'idea' Indentation: - active: true + active: false autoCorrect: true indentSize: 2 continuationIndentSize: 4 MaximumLineLength: active: true + excludes: "**/*Configuration.kt" maxLineLength: 150 ModifierOrdering: active: true autoCorrect: true - MultiLineIfElse: - active: true - autoCorrect: true NoBlankLineBeforeRbrace: active: true autoCorrect: true @@ -263,9 +218,6 @@ formatting: NoEmptyClassBody: active: true autoCorrect: true - NoEmptyFirstLineInMethodBlock: - active: false - autoCorrect: true NoLineBreakAfterElse: active: true autoCorrect: true @@ -288,7 +240,7 @@ formatting: active: true autoCorrect: true NoWildcardImports: - active: false + active: true autoCorrect: true PackageName: active: true @@ -296,9 +248,9 @@ formatting: ParameterListWrapping: active: true autoCorrect: true - indentSize: 2 + indentSize: 4 SpacingAroundColon: - active: false + active: true autoCorrect: true SpacingAroundComma: active: true @@ -306,12 +258,6 @@ formatting: SpacingAroundCurly: active: true autoCorrect: true - SpacingAroundDot: - active: true - autoCorrect: true - SpacingAroundDoubleColon: - active: false - autoCorrect: true SpacingAroundKeyword: active: true autoCorrect: true @@ -324,12 +270,6 @@ formatting: SpacingAroundRangeOperator: active: true autoCorrect: true - SpacingBetweenDeclarationsWithAnnotations: - active: false - autoCorrect: true - SpacingBetweenDeclarationsWithComments: - active: false - autoCorrect: true StringTemplate: active: true autoCorrect: true @@ -338,41 +278,39 @@ naming: active: true ClassNaming: active: true - excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] - classPattern: '[A-Z][a-zA-Z0-9]*' + excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" + classPattern: '[A-Z$][a-zA-Z0-9$]*' ConstructorParameterNaming: active: true - excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" parameterPattern: '[a-z][A-Za-z0-9]*' privateParameterPattern: '[a-z][A-Za-z0-9]*' excludeClassPattern: '$^' - ignoreOverridden: true EnumNaming: active: true - excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] - enumEntryPattern: '[A-Z][_a-zA-Z0-9]*' + excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" + enumEntryPattern: '^[A-Z][_a-zA-Z0-9]*' ForbiddenClassName: active: false - excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] - forbiddenName: [ ] + excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" + forbiddenName: '' FunctionMaxLength: active: false - excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" maximumFunctionNameLength: 30 FunctionMinLength: active: false - excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" minimumFunctionNameLength: 3 FunctionNaming: active: true - excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] - functionPattern: '([a-z][a-zA-Z0-9]*)|(`.*`)' + excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" + functionPattern: '^([a-z$][a-zA-Z$0-9]*)|(`.*`)$' excludeClassPattern: '$^' ignoreOverridden: true - ignoreAnnotated: [ 'Composable' ] FunctionParameterNaming: active: true - excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" parameterPattern: '[a-z][A-Za-z0-9]*' excludeClassPattern: '$^' ignoreOverridden: true @@ -381,37 +319,36 @@ naming: rootPackage: '' MatchingDeclarationName: active: true - mustBeFirst: true MemberNameEqualsClassName: - active: true + active: false ignoreOverridden: true ObjectPropertyNaming: active: true - excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] - constantPattern: '^`?[_A-Z0-9]+`?$' - propertyPattern: '[A-Za-z][_A-Za-z0-9]*' - privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*' + excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" + constantPattern: '`?[A-Za-z0-9][_A-Za-z0-9]*`?' + propertyPattern: '`?[A-Za-z0-9][_A-Za-z0-9]*`?' + privatePropertyPattern: '([_`])?[A-Za-z0-9][_A-Za-z0-9]*`?' PackageNaming: active: true - excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] - packagePattern: '[a-z]+(\.[a-z][A-Za-z0-9]*)*' + excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" + packagePattern: '^[a-z]+(\.[a-z][A-Za-z0-9]*)*$' TopLevelPropertyNaming: active: true - excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] - constantPattern: '^`?[_A-Z0-9]+`?$' + excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" + constantPattern: '`?[A-Za-z0-9][_A-Za-z0-9]*`?' propertyPattern: '[A-Za-z][_A-Za-z0-9]*' privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*' VariableMaxLength: active: false - excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" maximumVariableNameLength: 64 VariableMinLength: active: false - excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" minimumVariableNameLength: 1 VariableNaming: active: true - excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" variablePattern: '[a-z][A-Za-z0-9]*' privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*' excludeClassPattern: '$^' @@ -420,61 +357,40 @@ naming: performance: active: true ArrayPrimitive: - active: true + active: false ForEachOnRange: active: true - excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" SpreadOperator: active: true - excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] + excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" UnnecessaryTemporaryInstantiation: active: true potential-bugs: active: true - Deprecation: - active: false DuplicateCaseInWhenExpression: active: true EqualsAlwaysReturnsTrueOrFalse: - active: true + active: false EqualsWithHashCodeExist: active: true ExplicitGarbageCollectionCall: active: true - HasPlatformType: - active: false - IgnoredReturnValue: - active: false - restrictToAnnotatedMethods: true - returnValueAnnotations: [ '*.CheckReturnValue', '*.CheckResult' ] - ImplicitDefaultLocale: - active: false - ImplicitUnitReturnType: - active: false - allowExplicitReturnType: true InvalidRange: - active: true + active: false IteratorHasNextCallsNextMethod: - active: true + active: false IteratorNotThrowingNoSuchElementException: - active: true - LateinitUsage: active: false - excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] - excludeAnnotatedProperties: [ ] - ignoreOnClassesPattern: '' - MapGetWithNotNullAssertionOperator: + LateinitUsage: active: false + excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" + excludeAnnotatedProperties: "" + ignoreOnClassesPattern: "" MissingWhenCase: - active: true - RedundantElseInWhen: - active: true - UnconditionalJumpStatementInLoop: - active: false - UnnecessaryNotNullOperator: active: false - UnnecessarySafeCall: + UnconditionalJumpStatementInLoop: active: false UnreachableCode: active: true @@ -485,7 +401,7 @@ potential-bugs: UselessPostfixExpression: active: false WrongEqualsTypeParameter: - active: true + active: false style: active: true @@ -497,10 +413,8 @@ style: DataClassShouldBeImmutable: active: false EqualsNullCall: - active: true - EqualsOnSignatureLine: active: false - ExplicitCollectionElementAccessMethod: + EqualsOnSignatureLine: active: false ExplicitItLambdaParameter: active: false @@ -509,39 +423,26 @@ style: includeLineWrapping: false ForbiddenComment: active: true - values: [ 'TODO:', 'FIXME:', 'STOPSHIP:' ] - allowedPatterns: '' + values: 'TODO:,FIXME:,STOPSHIP:' ForbiddenImport: active: false - imports: [ ] - forbiddenPatterns: '' - ForbiddenMethodCall: - active: false - methods: [ 'kotlin.io.println', 'kotlin.io.print' ] - ForbiddenPublicDataClass: - active: false - ignorePackages: [ '*.internal', '*.internal.*' ] + imports: '' ForbiddenVoid: active: false ignoreOverridden: false - ignoreUsageInGenerics: false FunctionOnlyReturningConstant: - active: true + active: false ignoreOverridableFunction: true excludedFunctions: 'describeContents' - excludeAnnotatedFunction: [ 'dagger.Provides' ] - LibraryCodeMustSpecifyReturnType: - active: true LoopWithTooManyJumpStatements: - active: true + active: false maxJumpCount: 1 MagicNumber: active: true - excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] - ignoreNumbers: [ '-1', '0', '1', '2' ] + excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" + ignoreNumbers: '-1,0,1,2' ignoreHashCodeFunction: true ignorePropertyDeclaration: false - ignoreLocalVariableDeclaration: false ignoreConstantDeclaration: true ignoreCompanionObjectPropertyDeclaration: true ignoreAnnotation: false @@ -550,22 +451,21 @@ style: ignoreRanges: false MandatoryBracesIfStatements: active: false - MandatoryBracesLoops: - active: false MaxLineLength: active: true - maxLineLength: 120 + excludes: "**/*Configuration.kt" + maxLineLength: 150 excludePackageStatements: true excludeImportStatements: true excludeCommentStatements: false MayBeConst: - active: true + active: false ModifierOrder: active: true NestedClassesVisibility: active: false NewLineAtEndOfFile: - active: true + active: false NoTabs: active: false OptionalAbstractKeyword: @@ -577,18 +477,15 @@ style: PreferToOverPairSyntax: active: false ProtectedMemberInFinalClass: - active: true - RedundantExplicitType: active: false RedundantVisibilityModifierRule: active: false ReturnCount: active: true max: 2 - excludedFunctions: 'equals' + excludedFunctions: "equals" excludeLabeled: false excludeReturnFromLambda: true - excludeGuardClauses: false SafeCast: active: true SerialVersionUIDInSerializableClass: @@ -604,14 +501,12 @@ style: active: false acceptableDecimalLength: 5 UnnecessaryAbstractClass: - active: true - excludeAnnotatedClasses: [ 'dagger.Module' ] - UnnecessaryAnnotationUseSiteTarget: active: false + excludeAnnotatedClasses: "dagger.Module" UnnecessaryApply: active: false UnnecessaryInheritance: - active: true + active: false UnnecessaryLet: active: false UnnecessaryParentheses: @@ -621,29 +516,24 @@ style: UnusedImports: active: false UnusedPrivateClass: - active: true - UnusedPrivateMember: active: false - allowedNames: '(_|ignored|expected|serialVersionUID)' - UseArrayLiteralsInAnnotations: + UnusedPrivateMember: active: false + allowedNames: "(_|ignored|expected|serialVersionUID)" UseCheckOrError: active: false UseDataClass: active: false - excludeAnnotatedClasses: [ ] - allowVars: false - UseIfInsteadOfWhen: - active: false + excludeAnnotatedClasses: "" UseRequire: active: false UselessCallOnNotNull: - active: true + active: false UtilityClassWithPublicConstructor: - active: true + active: false VarCouldBeVal: active: false WildcardImport: - active: true - excludes: [ '**/test/**', '**/androidTest/**', '**/commonTest/**', '**/jvmTest/**', '**/jsTest/**', '**/iosTest/**' ] - excludeImports: [ 'java.util.*', 'kotlinx.android.synthetic.*' ] \ No newline at end of file + active: false + excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt" + excludeImports: 'java.util.*,kotlinx.android.synthetic.*' diff --git a/knitter/src/main/kotlin/org/sheinbergon/needle/knitter/coroutines/PinnedDispatchers.kt b/knitter/src/main/kotlin/org/sheinbergon/needle/knitter/coroutines/PinnedDispatchers.kt index 434e62c..ee9ef13 100644 --- a/knitter/src/main/kotlin/org/sheinbergon/needle/knitter/coroutines/PinnedDispatchers.kt +++ b/knitter/src/main/kotlin/org/sheinbergon/needle/knitter/coroutines/PinnedDispatchers.kt @@ -6,7 +6,7 @@ import kotlinx.coroutines.asCoroutineDispatcher import org.sheinbergon.needle.AffinityDescriptor import org.sheinbergon.needle.concurrent.FixedAffinityPinnedThreadFactory import org.sheinbergon.needle.concurrent.GovernedAffinityPinnedThreadFactory -import org.sheinbergon.needle.concurrent.PinnedThreadPoolExecutor +import org.sheinbergon.needle.concurrent.PinnedExecutors import kotlin.coroutines.CoroutineContext @@ -23,7 +23,7 @@ private class GovernedAffinityDelegatingDispatcher( init { factory = GovernedAffinityPinnedThreadFactory(affinity) - val executor = PinnedThreadPoolExecutor.newFixedPinnedThreadPool(parallelism, factory) + val executor = PinnedExecutors.newFixedPinnedThreadPool(parallelism, factory) delegate = executor.asCoroutineDispatcher() } @@ -43,6 +43,6 @@ fun fixedAffinitySingleThread(affinity: AffinityDescriptor) = fun fixedAffinityThreadPool(parallelism: Int, affinity: AffinityDescriptor): CoroutineDispatcher { val factory = FixedAffinityPinnedThreadFactory(affinity) - val executor = PinnedThreadPoolExecutor.newFixedPinnedThreadPool(parallelism, factory) + val executor = PinnedExecutors.newFixedPinnedThreadPool(parallelism, factory) return executor.asCoroutineDispatcher() } diff --git a/knitter/src/test/kotlin/org/sheinbergon/needle/knitter/PinnedThreadsTest.kt b/knitter/src/test/kotlin/org/sheinbergon/needle/knitter/PinnedThreadsTest.kt index 84dd8a6..93b0972 100644 --- a/knitter/src/test/kotlin/org/sheinbergon/needle/knitter/PinnedThreadsTest.kt +++ b/knitter/src/test/kotlin/org/sheinbergon/needle/knitter/PinnedThreadsTest.kt @@ -3,7 +3,11 @@ package org.sheinbergon.needle.knitter import org.amshove.kluent.shouldBe import org.amshove.kluent.shouldBeEqualTo import org.junit.jupiter.api.Test -import org.sheinbergon.needle.* +import org.sheinbergon.needle.`1` +import org.sheinbergon.needle.NEEDLE +import org.sheinbergon.needle.binaryTestMask +import org.sheinbergon.needle.default +import org.sheinbergon.needle.testAffinityDescriptor import java.util.concurrent.CountDownLatch class PinnedThreadsTest { @@ -12,11 +16,11 @@ class PinnedThreadsTest { fun `PinnedThread creation - Kotlin interface - Parameterized`() { val latch = CountDownLatch(`1`) val pinned = pinnedThread( - start = false, - name = NEEDLE, - contextClassLoader = ClassLoader.getPlatformClassLoader(), - isDaemon = true, - affinity = testAffinityDescriptor) { + start = false, + name = NEEDLE, + contextClassLoader = ClassLoader.getPlatformClassLoader(), + isDaemon = true, + affinity = testAffinityDescriptor) { latch.countDown() runCatching { Thread.sleep(1250) } } diff --git a/knitter/src/test/kotlin/org/sheinbergon/needle/knitter/coroutines/PinnedDispatchersTest.kt b/knitter/src/test/kotlin/org/sheinbergon/needle/knitter/coroutines/PinnedDispatchersTest.kt index 7e4a9b7..c75cd4e 100644 --- a/knitter/src/test/kotlin/org/sheinbergon/needle/knitter/coroutines/PinnedDispatchersTest.kt +++ b/knitter/src/test/kotlin/org/sheinbergon/needle/knitter/coroutines/PinnedDispatchersTest.kt @@ -1,10 +1,24 @@ package org.sheinbergon.needle.knitter.coroutines -import kotlinx.coroutines.* +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.runBlocking import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldBeLessOrEqualTo import org.junit.jupiter.api.Test -import org.sheinbergon.needle.* +import org.sheinbergon.needle.`1` +import org.sheinbergon.needle.AffinityDescriptor +import org.sheinbergon.needle.Needle +import org.sheinbergon.needle.availableCores +import org.sheinbergon.needle.binaryTestMask +import org.sheinbergon.needle.negatedBinaryTestMask +import org.sheinbergon.needle.negatedTestAffinityDescriptor +import org.sheinbergon.needle.negatedTextTestMask +import org.sheinbergon.needle.testAffinityDescriptor +import org.sheinbergon.needle.textTestMask class PinnedDispatchersTest { @@ -43,34 +57,34 @@ class PinnedDispatchersTest { } private fun deferredAffinitySingleAsync(dispatcher: CoroutineDispatcher) = - GlobalScope.async(dispatcher) { Needle.affinity() } + GlobalScope.async(dispatcher) { Needle.affinity() } private fun deferredAffinityPoolAsync(cores: Int, dispatcher: CoroutineDispatcher) = (`1`..cores) - .map { - GlobalScope.async(dispatcher) { - Thread.currentThread() to Needle.affinity() - } + .map { + GlobalScope.async(dispatcher) { + Thread.currentThread() to Needle.affinity() } + } private suspend fun blockingAssertSingle( - deferred: Deferred, - binaryMask: Long, - textMask: String + deferred: Deferred, + binaryMask: Long, + textMask: String ) { val affinity = deferred.await() affinity.mask() shouldBeEqualTo binaryMask - affinity.toString() shouldBeEqualTo textMask + affinity.toString() shouldBeEqualTo textMask } private suspend fun blockingAssertPool( - cores: Int, - deferred: List>>, - binaryMask: Long, - textMask: String + cores: Int, + deferred: List>>, + binaryMask: Long, + textMask: String ) { val results = deferred.awaitAll() val threads = results.mapTo(mutableSetOf(), Pair::first) - threads.size shouldBeLessOrEqualTo cores + threads.size shouldBeLessOrEqualTo cores results.forEach { (_, affinity) -> affinity.mask() shouldBeEqualTo binaryMask affinity.toString() shouldBeEqualTo textMask