From 86e8acfc08fd78e16a2aa051f9903e774a1b9f59 Mon Sep 17 00:00:00 2001 From: Peter Isberg Date: Mon, 3 Aug 2026 19:05:14 +0200 Subject: [PATCH 1/2] docs: document DetectorType's 127 constants, complete the tag coverage, and make the licence gate honest Three decisions taken by the maintainer, applied. DetectorType. The enum a user types into @AsyncTest(excludes=...) had 127 bare constants in the published javadoc. Each now carries the first sentence of the detector it selects, taken from that detector's own class javadoc rather than invented. The mapping is derived: AsyncTestConfig.build() gives constant to flag, DetectorRegistry's constructor gives flag to class, and all 127 resolve with none left over. The file is @AILocked and the lock was waived for this on purpose: its own reason is that a constant needs synchronized edits in five places, and a comment adds no constant. The annotation now says the lock is on the constant set, not the file. Two stale claims in the same guardrail are fixed while there: it described "both branches of build()", which has been one expression per detector for some time, and @AIKeepInSync still named the old META-INF/services path for the built-in factories. Both feed the generated CLAUDE.md, so a stale guardrail misdirects every future contributor. Tags. 295 @param and 122 @return appended to existing blocks. Appended, not rebuilt: an earlier attempt rebuilt blocks and replaced real prose with a stub, turning ConcurrencyRunner.execute's javadoc into "Execute.". 287 single-line comments were expanded in a separate first pass so no insertion had to reason about indices another insertion had already moved. Licence gate. Behaviour unchanged, which is the decision: it is intended for a PolyForm Noncommercial library and LicenseGuard is security-critical. What changes is honesty. The README said "outcome depends on the configured backend", which does not prepare anyone for a SecurityException before a test body runs; it now shows the error and explains that CI is silently mocked while a laptop is not. TROUBLESHOOTING.md gains the fix for Maven, Gradle and the IDE, and the reason the gate is loud: a run that was not licensed must never look like a run that found no bugs. Deliberately unchanged: the 51 default-constructor warnings, whose fix would add 51 public constructors to satisfy a style rule. Verified on three gates rather than compilation, which stayed silent through every earlier mistake: javadoc:jar 0 errors, PMD 0 violations, mvn verify green across all four modules. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01AENSLJhmSuFzLhXDv4QKUA --- CLAUDE.md | 2 +- README.md | 19 ++- .../.claude/rules/async-test-configuration.md | 6 +- async-test-lib/CLAUDE.md | 2 +- .../deversity/asynctest/AsyncTestConfig.java | 22 ++- .../deversity/asynctest/AsyncTestContext.java | 16 ++- .../se/deversity/asynctest/DetectorType.java | 135 +++++++++++++++++- .../java/se/deversity/asynctest/Preset.java | 8 +- .../benchmark/BenchmarkComparator.java | 5 + .../benchmark/BenchmarkComparisonResult.java | 41 ++++-- .../benchmark/BenchmarkRecorder.java | 6 + .../BenchmarkRegressionException.java | 4 +- .../asynctest/benchmark/BenchmarkResult.java | 55 +++++-- .../diagnostics/ABAProblemDetector.java | 18 ++- .../AtomicNonAtomicUpdateDetector.java | 28 +++- .../diagnostics/AtomicityValidator.java | 6 +- .../asynctest/diagnostics/AutoFix.java | 44 ++++-- .../diagnostics/BlockingQueueDetector.java | 2 + .../BoxedPrimitiveLockDetector.java | 8 +- .../diagnostics/BusyWaitDetector.java | 6 +- .../diagnostics/CacheConcurrencyDetector.java | 2 + .../diagnostics/CalendarDetector.java | 8 +- ...letableFutureBlockingCallbackDetector.java | 12 +- .../CompletableFutureChainDetector.java | 2 + ...tableFutureCommonPoolBlockingDetector.java | 8 +- .../CompletableFutureExceptionDetector.java | 2 + .../CompletableFutureObtrudeDetector.java | 8 +- ...ConcurrentMapComputeRecursionDetector.java | 16 ++- .../ConcurrentModificationDetector.java | 2 + .../ConditionVariableDetector.java | 2 + .../ConstructorSafetyValidator.java | 14 +- .../CopyOnWriteCollectionDetector.java | 8 +- .../diagnostics/CountDownLatchDetector.java | 16 ++- .../diagnostics/CyclicBarrierDetector.java | 26 +++- .../DaemonThreadHygieneDetector.java | 6 +- .../diagnostics/DeadlockDetector.java | 10 +- .../DeprecatedThreadApiDetector.java | 8 +- .../DoubleCheckedLockingDetector.java | 16 ++- .../diagnostics/ExchangerDetector.java | 20 ++- .../diagnostics/ExecutorDeadlockDetector.java | 4 +- .../diagnostics/ExecutorShutdownDetector.java | 4 +- .../diagnostics/ExplicitGcDetector.java | 8 +- .../diagnostics/FalseSharingDetector.java | 12 +- .../FileChannelPositionRaceDetector.java | 4 +- .../FinalFieldMutationDetector.java | 16 ++- .../diagnostics/ForkJoinPoolDetector.java | 31 +++- .../ForkJoinTaskBlockingDetector.java | 20 ++- .../diagnostics/FutureBlockingDetector.java | 4 +- .../diagnostics/FutureIgnoredDetector.java | 8 +- .../GathererConcurrencyMisuseDetector.java | 20 ++- .../HighContentionAtomicDetector.java | 4 +- .../HttpClientConcurrencyDetector.java | 2 + .../InheritableThreadLocalMisuseDetector.java | 4 +- .../diagnostics/InterruptMonitor.java | 6 +- .../InterruptSwallowingDetector.java | 8 +- .../diagnostics/IssueDeduplicator.java | 24 +++- .../asynctest/diagnostics/IssueSeverity.java | 16 ++- .../JdbcConnectionSharedDetector.java | 4 +- .../diagnostics/LatchMisuseDetector.java | 4 +- .../LazyConstantMisuseDetector.java | 32 +++-- .../diagnostics/LazyInitRaceDetector.java | 8 +- .../diagnostics/LazyInitValidator.java | 4 +- .../diagnostics/LearningContent.java | 40 ++++-- .../diagnostics/LivelockDetector.java | 8 +- .../diagnostics/LockContentionDetector.java | 8 +- .../diagnostics/LockDowngradeDetector.java | 4 +- .../diagnostics/LockLeakDetector.java | 2 + .../diagnostics/LockOrderValidator.java | 10 +- .../LockUpgradeDeadlockDetector.java | 15 +- .../diagnostics/MdcContextLeakDetector.java | 8 +- .../diagnostics/MemoryModelValidator.java | 10 +- .../diagnostics/MemoryOrderingMonitor.java | 14 +- .../diagnostics/MissedSignalDetector.java | 8 +- .../diagnostics/MutableMapKeyDetector.java | 4 +- .../NestedMonitorLockoutDetector.java | 4 +- .../NonAtomicConcurrentMapUpdateDetector.java | 4 +- .../diagnostics/NotifyAllValidator.java | 4 +- .../NotifyWithoutMonitorDetector.java | 10 +- .../OptimisticReadValidationDetector.java | 24 +++- .../diagnostics/ParallelStreamDetector.java | 2 + .../diagnostics/Phase1DetectorSet.java | 9 ++ .../asynctest/diagnostics/PhaserDetector.java | 21 ++- .../diagnostics/PipelineMonitor.java | 20 ++- .../PublicLockExposureDetector.java | 8 +- .../diagnostics/RaceConditionDetector.java | 6 +- .../diagnostics/ReadWriteLockMonitor.java | 21 ++- .../diagnostics/ReentrantLockDetector.java | 20 ++- .../diagnostics/ResourceLeakDetector.java | 2 + .../ScheduledExecutorDetector.java | 28 +++- .../ScopedValueMisuseDetector.java | 28 +++- .../diagnostics/SemaphoreMisuseDetector.java | 2 + .../diagnostics/SharedByteBufferDetector.java | 4 +- .../SharedCharsetCoderDetector.java | 4 +- .../diagnostics/SharedChecksumDetector.java | 4 +- .../diagnostics/SharedCollectionDetector.java | 8 +- .../SharedDecimalFormatDetector.java | 8 +- .../diagnostics/SharedDeflaterDetector.java | 4 +- .../diagnostics/SharedFormatterDetector.java | 8 +- .../diagnostics/SharedIteratorDetector.java | 4 +- .../SharedJsonMapperReconfigDetector.java | 4 +- .../diagnostics/SharedKdfDetector.java | 4 +- .../diagnostics/SharedMatcherDetector.java | 8 +- .../SharedMessageDigestDetector.java | 8 +- .../diagnostics/SharedRandomDetector.java | 2 + .../SharedSecureRandomDetector.java | 4 +- .../SharedStatefulCryptoDetector.java | 4 +- .../diagnostics/SharedTimeZoneDetector.java | 8 +- .../diagnostics/SharedXmlParserDetector.java | 8 +- .../diagnostics/SimpleDateFormatDetector.java | 2 + .../asynctest/diagnostics/SiteCapture.java | 8 +- .../diagnostics/SleepInLockDetector.java | 8 +- .../diagnostics/SpuriousWakeupDetector.java | 9 +- .../StableValueMisuseDetector.java | 28 +++- .../diagnostics/StampedLockDetector.java | 33 ++++- .../diagnostics/StatefulLambdaDetector.java | 8 +- .../diagnostics/StreamClosingDetector.java | 2 + .../diagnostics/StringBuilderDetector.java | 8 +- .../StructuredConcurrencyMisuseDetector.java | 24 +++- .../StructuredTaskScopeMisuseDetector.java | 63 ++++++-- ...nchronizedCollectionIterationDetector.java | 15 +- .../SynchronizedNonFinalDetector.java | 8 +- .../SynchronizedOnLiteralDetector.java | 8 +- .../diagnostics/SynchronizerMonitor.java | 17 ++- .../SystemPropertyMutationDetector.java | 8 +- .../diagnostics/ThisEscapeDetector.java | 4 +- .../diagnostics/ThreadFactoryDetector.java | 13 +- .../diagnostics/ThreadLeakDetector.java | 8 +- .../ThreadLocalContaminationDetector.java | 27 +++- .../diagnostics/ThreadLocalMonitor.java | 6 +- .../ThreadLocalRandomMisuseDetector.java | 4 +- .../diagnostics/ThreadPoolMonitor.java | 24 +++- .../diagnostics/ThreadStarvationDetector.java | 10 +- .../asynctest/diagnostics/TimerDetector.java | 8 +- .../diagnostics/TryLockMisuseDetector.java | 13 +- .../diagnostics/UnboundedQueueDetector.java | 10 +- .../UncaughtExceptionHandlerDetector.java | 8 +- .../UncommittedChangesDetector.java | 2 + ...irtualThreadCarrierExhaustionDetector.java | 20 ++- .../VirtualThreadContextLeakDetector.java | 24 +++- .../VirtualThreadCpuBoundTaskDetector.java | 24 +++- .../VirtualThreadPinningDetector.java | 13 ++ .../VirtualThreadStressConfig.java | 32 ++++- .../diagnostics/VisibilityMonitor.java | 11 +- .../diagnostics/VolatileArrayDetector.java | 18 ++- .../diagnostics/WaitTimeoutDetector.java | 21 ++- .../asynctest/diagnostics/WakeupDetector.java | 16 ++- .../WeakHashMapSharedDetector.java | 4 +- .../WeakReferenceRaceDetector.java | 8 +- .../deversity/asynctest/report/Baseline.java | 32 ++++- .../report/JUnitXmlReportListener.java | 2 + .../asynctest/report/JsonReportListener.java | 2 + .../asynctest/runner/ConcurrencyRunner.java | 3 + .../asynctest/runner/LicenseGuard.java | 2 + .../asynctest/spi/DetectorRegistry.java | 29 +++- .../spi/adapters/LegacyDetectorAdapter.java | 6 +- .../SharedMessageDigestDetectorFactory.java | 6 +- .../telemetry/TelemetryEventBuffer.java | 6 +- .../telemetry/TelemetryRegistry.java | 10 +- docs/CHANGELOG.md | 46 ++++++ docs/TROUBLESHOOTING.md | 46 ++++++ 160 files changed, 1773 insertions(+), 298 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 0c303ef1..46e9b458 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -113,7 +113,7 @@ Guardrails for module `async-test-analysis` are maintained in that module's own - Each enum constant requires synchronized changes in five places: (1) @AsyncTest attribute, (2) AsyncTestConfig field, (3) AsyncTestConfig.Builder default, (4) both branches of AsyncTestConfig.build() (detectAll block + excludes block), and (5) DetectorRegistry constructor. Adding a value here in isolation breaks the system. + Adding or removing a constant requires synchronized changes in five places: (1) @AsyncTest attribute, (2) AsyncTestConfig field, (3) AsyncTestConfig.Builder default, (4) the resolution line in AsyncTestConfig.build() ((detectAll || flag) && !excludes.contains(TYPE)), and (5) DetectorRegistry constructor. Adding a value here in isolation compiles and detects nothing. The lock is on the constant set, not the file: editing javadoc on existing constants cannot break that invariant and needs no ceremony. diff --git a/README.md b/README.md index 500aadb7..41bbfabe 100644 --- a/README.md +++ b/README.md @@ -439,13 +439,28 @@ See [intellij-plugin/README.md](intellij-plugin/README.md) for full instructions |---|---| | CI (any `GITHUB_ACTIONS` or `CI` env var set, no key) | Auto-mocked — tests run freely | | Local, no key, `-Dlicense.mock.mode=true` | Mock mode active — tests run freely | -| Local, no key, no mock flag | License gate runs; outcome depends on the configured backend | +| Local, no key, no mock flag | **The gate runs and can refuse.** See below | | Real key via `-Dlicense.key=` | Full validation against the licensing backend | +> **First run on a new machine.** With no key configured and no mock flag, the gate consults the +> licensing backend and a denial throws, before any test body runs: +> +> ``` +> java.lang.SecurityException: LICENSE DENIED: +> To run locally without a key: -Dlicense.mock.mode=true +> In CI (GITHUB_ACTIONS or CI env var set, no key): mock mode activates automatically. +> ``` +> +> This is the gate working as intended, not a bug in your test. CI is unaffected: mock mode turns +> itself on there when no key is present, which is why a suite that passes in CI can still stop on +> a developer laptop. + To run locally without a key during development: ``` mvn test -Dlicense.mock.mode=true ``` -Or add to your IDE's JVM args: `-Dlicense.mock.mode=true` +Or add to your IDE's JVM args: `-Dlicense.mock.mode=true`. Setting it once in your IDE's default +JUnit configuration is the usual fix, so it applies to every run rather than being remembered +per-test. Set your email identity when using a real key: `-Dlicense.user.email=you@example.com` diff --git a/async-test-lib/.claude/rules/async-test-configuration.md b/async-test-lib/.claude/rules/async-test-configuration.md index 3f4eba91..e2d0e653 100644 --- a/async-test-lib/.claude/rules/async-test-configuration.md +++ b/async-test-lib/.claude/rules/async-test-configuration.md @@ -8,14 +8,14 @@ paths: ["**/DetectorType.java", "**/AsyncTestConfig.java", "**/DetectorRegistry. ## Locked Status ### se.deversity.asynctest.DetectorType -- **Reason**: Each enum constant requires synchronized changes in five places: (1) @AsyncTest attribute, (2) AsyncTestConfig field, (3) AsyncTestConfig.Builder default, (4) both branches of AsyncTestConfig.build() (detectAll block + excludes block), and (5) DetectorRegistry constructor. Adding a value here in isolation breaks the system. +- **Reason**: Adding or removing a constant requires synchronized changes in five places: (1) @AsyncTest attribute, (2) AsyncTestConfig field, (3) AsyncTestConfig.Builder default, (4) the resolution line in AsyncTestConfig.build() ((detectAll || flag) && !excludes.contains(TYPE)), and (5) DetectorRegistry constructor. Adding a value here in isolation compiles and detects nothing. The lock is on the constant set, not the file: editing javadoc on existing constants cannot break that invariant and needs no ceremony. ## Mirrored — Keep In Sync ### se.deversity.asynctest.DetectorType - **Rule**: Free to change, but every mirror must change in the same commit. -- **Mirrors**: se.deversity.asynctest.AsyncTest, se.deversity.asynctest.AsyncTestConfig, se.deversity.asynctest.DetectorRegistry, se.deversity.asynctest.spi.LegacyDetectorFactories, META-INF/services/se.deversity.asynctest.spi.DetectorFactory -- **Reason**: A detector is only reachable from the public API when all of these agree. The enum constant is the name users type in @AsyncTest(excludes=...); the annotation attribute, the config field and its Builder default carry it through resolution; the registry constructor instantiates it; and the SPI factory plus its services entry are what detectAll loads. Adding the constant alone compiles and silently detects nothing. +- **Mirrors**: se.deversity.asynctest.AsyncTest, se.deversity.asynctest.AsyncTestConfig, se.deversity.asynctest.DetectorRegistry, se.deversity.asynctest.spi.LegacyDetectorFactories, META-INF/async-test/builtin-detector-factories +- **Reason**: A detector is only reachable from the public API when all of these agree. The enum constant is the name users type in @AsyncTest(excludes=...); the annotation attribute, the config field and its Builder default carry it through resolution; the registry constructor instantiates it; and the SPI factory plus its entry in the built-in factory list are what detectAll loads. Adding the constant alone compiles and silently detects nothing. - **Enforced by**: se.deversity.asynctest.spi.AllDetectorsSpiCoverageTest ## Context & Focus diff --git a/async-test-lib/CLAUDE.md b/async-test-lib/CLAUDE.md index 52b9e1b3..e32af22f 100644 --- a/async-test-lib/CLAUDE.md +++ b/async-test-lib/CLAUDE.md @@ -12,7 +12,7 @@ you are editing here. - Each enum constant requires synchronized changes in five places: (1) @AsyncTest attribute, (2) AsyncTestConfig field, (3) AsyncTestConfig.Builder default, (4) both branches of AsyncTestConfig.build() (detectAll block + excludes block), and (5) DetectorRegistry constructor. Adding a value here in isolation breaks the system. + Adding or removing a constant requires synchronized changes in five places: (1) @AsyncTest attribute, (2) AsyncTestConfig field, (3) AsyncTestConfig.Builder default, (4) the resolution line in AsyncTestConfig.build() ((detectAll || flag) && !excludes.contains(TYPE)), and (5) DetectorRegistry constructor. Adding a value here in isolation compiles and detects nothing. The lock is on the constant set, not the file: editing javadoc on existing constants cannot break that invariant and needs no ceremony. diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/AsyncTestConfig.java b/async-test-lib/src/main/java/se/deversity/asynctest/AsyncTestConfig.java index 8af5de62..40c0b1b7 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/AsyncTestConfig.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/AsyncTestConfig.java @@ -528,7 +528,12 @@ private AsyncTestConfig(Builder b) { licenseMockMode = b.licenseMockMode; } - /** Builds a config from an {@link AsyncTest} annotation instance. */ + /** + * Builds a config from an {@link AsyncTest} annotation instance. + * + * @param ann the ann + * @return the from + */ public static AsyncTestConfig from(AsyncTest ann) { return from(ann, ann.threads()); } @@ -540,6 +545,10 @@ public static AsyncTestConfig from(AsyncTest ann) { * other annotation fields. * * @since 1.6.0 + * + * @param ann the ann + * @param threadsOverride the threads override + * @return the from */ public static AsyncTestConfig from(AsyncTest ann, int threadsOverride) { // Check for global benchmarking system property @@ -725,7 +734,9 @@ public static AsyncTestConfig from(AsyncTest ann, int threadsOverride) { .build(); } - /** {@return a new builder initialised with the library defaults} */ + /** + * {@return a new builder initialised with the library defaults} + */ public static Builder builder() { return new Builder(); } @@ -1768,6 +1779,9 @@ public Builder excludes(DetectorType[] v) { * {@link #excludes(DetectorType[])} still layers on top. * * @since 1.7.0 + * + * @param v the v + * @return the includes */ public Builder includes(DetectorType[] v) { if (v != null && v.length > 0) { @@ -1776,7 +1790,9 @@ public Builder includes(DetectorType[] v) { return this; } - /** {@return the resolved configuration, with preset, includes and excludes applied} */ + /** + * {@return the resolved configuration, with preset, includes and excludes applied} + */ public AsyncTestConfig build() { if (!includes.isEmpty()) { // includes wins over detectAll/per-flag setters: force the diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/AsyncTestContext.java b/async-test-lib/src/main/java/se/deversity/asynctest/AsyncTestContext.java index 8e1ed4b0..adb7e37a 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/AsyncTestContext.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/AsyncTestContext.java @@ -630,13 +630,19 @@ public AsyncTestContext(AsyncTestConfig cfg) { // ---- Lifecycle (called by ConcurrencyRunner) ---- - /** Installs {@code ctx} into the calling thread's ThreadLocal. */ + /** + * Installs {@code ctx} into the calling thread's ThreadLocal. + * + * @param ctx the ctx + */ @AICallersOnly({"se.deversity.asynctest.runner.ConcurrencyRunner"}) public static void install(AsyncTestContext ctx) { CURRENT.set(ctx); } - /** Removes the context from the calling thread's ThreadLocal. */ + /** + * Removes the context from the calling thread's ThreadLocal. + */ @AIIdempotent(reason = "ThreadLocal.remove() is documented as a no-op when the thread has no value set; the install/uninstall symmetry rule (CLAUDE.md) tolerates extra uninstalls. ConcurrencyRunner relies on this in its outermost-finally cleanup.") public static void uninstall() { CURRENT.remove(); @@ -682,7 +688,11 @@ public static long replaySeed() { return ctx == null ? 0L : ctx.currentRoundSeed; } - /** Internal: set by {@code ConcurrencyRunner} before each invocation round. */ + /** + * Internal: set by {@code ConcurrencyRunner} before each invocation round. + * + * @param seed the seed + */ public void setReplaySeedForRound(long seed) { this.currentRoundSeed = seed; } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/DetectorType.java b/async-test-lib/src/main/java/se/deversity/asynctest/DetectorType.java index 6e0fef97..fc387aa4 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/DetectorType.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/DetectorType.java @@ -10,192 +10,319 @@ * Enumerates all available detectors for type-safe opt-outs. * Used with {@link AsyncTest#excludes()}. */ -@AILocked(reason = "Each enum constant requires synchronized changes in five places: (1) @AsyncTest attribute, (2) AsyncTestConfig field, (3) AsyncTestConfig.Builder default, (4) both branches of AsyncTestConfig.build() (detectAll block + excludes block), and (5) DetectorRegistry constructor. Adding a value here in isolation breaks the system.") +@AILocked(reason = "Adding or removing a constant requires synchronized changes in five places: (1) @AsyncTest attribute, (2) AsyncTestConfig field, (3) AsyncTestConfig.Builder default, (4) the resolution line in AsyncTestConfig.build() ((detectAll || flag) && !excludes.contains(TYPE)), and (5) DetectorRegistry constructor. Adding a value here in isolation compiles and detects nothing. The lock is on the constant set, not the file: editing javadoc on existing constants cannot break that invariant and needs no ceremony.") @AIKeepInSync( mirrors = { "se.deversity.asynctest.AsyncTest", "se.deversity.asynctest.AsyncTestConfig", "se.deversity.asynctest.DetectorRegistry", "se.deversity.asynctest.spi.LegacyDetectorFactories", - "META-INF/services/se.deversity.asynctest.spi.DetectorFactory" + "META-INF/async-test/builtin-detector-factories" }, reason = "A detector is only reachable from the public API when all of these agree. The enum " + "constant is the name users type in @AsyncTest(excludes=...); the annotation attribute, " + "the config field and its Builder default carry it through resolution; the registry " - + "constructor instantiates it; and the SPI factory plus its services entry are what " - + "detectAll loads. Adding the constant alone compiles and silently detects nothing.", + + "constructor instantiates it; and the SPI factory plus its entry in the built-in factory list are " + + "what detectAll loads. Adding the constant alone compiles and silently detects nothing.", enforcedBy = "se.deversity.asynctest.spi.AllDetectorsSpiCoverageTest" ) @API(status = Status.STABLE) public enum DetectorType { // Phase 1 + /** Enhanced deadlock detector that analyzes thread dumps and identifies circular lock dependencies, thread states, and provides actionable diagnostics. */ DEADLOCKS, + /** Monitors field access patterns to detect visibility issues (stale memory). */ VISIBILITY, + /** Detects thread starvation and livelocks. */ LIVELOCKS, // Phase 2: Core + /** Detects False Sharing - when multiple threads access adjacent memory locations that fall within the same CPU cache line (typically 64 bytes). */ FALSE_SHARING, + /** Detects spurious wakeups and lost notifications in wait/notify patterns. */ WAKEUP_ISSUES, + /** Validates that objects are fully constructed before being shared across threads. */ CONSTRUCTOR_SAFETY, + /** Detects the ABA Problem in atomic operations. */ ABA_PROBLEM, + /** Detects lock ordering violations that can cause deadlocks. */ LOCK_ORDER, + /** Monitors synchronizer behavior (CyclicBarrier, Phaser, CountDownLatch, etc.) Problems detected: - Threads not advancing synchronously through barriers - Phaser . */ SYNCHRONIZERS, + /** Monitors thread pool / executor health and issues. */ THREAD_POOL, + /** Detects memory ordering violations and compiler reordering issues. */ MEMORY_ORDERING, + /** Monitors event flow in async pipelines and detects signal loss. */ ASYNC_PIPELINE, + /** Monitors ReadWriteLock fairness and detects writer starvation. */ READ_WRITE_LOCK_FAIRNESS, // Phase 2: Monitors + /** Detects semaphore misuse patterns in concurrent code. */ SEMAPHORE, + /** Detects exception handling issues in CompletableFuture chains. */ COMPLETABLE_FUTURE_EXCEPTIONS, + /** Detects CompletableFuture instances that are created but never completed. */ COMPLETABLE_FUTURE_COMPLETION_LEAKS, + /** Detects virtual thread pinning issues. */ VIRTUAL_THREAD_PINNING, + /** Detects thread pool deadlock scenarios. */ THREAD_POOL_DEADLOCK, + /** Detects concurrent modification issues in collections during iteration. */ CONCURRENT_MODIFICATIONS, + /** Detects lock leak patterns where locks are acquired but never released. */ LOCK_LEAKS, + /** Detects concurrent use of non-thread-safe Random instances. */ SHARED_RANDOM, + /** Detects BlockingQueue misuse patterns in concurrent code. */ BLOCKING_QUEUE, + /** Detects Condition variable misuse patterns in concurrent code. */ CONDITION_VARIABLES, + /** Detects concurrent use of non-thread-safe SimpleDateFormat instances. */ SIMPLE_DATE_FORMAT, + /** Detects unsafe operations in parallel streams. */ PARALLEL_STREAMS, + /** Detects resource leak patterns in concurrent code. */ RESOURCE_LEAKS, // Phase 2: Additional Concurrency + /** Detects CountDownLatch misuse patterns: - Latch timeout (await with timeout expiring) - Missing countDown (latch never reaches zero) - Extra countDown (more cou. */ COUNTDOWN_LATCH, + /** Detects CyclicBarrier misuse patterns: - Barrier timeout (await with timeout expiring) - Broken barrier (barrier broken due to thread interruption or timeout) -. */ CYCLIC_BARRIER, + /** Detects ReentrantLock misuse patterns: - Lock starvation (thread waiting excessively long) - Unfair lock acquisition (threads not acquiring in FIFO order) - Loc. */ REENTRANT_LOCK, + /** Detects volatile array element visibility issues. */ VOLATILE_ARRAY, + /** Detects broken double-checked locking patterns. */ DOUBLE_CHECKED_LOCKING, + /** Detects wait/notify patterns without timeout (potential deadlock). */ WAIT_TIMEOUT, + /** Detects high lock contention — monitors where many threads compete to acquire the same lock, causing threads to spend significant time in BLOCKED state. */ LOCK_CONTENTION, + /** Detects the anti-pattern of synchronizing on a non-final, reassignable object reference. */ SYNCHRONIZED_NON_FINAL, + /** Detects missed (lost) signals — situations where notify() or notifyAll() is called on a condition before any thread is waiting on it, causing the signal to be s. */ MISSED_SIGNAL, + /** Detects lazy-initialization races — situations where multiple threads simultaneously observe a field as null and each proceeds to initialize it, causing the ini. */ LAZY_INIT_RACE, // Phase 2: Advanced Concurrency Utilities + /** Detects Phaser misuse patterns: - Missing arrive() calls (phaser never advances) - Phaser timeout (awaitAdvance with timeout expiring) - Phaser termination (pha. */ PHASER, + /** Detects StampedLock misuse patterns: - Optimistic read without validation - Lock upgrade issues (optimistic → write) - Stamp not released in finally block - Wro. */ STAMPED_LOCK, + /** Detects Exchanger misuse patterns: - Exchange timeout (exchange with timeout expiring) - Missing exchange partner (odd number of threads) - InterruptedException. */ EXCHANGER, + /** Detects ScheduledExecutorService misuse patterns: - Task scheduling without proper shutdown - Fixed delay vs fixed rate confusion - Long-running tasks blocking . */ SCHEDULED_EXECUTOR, + /** Detects ForkJoinPool misuse patterns: - Fork without join - RecursiveTask not returning result - Pool starvation (too few threads) - Exception in forked tasks. */ FORK_JOIN_POOL, + /** Detects ThreadFactory misuse patterns: - Missing uncaught exception handler - Non-daemon threads in thread pools - Missing thread naming convention - Thread pri. */ THREAD_FACTORY, // Phase 3 + /** Detects potential race conditions by tracking cross-thread field accesses. */ RACE_CONDITIONS, + /** Monitors ThreadLocal lifecycle usage to detect leaks and poor cleanup. */ THREAD_LOCAL_LEAKS, + /** Detects spin loops that perform excessive work before yielding or blocking. */ BUSY_WAITING, + /** Tracks compound operations that should behave atomically. */ ATOMICITY_VIOLATIONS, + /** Tracks caught interrupts and whether they were restored or ignored. */ INTERRUPT_MISHANDLING, // Phase 4: Infrastructure & Resource Management + /** Detects thread leaks in concurrent code. */ THREAD_LEAKS, + /** Detects Thread.sleep() calls while holding a lock. */ SLEEP_IN_LOCK, + /** Detects unbounded queue usage in concurrent code. */ UNBOUNDED_QUEUE, + /** Detects thread starvation in thread pools. */ THREAD_STARVATION, // Phase 5: Thread-Safety of Common Types + /** Detects concurrent use of non-thread-safe java.util.Calendar instances. */ CALENDAR, + /** Detects non-thread-safe collections shared across multiple threads without synchronization. */ SHARED_COLLECTIONS, + /** Detects misuse of java.util.Timer in concurrent code. */ TIMER, + /** Detects CopyOnWriteArrayList and CopyOnWriteArraySet used in write-heavy concurrent scenarios where the copy-on-write overhead becomes a significant performance. */ COPY_ON_WRITE_COLLECTIONS, + /** Detects StringBuilder instances shared across multiple threads without synchronization. */ STRING_BUILDER, // Phase 6: Virtual Thread Concurrency (Java 21+) + /** Detects misuse of Java 21+ Structured Concurrency (StructuredTaskScope). */ STRUCTURED_CONCURRENCY, + /** Detects ThreadLocal context leaks in virtual threads. */ VIRTUAL_THREAD_CONTEXT_LEAKS, + /** Detects misuse of Java 21+ ScopedValue. */ SCOPED_VALUE, + /** Detects CPU-bound tasks running on virtual threads. */ VIRTUAL_THREAD_CPU_BOUND, + /** Detects potential carrier thread exhaustion caused by concurrent blocking of virtual threads. */ VIRTUAL_THREAD_CARRIER_EXHAUSTION, // Phase 7: High-Level Concurrency Patterns + /** Detects HTTP client concurrency issues, particularly with Java 11+ HttpClient. */ HTTP_CLIENT, + /** Detects I/O stream (InputStream, OutputStream, Reader, Writer) not being properly closed in concurrent code. */ STREAM_CLOSING, + /** Detects concurrent access to non-thread-safe cache implementations. */ CACHE_CONCURRENCY, + /** Detects improper CompletableFuture chain usage in concurrent code. */ COMPLETABLEFUTURE_CHAIN, // Phase 8: Lifecycle & Structural Correctness + /** Detects ExecutorService instances that are created and used but never properly shut down, or shut down without a subsequent awaitTermination() call. */ EXECUTOR_SHUTDOWN, + /** Detects mutable objects used as java.util.HashMap / java.util.HashSet keys that are mutated after insertion. */ MUTABLE_MAP_KEY, + /** Detects the nested monitor lockout anti-pattern: performing a blocking operation (e.g. */ NESTED_MONITOR_LOCKOUT, + /** Detects incorrect java.util.concurrent.locks.ReentrantReadWriteLock downgrade and upgrade patterns. */ LOCK_DOWNGRADE, + /** Detects misuse of InheritableThreadLocal in thread-pool environments. */ INHERITABLE_THREAD_LOCAL, // Phase 9: Repository & Environment State + /** Detects untracked or uncommitted changes in the Git repository. */ UNCOMMITTED_CHANGES, // Phase 10: API Traps & Subtle Concurrency Bugs + /** Detects ThreadLocal values that bleed from one task into the next task executing on the same pooled thread — cross-task state contamination. */ THREAD_LOCAL_CONTAMINATION, + /** Detects non-atomic compound updates on AtomicInteger, AtomicLong, AtomicReference, and similar: using get() then set() instead of compareAndSet(), silently losi. */ ATOMIC_NON_ATOMIC_UPDATE, + /** Detects iteration over Collections#synchronizedList, Collections#synchronizedMap, or Collections#synchronizedSet wrappers without holding the wrapper's intrinsi. */ SYNCHRONIZED_COLLECTION_ITERATION, + /** Detects java.util.Formatter, java.io.PrintWriter, and java.io.PrintStream instances shared across multiple threads without external synchronization. */ SHARED_FORMATTER, + /** Detects recursive calls to ConcurrentHashMap#computeIfAbsent (or compute / computeIfPresent / merge) on the same map and key from the same thread — a well-known. */ CONCURRENT_MAP_COMPUTE_RECURSION, + /** Detects synchronized blocks that lock on interned String literals or JVM-cached boxed primitives (Integer / Long in the range [-128, 127]). */ SYNCHRONIZED_ON_LITERAL, + /** Detects classes that use synchronized(this) (or synchronized instance methods) while this is publicly accessible — exposing the internal lock to external caller. */ PUBLIC_LOCK_EXPOSURE, + /** Detects blocking calls (Thread#sleep, Object#wait, Future.get(), blocking I/O) made from within a java.util.concurrent.ForkJoinTask body. */ FORK_JOIN_TASK_BLOCKING, + /** Detects incorrect usage of java.util.concurrent.locks.StampedLock optimistic reads: reading data after tryOptimisticRead() without calling validate(stamp), or c. */ OPTIMISTIC_READ_VALIDATION, + /** Detects blocking operations (Thread#sleep, Object#wait, blocking I/O, Future.get()) running inside CompletableFuture stages that were submitted to the common Fo. */ CF_COMMON_POOL_BLOCKING, // Phase 11: Thread-Safety of Additional Types & Patterns + /** Detects java.util.regex.Matcher instances shared across multiple threads. */ SHARED_MATCHER, + /** Detects java.text.DecimalFormat and java.text.NumberFormat instances shared across multiple threads without external synchronization. */ SHARED_DECIMAL_FORMAT, + /** Detects race conditions around java.lang.ref.WeakReference and java.lang.ref.SoftReference get() calls. */ WEAK_REFERENCE_RACE, + /** Detects lambda / Runnable / java.util.concurrent.Callable instances whose captured mutable state is mutated concurrently from multiple threads. */ STATEFUL_LAMBDA, + /** Detects java.security.MessageDigest instances shared across multiple threads. */ SHARED_MESSAGE_DIGEST, // Phase 12: Operational & Hygiene Concurrency Issues + /** Detects InterruptedException catches where the interrupt flag is silently swallowed. */ INTERRUPT_SWALLOWING, + /** Detects SLF4J MDC (Mapped Diagnostic Context) entries that are not cleared at task end, causing leakage to the next task run on the same pooled thread. */ MDC_CONTEXT_LEAK, + /** Detects concurrent mutations to JVM system properties via System#setProperty or System#clearProperty during an async test run. */ SYSTEM_PROPERTY_MUTATION, + /** Detects java.util.concurrent.Future instances returned from java.util.concurrent.ExecutorService#submit (or similar) that are never inspected. */ FUTURE_IGNORED, + /** Detects explicit garbage-collection invocations (System#gc() or Runtime#gc()) during a concurrent test run. */ EXPLICIT_GC, + /** Detects use of deprecated and unsafe Thread API methods: Thread.stop(), Thread.suspend(), Thread.resume(), Thread.destroy(), and Thread.countStackFrames(). */ DEPRECATED_THREAD_API, + /** Detects XML parser instances shared across multiple threads. */ SHARED_XML_PARSER, + /** Detects synchronized blocks that lock on cached boxed primitives or on JEP 390 value-based classes. */ BOXED_PRIMITIVE_LOCK, + /** Detects java.util.TimeZone instances whose mutable state is modified while being accessed from multiple threads. */ SHARED_TIMEZONE, + /** Detects threads that are started without a custom Thread.UncaughtExceptionHandler and that subsequently throw an uncaught exception. */ UNCAUGHT_EXCEPTION_HANDLER, // Phase 13: Additional concurrency-bug categories (1.0.0+) + /** Detects Thread instances created by user code without Thread#setDaemon(boolean) setDaemon(true) that remain alive at detector tear-down. */ DAEMON_THREAD_HYGIENE, + /** Detects attempted Object#notify() / Object#notifyAll() calls where the calling thread does not hold the target monitor. */ NOTIFY_WITHOUT_MONITOR, + /** Detects SecureRandom instances accessed from multiple threads. */ SHARED_SECURE_RANDOM, + /** Detects WeakHashMap or IdentityHashMap instances accessed from more than one thread. */ WEAK_HASH_MAP_SHARED, + /** Detects Connection, Statement, PreparedStatement, or ResultSet instances accessed from more than one thread. */ JDBC_CONNECTION_SHARED, // Phase 14: Additional thread-unsafe primitives & publication hazards (1.7.0+) + /** Detects stateful JCA cryptographic primitives — Cipher, Mac, and Signature — shared across multiple threads. */ SHARED_STATEFUL_CRYPTO, + /** Detects non-atomic check-then-act compound operations on a ConcurrentMap. */ CONCURRENT_MAP_CHECK_THEN_ACT, + /** Detects Deflater / Inflater instances shared across threads. */ SHARED_DEFLATER, + /** Detects this-escape: a constructor publishing a reference to the object being built before construction finishes. */ THIS_ESCAPE, + /** Detects misuse of ThreadLocalRandom: caching the reference returned by ThreadLocalRandom#current() and using it from a different thread. */ THREAD_LOCAL_RANDOM_MISUSE, // Phase 15: Asynchronous flow & lock-usage hazards (1.8.0+) + /** Detects CompletableFuture.obtrudeValue() or obtrudeException() calls which bypass normal completion pipelines and trigger race conditions or state inconsistency. */ COMPLETABLE_FUTURE_OBTRUDE_ABUSE, + /** Detects wait() or Condition.await() calls invoked outside of a while loop condition check, exposing the thread to spurious wakeups. */ SPURIOUS_WAKEUP_HAZARD, + /** Detects attempts to upgrade a ReentrantReadWriteLock from a read lock to a write lock on the same thread, which inevitably deadlocks. */ LOCK_UPGRADE_DEADLOCK, + /** Detects misuse of Lock.tryLock(), such as calling unlock() unconditionally when tryLock() returned false. */ TRY_LOCK_MISUSE, + /** Detects blocking calls (like get(), join(), sleep()) inside CompletableFuture callback pipelines, which can cause pool thread starvation or deadlocks. */ COMPLETABLE_FUTURE_BLOCKING_CALLBACK, // Phase 16: JDK 25/26 preview-era concurrency detectors + /** Detects misuse of Java 25+ StableValue (JEP 502 — Stable Values, Preview in JDK 25, continuing in JDK 26). */ STABLE_VALUE_MISUSE, + /** Detects misuse of the StructuredTaskScope API (JEP 505/525 — Structured Concurrency; fifth preview in JDK 25, sixth preview in JDK 26). */ STRUCTURED_TASK_SCOPE_MISUSE, + /** Detects unsafe use of Stream.gather(Gatherer) (JEP 485 — Stream Gatherers, finalized in JDK 24 and the standard intermediate-operation extension point in JDK 25. */ GATHERER_CONCURRENCY_MISUSE, // Phase 17: Shared stateful JDK objects, I/O position races & contention advisories + /** Detects java.nio.Buffer / java.nio.ByteBuffer instances shared across threads without coordination. */ SHARED_BYTE_BUFFER, + /** Detects CharsetEncoder / CharsetDecoder instances shared across threads. */ SHARED_CHARSET_CODER, + /** Detects Checksum implementations (e.g. */ SHARED_CHECKSUM, + /** Detects FileChannel / SeekableByteChannel instances whose implicit position is read or mutated from more than one thread. */ FILE_CHANNEL_POSITION_RACE, + /** Detects a single Iterator, ListIterator, or Spliterator instance being driven from more than one thread. */ SHARED_ITERATOR, + /** Advisory detector for hot compare-and-swap loops on shared AtomicLong/AtomicInteger/AtomicReference instances that would perform better as LongAdder/LongAccumul. */ HIGH_CONTENTION_ATOMIC, + /** Detects serializer/mapper instances (Jackson ObjectMapper, a Gson built via GsonBuilder, or similar) that are reconfigured after concurrent use has begun. */ SHARED_JSON_MAPPER_RECONFIG, // Phase 18: JDK 25/26 GA-era concurrency detectors (1.8.0+) + /** Detects misuse of Java 26+ LazyConstant (Lazy Constants, second preview in JDK 26 — the renamed and radically simplified successor of the JDK 25 StableValue pre. */ LAZY_CONSTANT_MISUSE, + /** Detects reflective mutation of final fields (Field.setAccessible(true) + Field.set(...)), which JDK 26 warns about and future JDK releases will deny by default . */ FINAL_FIELD_MUTATION, + /** Detects javax.crypto.KDF (Key Derivation Function, JEP 510 — final in JDK 25) instances shared across threads. */ SHARED_KDF, // Executor / future / latch detectors that shipped implemented and tested but unwired + /** Detects CountDownLatch-style misuse such as missing or extra countdowns. */ LATCH_MISUSE, + /** Detects self-deadlock patterns in single-thread or bounded executors. */ EXECUTOR_DEADLOCK, + /** Detects blocking waits on sibling futures inside bounded executors. */ FUTURE_BLOCKING } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/Preset.java b/async-test-lib/src/main/java/se/deversity/asynctest/Preset.java index 57ac35f8..0c6e3e68 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/Preset.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/Preset.java @@ -105,13 +105,19 @@ public enum Preset { * lets callers distinguish "use everything available right now" from "use * exactly this set", which matters when new detectors are added in future * releases. + * + * @return the enabled */ @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "enabled is already an unmodifiable Set.copyOf snapshot stored in a final field") public @Nullable Set enabled() { return enabled; } - /** True when the preset is the legacy default. */ + /** + * True when the preset is the legacy default. + * + * @return the is all + */ public boolean isAll() { return this == ALL || this == STRICT; } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkComparator.java b/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkComparator.java index 1a035a99..197c4675 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkComparator.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkComparator.java @@ -170,6 +170,9 @@ private String buildRegressionMessage(BenchmarkComparisonResult result) { /** * Load baseline for a specific benchmark key. + * + * @param benchmarkKey the benchmark key + * @return the load baseline */ public Optional loadBaseline(String benchmarkKey) { File storeFile = benchmarkStorePath.toFile(); @@ -224,6 +227,8 @@ private Map readStore(File storeFile) throws IOExceptio /** * Save a benchmark result as the new baseline. + * + * @param result the result */ public void saveBaseline(BenchmarkResult result) { Map store = loadAllBaselines(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkComparisonResult.java b/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkComparisonResult.java index 9438ef42..e9edd3d0 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkComparisonResult.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkComparisonResult.java @@ -29,6 +29,9 @@ private BenchmarkComparisonResult(Builder builder) { /** * Create a result for the first run (no baseline exists). + * + * @param currentResult the current result + * @return the first run */ public static BenchmarkComparisonResult firstRun(BenchmarkResult currentResult) { return builder() @@ -41,43 +44,59 @@ public static BenchmarkComparisonResult firstRun(BenchmarkResult currentResult) .build(); } - /** {@return the current result} */ + /** + * {@return the current result} + */ public @Nullable BenchmarkResult getCurrentResult() { return currentResult; } - /** {@return the baseline result} */ + /** + * {@return the baseline result} + */ public @Nullable BenchmarkResult getBaselineResult() { return baselineResult; } - /** {@return the percent change} */ + /** + * {@return the percent change} + */ public double getPercentChange() { return percentChange; } - /** {@return whether regression} */ + /** + * {@return whether regression} + */ public boolean isRegression() { return isRegression; } - /** {@return whether improvement} */ + /** + * {@return whether improvement} + */ public boolean isImprovement() { return isImprovement; } - /** {@return whether first run} */ + /** + * {@return whether first run} + */ public boolean isFirstRun() { return isFirstRun; } - /** {@return the threshold percent} */ + /** + * {@return the threshold percent} + */ public double getThresholdPercent() { return thresholdPercent; } /** * Check if the result is within acceptable bounds (not a regression or improvement). + * + * @return the is within threshold */ public boolean isWithinThreshold() { return !isRegression && !isImprovement; @@ -128,7 +147,9 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(currentResult, baselineResult, percentChange, isRegression, isImprovement, isFirstRun); } - /** {@return the builder} */ + /** + * {@return the builder} + */ public static Builder builder() { return new Builder(); @@ -219,7 +240,9 @@ public Builder thresholdPercent(double thresholdPercent) { this.thresholdPercent = thresholdPercent; return this; } - /** {@return the build} */ + /** + * {@return the build} + */ public BenchmarkComparisonResult build() { return new BenchmarkComparisonResult(this); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkRecorder.java b/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkRecorder.java index 509be453..30ae6eb1 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkRecorder.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkRecorder.java @@ -86,6 +86,8 @@ public BenchmarkRecorder(AsyncTestConfig config, String testClass, String testMe /** * Check if benchmarking is enabled. + * + * @return the is benchmarking enabled */ public boolean isBenchmarkingEnabled() { return benchmarkingEnabled; @@ -209,6 +211,8 @@ private void printComparisonResult(BenchmarkComparisonResult result) { /** * Get the total execution time in nanoseconds. + * + * @return the get total execution time in nanoseconds */ public long getTotalExecutionTimeNanos() { return System.nanoTime() - startTimeNanos; @@ -216,6 +220,8 @@ public long getTotalExecutionTimeNanos() { /** * Get the number of recorded invocations. + * + * @return the get invocation count */ public int getInvocationCount() { synchronized (invocationTimesNanos) { diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkRegressionException.java b/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkRegressionException.java index c7de821d..0957ec0d 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkRegressionException.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkRegressionException.java @@ -19,7 +19,9 @@ public BenchmarkRegressionException(String message, BenchmarkComparisonResult co this.comparisonResult = comparisonResult; } - /** {@return the comparison result} */ + /** + * {@return the comparison result} + */ public @Nullable BenchmarkComparisonResult getComparisonResult() { return comparisonResult; } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkResult.java b/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkResult.java index 6bc49a13..ecfe372f 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkResult.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkResult.java @@ -42,58 +42,80 @@ private BenchmarkResult(Builder builder) { this.invocationTimesNanos = new ArrayList<>(builder.invocationTimesNanos); } - /** {@return the test class} */ + /** + * {@return the test class} + */ public @Nullable String getTestClass() { return testClass; } - /** {@return the test method} */ + /** + * {@return the test method} + */ public @Nullable String getTestMethod() { return testMethod; } - /** {@return the timestamp} */ + /** + * {@return the timestamp} + */ public LocalDateTime getTimestamp() { return timestamp; } - /** {@return the threads} */ + /** + * {@return the threads} + */ public int getThreads() { return threads; } - /** {@return the invocations} */ + /** + * {@return the invocations} + */ public int getInvocations() { return invocations; } - /** {@return the total execution time in nanoseconds} */ + /** + * {@return the total execution time in nanoseconds} + */ public long getTotalExecutionTimeNanos() { return totalExecutionTimeNanos; } - /** {@return the avg time per invocation in nanoseconds} */ + /** + * {@return the avg time per invocation in nanoseconds} + */ public long getAvgTimePerInvocationNanos() { return avgTimePerInvocationNanos; } - /** {@return the min time per invocation in nanoseconds} */ + /** + * {@return the min time per invocation in nanoseconds} + */ public long getMinTimePerInvocationNanos() { return minTimePerInvocationNanos; } - /** {@return the max time per invocation in nanoseconds} */ + /** + * {@return the max time per invocation in nanoseconds} + */ public long getMaxTimePerInvocationNanos() { return maxTimePerInvocationNanos; } - /** {@return the invocation times in nanoseconds} */ + /** + * {@return the invocation times in nanoseconds} + */ public List getInvocationTimesNanos() { return Collections.unmodifiableList(invocationTimesNanos); } /** * Get a unique key for this benchmark (class + method). + * + * @return the get benchmark key */ public String getBenchmarkKey() { return testClass + "#" + testMethod; @@ -101,6 +123,8 @@ public String getBenchmarkKey() { /** * Calculate the standard deviation of invocation times. + * + * @return the get standard deviation */ public double getStandardDeviation() { if (invocationTimesNanos.size() <= 1) { @@ -117,6 +141,9 @@ public double getStandardDeviation() { /** * Format time in nanoseconds to a human-readable string. + * + * @param nanos the in nanoseconds + * @return the format time */ public static String formatTime(long nanos) { if (nanos < 1_000) { @@ -160,7 +187,9 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(testClass, testMethod, timestamp); } - /** {@return the builder} */ + /** + * {@return the builder} + */ public static Builder builder() { return new Builder(); @@ -287,7 +316,9 @@ public Builder invocationTimesNanos(List invocationTimesNanos) { this.invocationTimesNanos = new ArrayList<>(invocationTimesNanos); return this; } - /** {@return the build} */ + /** + * {@return the build} + */ public BenchmarkResult build() { return new BenchmarkResult(this); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ABAProblemDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ABAProblemDetector.java index feb731de..86a844e9 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ABAProblemDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ABAProblemDetector.java @@ -75,6 +75,10 @@ private static class CASAttempt { /** * Record a value change in an atomic variable. + * + * @param variableName the variable name + * @param oldValue the old value + * @param newValue the new value */ public void recordValueChange(String variableName, Object oldValue, Object newValue) { if (!enabled) return; @@ -94,6 +98,12 @@ public void recordValueChange(String variableName, Object oldValue, Object newVa /** * Record a CAS (Compare-And-Swap) attempt. + * + * @param variableName the variable name + * @param expectedValue the expected value + * @param newValue the new value + * @param succeeded the {@code succeeded} flag + * @param actualCurrentValue the actual current value */ public void recordCASAttempt(String variableName, Object expectedValue, Object newValue, boolean succeeded, Object actualCurrentValue) { @@ -183,6 +193,8 @@ private boolean detectABA(AtomicValueHistory history, CASAttempt attempt) { /** * Analyze for ABA problems. + * + * @return the analyze ABA */ public ABAReport analyzeABA() { ABAReport report = new ABAReport(); @@ -209,6 +221,8 @@ public ABAReport analyzeABA() { /** * Standardized alias for {@link #analyzeABA()}. + * + * @return the analyze */ public ABAReport analyze() { return analyzeABA(); @@ -241,7 +255,9 @@ public static class ABAReport { /** The successful ABA cases. */ public final Set successfulABACases = new HashSet<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !variablesWithCycles.isEmpty() || !successfulABACases.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/AtomicNonAtomicUpdateDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/AtomicNonAtomicUpdateDetector.java index f39ae03f..8533d746 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/AtomicNonAtomicUpdateDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/AtomicNonAtomicUpdateDetector.java @@ -45,7 +45,13 @@ private AtomicState stateFor(Object atomic, String name) { id -> new AtomicState(name != null ? name : "Atomic@" + id)); } - /** Call after {@code atomic.get()}. */ + /** + * Call after {@code atomic.get()}. + * + * @param atomic the atomic + * @param name the name + * @param thread the thread + */ public void recordGet(Object atomic, String name, Thread thread) { if (atomic == null || thread == null) return; stateFor(atomic, name).pendingGetByThread.put(thread.threadId(), 1); @@ -55,6 +61,10 @@ public void recordGet(Object atomic, String name, Thread thread) { * Call after {@code atomic.set()} (a non-CAS write). * If the same thread previously called {@link #recordGet} without an intervening CAS, * the sequence is flagged as a lost-update race. + * + * @param atomic the atomic + * @param name the name + * @param thread the thread */ public void recordSet(Object atomic, String name, Thread thread) { if (atomic == null || thread == null) return; @@ -68,13 +78,21 @@ public void recordSet(Object atomic, String name, Thread thread) { } } - /** Call after a successful {@code atomic.compareAndSet()} — clears the pending-get flag. */ + /** + * Call after a successful {@code atomic.compareAndSet()} — clears the pending-get flag. + * + * @param atomic the atomic + * @param name the name + * @param thread the thread + */ public void recordCas(Object atomic, String name, Thread thread) { if (atomic == null || thread == null) return; stateFor(atomic, name).pendingGetByThread.remove(thread.threadId()); } - /** {@return report of non-atomic compound updates} */ + /** + * {@return report of non-atomic compound updates} + */ public AtomicNonAtomicUpdateReport analyze() { AtomicNonAtomicUpdateReport r = new AtomicNonAtomicUpdateReport(); for (AtomicState s : atomics.values()) { @@ -92,7 +110,9 @@ public static class AtomicNonAtomicUpdateReport { final List violations = new ArrayList<>(); final List details = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/AtomicityValidator.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/AtomicityValidator.java index cab7d11a..dba21b16 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/AtomicityValidator.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/AtomicityValidator.java @@ -208,6 +208,8 @@ public AtomicityReport analyzeAtomicity() { /** * Standardized alias for {@link #analyzeAtomicity()}. + * + * @return the analyze */ public AtomicityReport analyze() { return analyzeAtomicity(); @@ -248,7 +250,9 @@ public static class AtomicityReport { /** The totcou races. */ public final Set totcouRaces = new HashSet<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !checkThenActViolations.isEmpty() || !unsafeFieldAccesses.isEmpty() diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/AutoFix.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/AutoFix.java index 9c655bf8..5ab981ac 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/AutoFix.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/AutoFix.java @@ -13,7 +13,9 @@ private AutoFix() { } // ============= Deadlock Fixes ============= - /** {@return the deadlock fix} */ + /** + * {@return the deadlock fix} + */ public static String getDeadlockFix() { return """ 💡 AUTO-FIX: How to Fix This Deadlock @@ -61,7 +63,9 @@ public static String getDeadlockFix() { // ============= Race Condition Fixes ============= - /** {@return the race condition fix} */ + /** + * {@return the race condition fix} + */ public static String getRaceConditionFix() { return """ 💡 AUTO-FIX: How to Fix This Race Condition @@ -132,7 +136,9 @@ public void deposit(long amount) { // ============= Visibility Fixes ============= - /** {@return the visibility fix} */ + /** + * {@return the visibility fix} + */ public static String getVisibilityFix() { return """ 💡 AUTO-FIX: How to Fix This Visibility Issue @@ -169,7 +175,9 @@ public static String getVisibilityFix() { public synchronized void setReady() { ready = true; } - /** {@return the is ready} */ + /** + * {@return the is ready} + */ public synchronized boolean isReady() { return ready; @@ -179,7 +187,9 @@ public synchronized boolean isReady() { // ============= False Sharing Fixes ============= - /** {@return the false sharing fix} */ + /** + * {@return the false sharing fix} + */ public static String getFalseSharingFix() { return """ 💡 AUTO-FIX: How to Fix False Sharing @@ -227,7 +237,9 @@ private static class ColdFields { // ============= CompletableFuture Leak Fixes ============= - /** {@return the completable future leak fix} */ + /** + * {@return the completable future leak fix} + */ public static String getCompletableFutureLeakFix() { return """ 💡 AUTO-FIX: How to Fix CompletableFuture Completion Leak @@ -272,7 +284,9 @@ public static String getCompletableFutureLeakFix() { // ============= Virtual Thread Pinning Fixes ============= - /** {@return the virtual thread pinning fix} */ + /** + * {@return the virtual thread pinning fix} + */ public static String getVirtualThreadPinningFix() { return """ 💡 AUTO-FIX: How to Fix Virtual Thread Pinning @@ -317,7 +331,9 @@ public static String getVirtualThreadPinningFix() { // ============= Thread Pool Deadlock Fixes ============= - /** {@return the thread pool deadlock fix} */ + /** + * {@return the thread pool deadlock fix} + */ public static String getThreadPoolDeadlockFix() { return """ 💡 AUTO-FIX: How to Fix Thread Pool Deadlock @@ -365,7 +381,9 @@ public static String getThreadPoolDeadlockFix() { // ============= Busy Waiting Fixes ============= - /** {@return the busy waiting fix} */ + /** + * {@return the busy waiting fix} + */ public static String getBusyWaitingFix() { return """ 💡 AUTO-FIX: How to Fix Busy Waiting @@ -415,7 +433,9 @@ public static String getBusyWaitingFix() { // ============= Atomicity Violation Fixes ============= - /** {@return the atomicity violation fix} */ + /** + * {@return the atomicity violation fix} + */ public static String getAtomicityViolationFix() { return """ 💡 AUTO-FIX: How to Fix Atomicity Violation @@ -453,7 +473,9 @@ public synchronized void increment() { // ============= Lock Leak Fixes ============= - /** {@return the lock leak fix} */ + /** + * {@return the lock leak fix} + */ public static String getLockLeakFix() { return """ 💡 AUTO-FIX: How to Fix Lock Leak diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/BlockingQueueDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/BlockingQueueDetector.java index 281e5be9..a33f5c25 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/BlockingQueueDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/BlockingQueueDetector.java @@ -241,6 +241,8 @@ public static class BlockingQueueReport { /** * Check if any issues were detected. + * + * @return the has issues */ public boolean hasIssues() { return !silentFailures.isEmpty() || !emptyPolls.isEmpty() || diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/BoxedPrimitiveLockDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/BoxedPrimitiveLockDetector.java index 18ca1484..8de22195 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/BoxedPrimitiveLockDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/BoxedPrimitiveLockDetector.java @@ -171,7 +171,9 @@ public void recordLockAcquire(Object lockObject, Thread thread, String location) return null; } - /** {@return report of synchronizations on cached boxed primitives or value-based classes} */ + /** + * {@return report of synchronizations on cached boxed primitives or value-based classes} + */ public BoxedPrimitiveLockReport analyze() { BoxedPrimitiveLockReport r = new BoxedPrimitiveLockReport(); for (LockEvent e : events) { @@ -196,7 +198,9 @@ public static class BoxedPrimitiveLockReport { final List violations = new ArrayList<>(); private boolean hasValueBasedIssues; - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/BusyWaitDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/BusyWaitDetector.java index 6fe8fedc..5dff5013 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/BusyWaitDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/BusyWaitDetector.java @@ -173,6 +173,8 @@ private static void addToReport(BusyWaitReport report, long threadId, SpinEvent /** * Standardized alias for {@link #analyzeBusyWaiting()}. + * + * @return the analyze */ public BusyWaitReport analyze() { return analyzeBusyWaiting(); @@ -207,7 +209,9 @@ public static class BusyWaitReport { /** The cpu wasted. */ public long cpuWasted; - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !busyWaitLoops.isEmpty() || !tightLoops.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CacheConcurrencyDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CacheConcurrencyDetector.java index f4571cd1..be662f32 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CacheConcurrencyDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CacheConcurrencyDetector.java @@ -222,6 +222,8 @@ public static class CacheConcurrencyReport { /** * Check if any issues were detected. + * + * @return the has issues */ public boolean hasIssues() { return !concurrentReadWrite.isEmpty() || diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CalendarDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CalendarDetector.java index e5752ea5..419d7105 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CalendarDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CalendarDetector.java @@ -139,6 +139,8 @@ private void recordAccess(Calendar calendar, String name, String method) { /** * Analyse Calendar usage and return a report. + * + * @return the analyze */ public CalendarReport analyze() { CalendarReport report = new CalendarReport(); @@ -190,7 +192,11 @@ public static class CalendarReport { final java.util.List calendarErrors = new java.util.ArrayList<>(); final Map calendarActivity = new ConcurrentHashMap<>(); - /** Returns {@code true} when shared-access or errors were detected. */ + /** + * Returns {@code true} when shared-access or errors were detected. + * + * @return the has issues + */ public boolean hasIssues() { return !sharedCalendars.isEmpty() || !calendarErrors.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureBlockingCallbackDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureBlockingCallbackDetector.java index e1e7c82e..76b346f0 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureBlockingCallbackDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureBlockingCallbackDetector.java @@ -37,6 +37,9 @@ private static final class State { /** * Record entry into a CompletableFuture callback. + * + * @param callbackName the callback name + * @param thread the thread */ public void recordEnterCallback(String callbackName, Thread thread) { if (thread == null) return; @@ -45,6 +48,8 @@ public void recordEnterCallback(String callbackName, Thread thread) { /** * Record exit from a CompletableFuture callback. + * + * @param thread the thread */ public void recordExitCallback(Thread thread) { if (thread == null) return; @@ -53,6 +58,9 @@ public void recordExitCallback(Thread thread) { /** * Record a blocking call executed on a thread. + * + * @param thread the thread + * @param blockingApiName the blocking api name */ public void recordBlockingCall(Thread thread, String blockingApiName) { if (thread == null) return; @@ -97,7 +105,9 @@ public static final class Report { /** The structured violations. */ public final List structuredViolations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureChainDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureChainDetector.java index 8d35e29c..5aed16bc 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureChainDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureChainDetector.java @@ -243,6 +243,8 @@ public static class CompletableFutureChainReport { /** * Check if any issues were detected. + * + * @return the has issues */ public boolean hasIssues() { return !unjoinedFutures.isEmpty() || diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureCommonPoolBlockingDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureCommonPoolBlockingDetector.java index c1dc2385..865c9c89 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureCommonPoolBlockingDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureCommonPoolBlockingDetector.java @@ -70,7 +70,9 @@ public void recordBlockingCall(Object future, Thread thread, String callType) { thread.getName(), type, name)); } - /** {@return report of blocking calls in common-pool futures} */ + /** + * {@return report of blocking calls in common-pool futures} + */ public CompletableFutureCommonPoolBlockingReport analyze() { CompletableFutureCommonPoolBlockingReport r = new CompletableFutureCommonPoolBlockingReport(); r.violations.addAll(violations); @@ -81,7 +83,9 @@ public CompletableFutureCommonPoolBlockingReport analyze() { public static class CompletableFutureCommonPoolBlockingReport { final List violations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureExceptionDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureExceptionDetector.java index 4eac0fa1..30f58402 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureExceptionDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureExceptionDetector.java @@ -189,6 +189,8 @@ public static class CompletableFutureExceptionReport { /** * Check if any issues were detected. + * + * @return the has issues */ public boolean hasIssues() { return !unhandledExceptions.isEmpty() || !missingHandlers.isEmpty() || !swallowedExceptions.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureObtrudeDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureObtrudeDetector.java index 29e59bce..94479b47 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureObtrudeDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureObtrudeDetector.java @@ -39,6 +39,10 @@ private static final class State { /** * Record an obtrude action on a CompletableFuture. + * + * @param future the future + * @param label the label + * @param thread the thread */ public void recordObtrude(CompletableFuture future, String label, Thread thread) { if (future == null || thread == null) return; @@ -84,7 +88,9 @@ public static final class Report { /** The structured violations. */ public final List structuredViolations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConcurrentMapComputeRecursionDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConcurrentMapComputeRecursionDetector.java index 991fb36a..83cc6d59 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConcurrentMapComputeRecursionDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConcurrentMapComputeRecursionDetector.java @@ -61,13 +61,21 @@ public void recordComputeStart(Map map, Object key, Thread thread, String } } - /** Record exit from a {@code compute*} / {@code merge} mapping function. */ + /** + * Record exit from a {@code compute*} / {@code merge} mapping function. + * + * @param map the map + * @param key the key + * @param thread the thread + */ public void recordComputeEnd(Map map, Object key, Thread thread) { if (map == null || key == null || thread == null) return; activeComputes.remove(slot(map, key, thread)); } - /** {@return report of recursive compute calls} */ + /** + * {@return report of recursive compute calls} + */ public ConcurrentMapComputeRecursionReport analyze() { ConcurrentMapComputeRecursionReport r = new ConcurrentMapComputeRecursionReport(); r.recursions.addAll(recursions); @@ -78,7 +86,9 @@ public ConcurrentMapComputeRecursionReport analyze() { public static class ConcurrentMapComputeRecursionReport { final List recursions = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !recursions.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConcurrentModificationDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConcurrentModificationDetector.java index 4611b3e0..0fcc827f 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConcurrentModificationDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConcurrentModificationDetector.java @@ -206,6 +206,8 @@ public static class ConcurrentModificationReport { /** * Check if any issues were detected. + * + * @return the has issues */ public boolean hasIssues() { return !concurrentModifications.isEmpty() || !concurrentIterations.isEmpty() || !concurrentMutations.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConditionVariableDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConditionVariableDetector.java index 5392300f..f81b22b4 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConditionVariableDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConditionVariableDetector.java @@ -201,6 +201,8 @@ public static class ConditionVariableReport { /** * Check if any issues were detected. + * + * @return the has issues */ public boolean hasIssues() { return !lostSignals.isEmpty() || !stuckWaiters.isEmpty() || !missingSignals.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConstructorSafetyValidator.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConstructorSafetyValidator.java index 307a2034..68f088c2 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConstructorSafetyValidator.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConstructorSafetyValidator.java @@ -51,6 +51,8 @@ private static final class FieldAccessInfo { /** * Mark the start of object construction. + * + * @param object the object */ public void recordConstructionStart(Object object) { if (!enabled || object == null) return; @@ -62,6 +64,8 @@ public void recordConstructionStart(Object object) { /** * Mark the end of object construction. + * + * @param object the object */ public void recordConstructionEnd(Object object) { if (!enabled) return; @@ -76,6 +80,10 @@ public void recordConstructionEnd(Object object) { /** * Record a field access to a partially constructed object. + * + * @param object the object + * @param fieldName the field name + * @param timestamp the timestamp */ public void recordFieldAccess(Object object, String fieldName, long timestamp) { if (!enabled) return; @@ -107,6 +115,8 @@ public void recordFieldAccess(Object object, String fieldName, long timestamp) { /** * Validate constructor safety. + * + * @return the validate constructor safety */ public ConstructorSafetyReport validateConstructorSafety() { ConstructorSafetyReport report = new ConstructorSafetyReport(); @@ -184,7 +194,9 @@ public static class ConstructorSafetyReport { /** The fields accessed during construction. */ public final Set fieldsAccessedDuringConstruction = new HashSet<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !unsafeObjects.isEmpty() || !fieldsAccessedDuringConstruction.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CopyOnWriteCollectionDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CopyOnWriteCollectionDetector.java index 57788d8b..84f31bfd 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CopyOnWriteCollectionDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CopyOnWriteCollectionDetector.java @@ -117,6 +117,8 @@ private CoWState resolve(Object collection, String name) { /** * Analyse Copy-on-Write collection usage and return a report. + * + * @return the analyze */ public CopyOnWriteReport analyze() { CopyOnWriteReport report = new CopyOnWriteReport(); @@ -158,7 +160,11 @@ public static class CopyOnWriteReport { final java.util.List writeHeavyViolations = new java.util.ArrayList<>(); final Map collectionActivity = new ConcurrentHashMap<>(); - /** Returns {@code true} when any write-heavy violations were detected. */ + /** + * Returns {@code true} when any write-heavy violations were detected. + * + * @return the has issues + */ public boolean hasIssues() { return !writeHeavyViolations.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CountDownLatchDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CountDownLatchDetector.java index 99dc896d..da9eceba 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CountDownLatchDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CountDownLatchDetector.java @@ -23,6 +23,10 @@ public class CountDownLatchDetector { /** * Register a CountDownLatch for monitoring. + * + * @param latch the latch + * @param name the name + * @param initialCount the initial count */ public void registerLatch(CountDownLatch latch, String name, int initialCount) { latchRegistry.put(latch, new LatchInfo(name, initialCount)); @@ -30,6 +34,8 @@ public void registerLatch(CountDownLatch latch, String name, int initialCount) { /** * Record a countDown() call. + * + * @param latch the latch */ public void recordCountDown(CountDownLatch latch) { LatchInfo info = latchRegistry.get(latch); @@ -43,6 +49,8 @@ public void recordCountDown(CountDownLatch latch) { /** * Record an await() call that timed out. + * + * @param latch the latch */ public void recordTimeout(CountDownLatch latch) { timedOutLatches.add(latch); @@ -50,6 +58,8 @@ public void recordTimeout(CountDownLatch latch) { /** * Record a successful await() call. + * + * @param latch the latch */ public void recordAwaitSuccess(CountDownLatch latch) { LatchInfo info = latchRegistry.get(latch); @@ -60,6 +70,8 @@ public void recordAwaitSuccess(CountDownLatch latch) { /** * Analyze latch usage and return report. + * + * @return the analyze */ public CountDownLatchReport analyze() { return new CountDownLatchReport( @@ -87,7 +99,9 @@ public CountDownLatchReport( this.extraCountDownLatches = Collections.unmodifiableSet(new HashSet<>(extraCountDownLatches)); } - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !timedOutLatches.isEmpty() || !extraCountDownLatches.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CyclicBarrierDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CyclicBarrierDetector.java index c6f762c4..9a3f1233 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CyclicBarrierDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CyclicBarrierDetector.java @@ -24,6 +24,10 @@ public class CyclicBarrierDetector { /** * Register a CyclicBarrier for monitoring. + * + * @param barrier the barrier + * @param name the name + * @param parties the parties */ public void registerBarrier(CyclicBarrier barrier, String name, int parties) { barrierRegistry.put(barrier, new BarrierInfo(name, parties)); @@ -31,6 +35,8 @@ public void registerBarrier(CyclicBarrier barrier, String name, int parties) { /** * Record a thread arriving at the barrier. + * + * @param barrier the barrier */ public void recordArrival(CyclicBarrier barrier) { BarrierInfo info = barrierRegistry.get(barrier); @@ -41,6 +47,8 @@ public void recordArrival(CyclicBarrier barrier) { /** * Record a barrier await() that timed out. + * + * @param barrier the barrier */ public void recordTimeout(CyclicBarrier barrier) { timedOutBarriers.add(barrier); @@ -48,6 +56,8 @@ public void recordTimeout(CyclicBarrier barrier) { /** * Record a barrier that was broken. + * + * @param barrier the barrier */ public void recordBroken(CyclicBarrier barrier) { brokenBarriers.add(barrier); @@ -56,6 +66,8 @@ public void recordBroken(CyclicBarrier barrier) { /** * Record a barrier that was reset, repairing it after it broke. * Subsequent await() calls are no longer considered reuse-after-broken. + * + * @param barrier the barrier */ public void recordReset(CyclicBarrier barrier) { brokenBarriers.remove(barrier); @@ -65,6 +77,8 @@ public void recordReset(CyclicBarrier barrier) { * Record a thread calling await() on the barrier. If the barrier is * currently broken and has not been reset since, this is flagged as * reuse of a broken barrier without an intervening reset(). + * + * @param barrier the barrier */ public void recordAwait(CyclicBarrier barrier) { if (brokenBarriers.contains(barrier)) { @@ -74,6 +88,8 @@ public void recordAwait(CyclicBarrier barrier) { /** * Record successful barrier completion. + * + * @param barrier the barrier */ public void recordBarrierComplete(CyclicBarrier barrier) { BarrierInfo info = barrierRegistry.get(barrier); @@ -84,6 +100,8 @@ public void recordBarrierComplete(CyclicBarrier barrier) { /** * Analyze barrier usage and return report. + * + * @return the analyze */ public CyclicBarrierReport analyze() { return new CyclicBarrierReport( @@ -131,12 +149,16 @@ public CyclicBarrierReport( this(barrierRegistry, timedOutBarriers, brokenBarriers, Collections.emptySet()); } - /** {@return the reuse after broken barriers} */ + /** + * {@return the reuse after broken barriers} + */ public Set getReuseAfterBrokenBarriers() { return reuseAfterBrokenBarriers; } - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !timedOutBarriers.isEmpty() || !brokenBarriers.isEmpty() || !reuseAfterBrokenBarriers.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DaemonThreadHygieneDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DaemonThreadHygieneDetector.java index 0cddfbe0..5ca8b3ba 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DaemonThreadHygieneDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DaemonThreadHygieneDetector.java @@ -99,6 +99,8 @@ public void recordThread(Thread thread, String label) { * Analyze: a thread is flagged when it (1) was not marked daemon at * registration time, and (2) is still alive (or never started) at analysis * time — i.e. has not cleanly terminated. + * + * @return the analyze */ public Report analyze() { Report r = new Report(); @@ -173,7 +175,9 @@ public static final class Report { /** The structured violations. */ public final List structuredViolations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DeadlockDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DeadlockDetector.java index e376626d..fa5eebd3 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DeadlockDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DeadlockDetector.java @@ -197,6 +197,8 @@ private static void printLockChain(ThreadInfo thread, Map thre * *

This is a JVM-wide snapshot: unlike {@link #analyze()}, it also reports deadlocks * that predate any particular detector instance. + * + * @return the has deadlock */ public static boolean hasDeadlock() { ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); @@ -206,6 +208,8 @@ public static boolean hasDeadlock() { /** * Get a summary of current lock contention. + * + * @return the get lock contention summary */ @SuppressWarnings("PMD.AssignmentInOperand") // counter++ in switch arrow case is idiomatic public static String getLockContentionSummary() { @@ -243,6 +247,8 @@ public static void printLearningAndFix() { * *

Deadlocks that already existed when this detector was constructed are excluded, * so a report with issues always means the monitored test introduced a new deadlock. + * + * @return the analyze */ public DeadlockReport analyze() { long[] current = ManagementFactory.getThreadMXBean().findDeadlockedThreads(); @@ -264,7 +270,9 @@ public DeadlockReport(boolean deadlocked) { this.deadlocked = deadlocked; } - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return deadlocked; } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DeprecatedThreadApiDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DeprecatedThreadApiDetector.java index 43ec47f9..b007c766 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DeprecatedThreadApiDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DeprecatedThreadApiDetector.java @@ -64,7 +64,9 @@ public void recordApiUse(String apiName, Thread thread) { events.add(new ApiUseEvent(apiName, thread.getName())); } - /** {@return report of deprecated Thread API usages} */ + /** + * {@return report of deprecated Thread API usages} + */ public DeprecatedThreadApiReport analyze() { DeprecatedThreadApiReport r = new DeprecatedThreadApiReport(); for (ApiUseEvent e : events) { @@ -80,7 +82,9 @@ public DeprecatedThreadApiReport analyze() { public static class DeprecatedThreadApiReport { final List violations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DoubleCheckedLockingDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DoubleCheckedLockingDetector.java index a07ae867..36d593c6 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DoubleCheckedLockingDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DoubleCheckedLockingDetector.java @@ -31,6 +31,12 @@ public class DoubleCheckedLockingDetector { /** * Register a double-checked locking pattern for monitoring. + * + * @param fieldName the field name + * @param isVolatile the {@code isVolatile} flag + * @param hasFirstCheck the {@code hasFirstCheck} flag + * @param hasSecondCheck the {@code hasSecondCheck} flag + * @param insideSynchronized the {@code insideSynchronized} flag */ public void registerDCL(String fieldName, boolean isVolatile, boolean hasFirstCheck, boolean hasSecondCheck, boolean insideSynchronized) { @@ -46,6 +52,10 @@ public void registerDCL(String fieldName, boolean isVolatile, boolean hasFirstCh /** * Record access to a field that might use DCL. + * + * @param fieldName the field name + * @param isRead the {@code isRead} flag + * @param isWrite the {@code isWrite} flag */ public void recordAccess(String fieldName, boolean isRead, boolean isWrite) { DCLInfo info = dclRegistry.get(fieldName); @@ -57,6 +67,8 @@ public void recordAccess(String fieldName, boolean isRead, boolean isWrite) { /** * Analyze DCL patterns and return report. + * + * @return the analyze */ public DoubleCheckedLockingReport analyze() { return new DoubleCheckedLockingReport(dclRegistry, brokenDCLs); @@ -77,7 +89,9 @@ public DoubleCheckedLockingReport( this.brokenDCLs = Collections.unmodifiableSet(new HashSet<>(brokenDCLs)); } - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !brokenDCLs.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ExchangerDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ExchangerDetector.java index 6fcb8bbf..fd989972 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ExchangerDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ExchangerDetector.java @@ -24,6 +24,9 @@ public class ExchangerDetector { /** * Register an Exchanger for monitoring. + * + * @param exchanger the exchanger + * @param name the name */ public void registerExchanger(Exchanger exchanger, String name) { exchangerRegistry.put(exchanger, new ExchangerInfo(name)); @@ -31,6 +34,9 @@ public void registerExchanger(Exchanger exchanger, String name) { /** * Record a thread starting an exchange. + * + * @param exchanger the exchanger + * @param exchangerName the exchanger name */ public void recordExchangeStart(Exchanger exchanger, String exchangerName) { ExchangerInfo info = exchangerRegistry.get(exchanger); @@ -41,6 +47,10 @@ public void recordExchangeStart(Exchanger exchanger, String exchangerName) { /** * Record a successful exchange completion. + * + * @param exchanger the exchanger + * @param exchangerName the exchanger name + * @param value the value */ public void recordExchangeComplete(Exchanger exchanger, String exchangerName, Object value) { ExchangerInfo info = exchangerRegistry.get(exchanger); @@ -54,6 +64,8 @@ public void recordExchangeComplete(Exchanger exchanger, String exchangerName, /** * Record an exchange that timed out. + * + * @param exchanger the exchanger */ public void recordTimeout(Exchanger exchanger) { timedOutExchangers.add(exchanger); @@ -61,6 +73,8 @@ public void recordTimeout(Exchanger exchanger) { /** * Record an exchange that was interrupted. + * + * @param exchanger the exchanger */ public void recordInterrupted(Exchanger exchanger) { interruptedExchangers.add(exchanger); @@ -68,6 +82,8 @@ public void recordInterrupted(Exchanger exchanger) { /** * Analyze Exchanger usage and return report. + * + * @return the analyze */ public ExchangerReport analyze() { return new ExchangerReport( @@ -99,7 +115,9 @@ public ExchangerReport( this.nullValueExchanges = nullValueExchanges; } - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !timedOutExchangers.isEmpty() || !interruptedExchangers.isEmpty() diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ExecutorDeadlockDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ExecutorDeadlockDetector.java index 4e2dc2a1..0775a475 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ExecutorDeadlockDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ExecutorDeadlockDetector.java @@ -125,7 +125,9 @@ public static class ExecutorDeadlockReport { /** The self deadlocks. */ public final Set selfDeadlocks = new HashSet<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !selfDeadlocks.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ExecutorShutdownDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ExecutorShutdownDetector.java index 92f91242..a3e463b0 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ExecutorShutdownDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ExecutorShutdownDetector.java @@ -126,7 +126,9 @@ public static class ExecutorShutdownReport { final List notShutDown = new ArrayList<>(); final List noAwaitTermination = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !notShutDown.isEmpty() || !noAwaitTermination.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ExplicitGcDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ExplicitGcDetector.java index 6752f8ee..c8d88bea 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ExplicitGcDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ExplicitGcDetector.java @@ -50,7 +50,9 @@ public void recordGcInvocation(Thread thread, String location) { location != null ? location : "unknown")); } - /** {@return report of explicit GC invocations} */ + /** + * {@return report of explicit GC invocations} + */ public ExplicitGcReport analyze() { ExplicitGcReport r = new ExplicitGcReport(); for (GcEvent e : events) { @@ -67,7 +69,9 @@ public ExplicitGcReport analyze() { public static class ExplicitGcReport { final List violations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FalseSharingDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FalseSharingDetector.java index 2520372c..387a5232 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FalseSharingDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FalseSharingDetector.java @@ -49,6 +49,10 @@ private static class AccessEvent { /** * Record a field access. Call this when a field is accessed in your test. + * + * @param object the object + * @param fieldName the field name + * @param fieldType the field type */ public void recordFieldAccess(Object object, String fieldName, Class fieldType) { if (!enabled || object == null) return; @@ -72,6 +76,8 @@ public void recordFieldAccess(Object object, String fieldName, Class fieldTyp /** * Analyze for false sharing patterns. + * + * @return the analyze false sharing */ public FalseSharingReport analyzeFalseSharing() { FalseSharingReport report = new FalseSharingReport(); @@ -115,6 +121,8 @@ public FalseSharingReport analyzeFalseSharing() { /** * Standardized alias for {@link #analyzeFalseSharing()}. + * + * @return the analyze */ public FalseSharingReport analyze() { return analyzeFalseSharing(); @@ -215,7 +223,9 @@ public ContentionPair(String f1, String f2, long dist, long acc1, long acc2) { /** The high contention fields. */ public final Set highContentionFields = new HashSet<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !falseSharedPairs.isEmpty() || !highContentionFields.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FileChannelPositionRaceDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FileChannelPositionRaceDetector.java index 60804088..36f57ee8 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FileChannelPositionRaceDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FileChannelPositionRaceDetector.java @@ -156,7 +156,9 @@ public static final class Report { /** The structured violations. */ public final List structuredViolations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FinalFieldMutationDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FinalFieldMutationDetector.java index 4caabc85..bec5ed18 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FinalFieldMutationDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FinalFieldMutationDetector.java @@ -188,16 +188,24 @@ public static class FinalFieldMutationReport { this.concurrentWriteIssues = concurrentWriteIssues; } - /** {@return true if any reflective final-field mutation was detected} */ + /** + * {@return true if any reflective final-field mutation was detected} + */ public boolean hasIssues() { return !mutationIssues.isEmpty(); } - /** {@return the mutation issues} */ + /** + * {@return the mutation issues} + */ public List getMutationIssues() { return Collections.unmodifiableList(mutationIssues); } - /** {@return the racing reader issues} */ + /** + * {@return the racing reader issues} + */ public List getRacingReaderIssues() { return Collections.unmodifiableList(racingReaderIssues); } - /** {@return the concurrent write issues} */ + /** + * {@return the concurrent write issues} + */ public List getConcurrentWriteIssues() { return Collections.unmodifiableList(concurrentWriteIssues); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ForkJoinPoolDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ForkJoinPoolDetector.java index 7d204ff9..6567385f 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ForkJoinPoolDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ForkJoinPoolDetector.java @@ -23,6 +23,10 @@ public class ForkJoinPoolDetector { /** * Register a ForkJoinPool for monitoring. + * + * @param pool the pool + * @param name the name + * @param parallelism the parallelism */ public void registerPool(ForkJoinPool pool, String name, int parallelism) { poolRegistry.put(pool, new PoolInfo(name, parallelism)); @@ -30,6 +34,10 @@ public void registerPool(ForkJoinPool pool, String name, int parallelism) { /** * Record a task being forked. + * + * @param pool the pool + * @param poolName the pool name + * @param taskName the task name */ public void recordFork(ForkJoinPool pool, String poolName, String taskName) { PoolInfo info = poolRegistry.get(pool); @@ -40,6 +48,10 @@ public void recordFork(ForkJoinPool pool, String poolName, String taskName) { /** * Record a task being joined. + * + * @param pool the pool + * @param poolName the pool name + * @param taskName the task name */ public void recordJoin(ForkJoinPool pool, String poolName, String taskName) { PoolInfo info = poolRegistry.get(pool); @@ -50,6 +62,9 @@ public void recordJoin(ForkJoinPool pool, String poolName, String taskName) { /** * Record a task that was forked but never joined. + * + * @param poolName the pool name + * @param taskName the task name */ public void recordForkWithoutJoin(String poolName, String taskName) { forkedWithoutJoin.add(poolName + ":" + taskName); @@ -57,6 +72,10 @@ public void recordForkWithoutJoin(String poolName, String taskName) { /** * Record an exception in a forked task. + * + * @param poolName the pool name + * @param taskName the task name + * @param t the t */ public void recordException(String poolName, String taskName, Throwable t) { exceptionsInTasks.add(poolName + ":" + taskName + " (" + t.getClass().getSimpleName() + ")"); @@ -64,6 +83,8 @@ public void recordException(String poolName, String taskName, Throwable t) { /** * Record work stealing event. + * + * @param pool the pool */ public void recordWorkSteal(ForkJoinPool pool) { taskStealCount++; @@ -71,6 +92,10 @@ public void recordWorkSteal(ForkJoinPool pool) { /** * Record task execution time. + * + * @param pool the pool + * @param poolName the pool name + * @param timeMs the time in milliseconds */ public void recordTaskTime(ForkJoinPool pool, String poolName, long timeMs) { PoolInfo info = poolRegistry.get(pool); @@ -81,6 +106,8 @@ public void recordTaskTime(ForkJoinPool pool, String poolName, long timeMs) { /** * Analyze ForkJoinPool usage and return report. + * + * @return the analyze */ public ForkJoinPoolReport analyze() { return new ForkJoinPoolReport( @@ -108,7 +135,9 @@ public ForkJoinPoolReport( this.taskStealCount = taskStealCount; } - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !forkedWithoutJoin.isEmpty() || !exceptionsInTasks.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ForkJoinTaskBlockingDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ForkJoinTaskBlockingDetector.java index e67d474e..a568b59d 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ForkJoinTaskBlockingDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ForkJoinTaskBlockingDetector.java @@ -32,13 +32,21 @@ public class ForkJoinTaskBlockingDetector { private final Set activeForkJoinThreads = ConcurrentHashMap.newKeySet(); private final List blockingCalls = new CopyOnWriteArrayList<>(); - /** Call at the start of a {@code ForkJoinTask.compute()} or {@code exec()} body. */ + /** + * Call at the start of a {@code ForkJoinTask.compute()} or {@code exec()} body. + * + * @param thread the thread + */ public void recordForkJoinTaskEntered(Thread thread) { if (thread == null) return; activeForkJoinThreads.add(thread.threadId()); } - /** Call at the end of a {@code ForkJoinTask.compute()} or {@code exec()} body. */ + /** + * Call at the end of a {@code ForkJoinTask.compute()} or {@code exec()} body. + * + * @param thread the thread + */ public void recordForkJoinTaskExited(Thread thread) { if (thread == null) return; activeForkJoinThreads.remove(thread.threadId()); @@ -61,7 +69,9 @@ public void recordBlockingCallAttempted(Thread thread, String callType) { thread.getName(), type)); } - /** {@return report of blocking calls inside ForkJoin tasks} */ + /** + * {@return report of blocking calls inside ForkJoin tasks} + */ public ForkJoinTaskBlockingReport analyze() { ForkJoinTaskBlockingReport r = new ForkJoinTaskBlockingReport(); r.blockingCalls.addAll(blockingCalls); @@ -72,7 +82,9 @@ public ForkJoinTaskBlockingReport analyze() { public static class ForkJoinTaskBlockingReport { final List blockingCalls = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !blockingCalls.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FutureBlockingDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FutureBlockingDetector.java index 6b042d7c..8e68227f 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FutureBlockingDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FutureBlockingDetector.java @@ -139,7 +139,9 @@ public static class FutureBlockingReport { /** The starvation risks. */ public final Set starvationRisks = new HashSet<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !starvationRisks.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FutureIgnoredDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FutureIgnoredDetector.java index 69f23c93..653e4987 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FutureIgnoredDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FutureIgnoredDetector.java @@ -69,7 +69,9 @@ public void recordInspect(Object future, Thread thread) { if (rec != null) rec.inspected = true; } - /** {@return report of Futures that were submitted but never inspected} */ + /** + * {@return report of Futures that were submitted but never inspected} + */ public FutureIgnoredReport analyze() { FutureIgnoredReport r = new FutureIgnoredReport(); for (SubmitRecord rec : submits.values()) { @@ -87,7 +89,9 @@ public FutureIgnoredReport analyze() { public static class FutureIgnoredReport { final List violations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/GathererConcurrencyMisuseDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/GathererConcurrencyMisuseDetector.java index 6c91452d..4a9371a1 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/GathererConcurrencyMisuseDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/GathererConcurrencyMisuseDetector.java @@ -165,18 +165,28 @@ public static class GathererConcurrencyMisuseReport { this.totalIntegrations = totalIntegrations; } - /** {@return true if any unsafe parallel-gatherer usage was detected} */ + /** + * {@return true if any unsafe parallel-gatherer usage was detected} + */ public boolean hasIssues() { return !missingCombinerIssues.isEmpty() || !sharedStateIssues.isEmpty(); } - /** {@return the missing combiner issues} */ + /** + * {@return the missing combiner issues} + */ public List getMissingCombinerIssues() { return Collections.unmodifiableList(missingCombinerIssues); } - /** {@return the shared state issues} */ + /** + * {@return the shared state issues} + */ public List getSharedStateIssues() { return Collections.unmodifiableList(sharedStateIssues); } - /** {@return the total gatherers} */ + /** + * {@return the total gatherers} + */ public int getTotalGatherers() { return totalGatherers; } - /** {@return the total integrations} */ + /** + * {@return the total integrations} + */ public int getTotalIntegrations() { return totalIntegrations; } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/HighContentionAtomicDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/HighContentionAtomicDetector.java index e2fa3c98..9ad898cc 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/HighContentionAtomicDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/HighContentionAtomicDetector.java @@ -196,7 +196,9 @@ public static final class Report { /** The structured violations. */ public final List structuredViolations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/HttpClientConcurrencyDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/HttpClientConcurrencyDetector.java index 37f4f7ef..0bf0b22a 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/HttpClientConcurrencyDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/HttpClientConcurrencyDetector.java @@ -211,6 +211,8 @@ public static class HttpClientConcurrencyReport { /** * Check if any issues were detected. + * + * @return the has issues */ public boolean hasIssues() { return !pendingRequests.isEmpty() || diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/InheritableThreadLocalMisuseDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/InheritableThreadLocalMisuseDetector.java index 68b2a4ad..284d704e 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/InheritableThreadLocalMisuseDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/InheritableThreadLocalMisuseDetector.java @@ -143,7 +143,9 @@ public static class InheritableThreadLocalReport { final List pooledSetIssues = new ArrayList<>(); final List multiThreadAccess = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !pooledGetIssues.isEmpty() || !pooledSetIssues.isEmpty() diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/InterruptMonitor.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/InterruptMonitor.java index 667e333a..0ea96ac0 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/InterruptMonitor.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/InterruptMonitor.java @@ -165,6 +165,8 @@ public InterruptReport analyzeInterruptHandling() { /** * Standardized alias for {@link #analyzeInterruptHandling()}. + * + * @return the analyze */ public InterruptReport analyze() { return analyzeInterruptHandling(); @@ -203,7 +205,9 @@ public static class InterruptReport { /** The blocking without handling. */ public final Set blockingWithoutHandling = new HashSet<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !ignoredInterrupts.isEmpty() || !repeatedIgnoredInterrupts.isEmpty() diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/InterruptSwallowingDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/InterruptSwallowingDetector.java index 29e03527..0cb95e28 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/InterruptSwallowingDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/InterruptSwallowingDetector.java @@ -59,7 +59,9 @@ public void recordCatch(Thread thread, String location, boolean restored) { location != null ? location : "unknown", restored)); } - /** {@return report of threads that swallowed an InterruptedException} */ + /** + * {@return report of threads that swallowed an InterruptedException} + */ public InterruptSwallowingReport analyze() { InterruptSwallowingReport r = new InterruptSwallowingReport(); for (CatchEvent e : events) { @@ -78,7 +80,9 @@ public InterruptSwallowingReport analyze() { public static class InterruptSwallowingReport { final List violations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/IssueDeduplicator.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/IssueDeduplicator.java index 3d4edcb3..3bece552 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/IssueDeduplicator.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/IssueDeduplicator.java @@ -158,32 +158,44 @@ void addEvent(T event) { threadIds.add(event.getThreadId()); } - /** {@return the fingerprint identifying this group} */ + /** + * {@return the fingerprint identifying this group} + */ public String getFingerprint() { return fingerprint; } - /** {@return number of occurrences in this group} */ + /** + * {@return number of occurrences in this group} + */ public int getCount() { return events.size(); } - /** {@return number of unique threads affected} */ + /** + * {@return number of unique threads affected} + */ public int getAffectedThreadCount() { return threadIds.size(); } - /** {@return set of affected thread IDs} */ + /** + * {@return set of affected thread IDs} + */ public Set getAffectedThreadIds() { return Collections.unmodifiableSet(threadIds); } - /** {@return the first event in this group (representative)} */ + /** + * {@return the first event in this group (representative)} + */ public @Nullable T getFirstEvent() { return events.isEmpty() ? null : events.get(0); } - /** {@return all events in this group} */ + /** + * {@return all events in this group} + */ public List getEvents() { return Collections.unmodifiableList(events); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/IssueSeverity.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/IssueSeverity.java index d47a2329..9a74dd2d 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/IssueSeverity.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/IssueSeverity.java @@ -53,17 +53,23 @@ public enum IssueSeverity { this.description = description; } - /** {@return the display label with emoji indicator} */ + /** + * {@return the display label with emoji indicator} + */ public String getLabel() { return label; } - /** {@return a brief description of what this severity means} */ + /** + * {@return a brief description of what this severity means} + */ public String getDescription() { return description; } - /** {@return ANSI color code for terminal output} */ + /** + * {@return ANSI color code for terminal output} + */ public String getAnsiColor() { switch (this) { case CRITICAL: return "\u001B[31m"; // Red @@ -74,7 +80,9 @@ public String getAnsiColor() { } } - /** {@return ANSI reset code} */ + /** + * {@return ANSI reset code} + */ public String getAnsiReset() { return "\u001B[0m"; } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/JdbcConnectionSharedDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/JdbcConnectionSharedDetector.java index 81ada98f..d92167ed 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/JdbcConnectionSharedDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/JdbcConnectionSharedDetector.java @@ -154,7 +154,9 @@ public static final class Report { /** The structured violations. */ public final List structuredViolations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LatchMisuseDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LatchMisuseDetector.java index 2cec6412..0d94fe3e 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LatchMisuseDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LatchMisuseDetector.java @@ -110,7 +110,9 @@ public static class LatchMisuseReport { /** The extra count downs. */ public final Set extraCountDowns = new HashSet<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !missingCountDowns.isEmpty() || !extraCountDowns.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LazyConstantMisuseDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LazyConstantMisuseDetector.java index 277429fc..bfadfd2f 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LazyConstantMisuseDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LazyConstantMisuseDetector.java @@ -283,7 +283,9 @@ public static class LazyConstantMisuseReport { this.totalComputes = totalComputes; } - /** {@return true if any correctness-affecting LazyConstant misuse was detected} */ + /** + * {@return true if any correctness-affecting LazyConstant misuse was detected} + */ public boolean hasIssues() { return !reentrantIssues.isEmpty() || !nullValueIssues.isEmpty() @@ -291,19 +293,33 @@ public boolean hasIssues() { || !nonDeterministicIssues.isEmpty(); } - /** {@return the reentrant issues} */ + /** + * {@return the reentrant issues} + */ public List getReentrantIssues() { return Collections.unmodifiableList(reentrantIssues); } - /** {@return the null value issues} */ + /** + * {@return the null value issues} + */ public List getNullValueIssues() { return Collections.unmodifiableList(nullValueIssues); } - /** {@return the multiple compute issues} */ + /** + * {@return the multiple compute issues} + */ public List getMultipleComputeIssues() { return Collections.unmodifiableList(multipleComputeIssues); } - /** {@return the non deterministic issues} */ + /** + * {@return the non deterministic issues} + */ public List getNonDeterministicIssues() { return Collections.unmodifiableList(nonDeterministicIssues); } - /** {@return the convoy warnings} */ + /** + * {@return the convoy warnings} + */ public List getConvoyWarnings() { return Collections.unmodifiableList(convoyWarnings); } - /** {@return the total gets} */ + /** + * {@return the total gets} + */ public int getTotalGets() { return totalGets; } - /** {@return the total computes} */ + /** + * {@return the total computes} + */ public int getTotalComputes() { return totalComputes; } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LazyInitRaceDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LazyInitRaceDetector.java index 5152fcad..d453653e 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LazyInitRaceDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LazyInitRaceDetector.java @@ -108,6 +108,8 @@ public void recordInitialization(String fieldId) { /** * Analyses recorded initialization data and returns a report of fields * where duplicate initialization was detected. + * + * @return the analyze */ public LazyInitRaceReport analyze() { LazyInitRaceReport report = new LazyInitRaceReport(); @@ -153,7 +155,11 @@ public static class LazyInitRaceReport { final List races = new ArrayList<>(); final List visibilityRisks = new ArrayList<>(); - /** Returns {@code true} when any lazy-init race or visibility risk was detected. */ + /** + * Returns {@code true} when any lazy-init race or visibility risk was detected. + * + * @return the has issues + */ public boolean hasIssues() { return !races.isEmpty() || !visibilityRisks.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LazyInitValidator.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LazyInitValidator.java index 12e45653..e83fedb6 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LazyInitValidator.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LazyInitValidator.java @@ -110,7 +110,9 @@ public static class LazyInitReport { /** The unsafe publication. */ public final Set unsafePublication = new HashSet<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !multipleInitializations.isEmpty() || !unsafePublication.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LearningContent.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LearningContent.java index 90f58d78..18a7cad2 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LearningContent.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LearningContent.java @@ -9,7 +9,9 @@ public final class LearningContent { private LearningContent() { } - /** {@return the deadlock explanation} */ + /** + * {@return the deadlock explanation} + */ public static String getDeadlockExplanation() { return """ 📚 LEARNING: What is a Deadlock? @@ -31,7 +33,9 @@ The four conditions for deadlock (all must be present): """; } - /** {@return the race condition explanation} */ + /** + * {@return the race condition explanation} + */ public static String getRaceConditionExplanation() { return """ 📚 LEARNING: What is a Race Condition? @@ -49,7 +53,9 @@ Visual example (lost update): """; } - /** {@return the visibility explanation} */ + /** + * {@return the visibility explanation} + */ public static String getVisibilityExplanation() { return """ 📚 LEARNING: What is a Memory Visibility Issue? @@ -65,7 +71,9 @@ Thread B (Core 2): while (!sharedFlag) { } ← never sees the change! """; } - /** {@return the false sharing explanation} */ + /** + * {@return the false sharing explanation} + */ public static String getFalseSharingExplanation() { return """ 📚 LEARNING: What is False Sharing? @@ -83,7 +91,9 @@ writes writes (invalidates entire line!) """; } - /** {@return the completable future leak explanation} */ + /** + * {@return the completable future leak explanation} + */ public static String getCompletableFutureLeakExplanation() { return """ 📚 LEARNING: What is a CompletableFuture Completion Leak? @@ -100,7 +110,9 @@ but never completed (neither successfully nor exceptionally). """; } - /** {@return the virtual thread pinning explanation} */ + /** + * {@return the virtual thread pinning explanation} + */ public static String getVirtualThreadPinningExplanation() { return """ 📚 LEARNING: What is Virtual Thread Pinning? @@ -117,7 +129,9 @@ public static String getVirtualThreadPinningExplanation() { """; } - /** {@return the thread pool deadlock explanation} */ + /** + * {@return the thread pool deadlock explanation} + */ public static String getThreadPoolDeadlockExplanation() { return """ 📚 LEARNING: What is a Thread Pool Deadlock? @@ -135,7 +149,9 @@ public static String getThreadPoolDeadlockExplanation() { """; } - /** {@return the busy waiting explanation} */ + /** + * {@return the busy waiting explanation} + */ public static String getBusyWaitingExplanation() { return """ 📚 LEARNING: What is Busy Waiting? @@ -156,7 +172,9 @@ public static String getBusyWaitingExplanation() { """; } - /** {@return the atomicity violation explanation} */ + /** + * {@return the atomicity violation explanation} + */ public static String getAtomicityViolationExplanation() { return """ 📚 LEARNING: What is an Atomicity Violation? @@ -178,7 +196,9 @@ An atomicity violation occurs when a compound operation (read-modify-write) """; } - /** {@return the lock leak explanation} */ + /** + * {@return the lock leak explanation} + */ public static String getLockLeakExplanation() { return """ 📚 LEARNING: What is a Lock Leak? diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LivelockDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LivelockDetector.java index 4cfcfd24..89e19c5a 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LivelockDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LivelockDetector.java @@ -85,6 +85,8 @@ public void captureSnapshot() { /** * Analyze captured snapshots for livelock and starvation patterns. + * + * @return the analyze livelocks */ public LivelockReport analyzeLivelocks() { LivelockReport report = new LivelockReport(); @@ -116,6 +118,8 @@ public LivelockReport analyzeLivelocks() { /** * Standardized alias for {@link #analyzeLivelocks()}. + * + * @return the analyze */ public LivelockReport analyze() { return analyzeLivelocks(); @@ -202,7 +206,9 @@ public static class LivelockReport { /** The no progress threads. */ public final Set noProgressThreads = new HashSet<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !starvedThreads.isEmpty() || !livelockCandidates.isEmpty() || !noProgressThreads.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockContentionDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockContentionDetector.java index de39efd8..addcdd19 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockContentionDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockContentionDetector.java @@ -119,6 +119,8 @@ public void recordReleased(Object monitor, String name) { /** * Analyses recorded data and returns a contention report. + * + * @return the analyze */ public LockContentionReport analyze() { LockContentionReport report = new LockContentionReport(); @@ -158,7 +160,11 @@ public static class LockContentionReport { final List hotLocks = new ArrayList<>(); - /** Returns {@code true} when any monitor exceeds the contention threshold. */ + /** + * Returns {@code true} when any monitor exceeds the contention threshold. + * + * @return the has issues + */ public boolean hasIssues() { return !hotLocks.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockDowngradeDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockDowngradeDetector.java index 0a55d455..fb80996c 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockDowngradeDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockDowngradeDetector.java @@ -172,7 +172,9 @@ public LockDowngradeReport analyze() { public static class LockDowngradeReport { final List upgradeAttempts = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !upgradeAttempts.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockLeakDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockLeakDetector.java index e8ea2af4..3f5f7c5a 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockLeakDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockLeakDetector.java @@ -189,6 +189,8 @@ public static class LockLeakReport { /** * Check if any issues were detected. + * + * @return the has issues */ public boolean hasIssues() { return !lockLeaks.isEmpty() || !heldLocks.isEmpty() || !excessiveHoldTimes.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockOrderValidator.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockOrderValidator.java index decf2813..9de60d11 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockOrderValidator.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockOrderValidator.java @@ -42,6 +42,8 @@ private static class LockSequence { /** * Record a lock acquisition. + * + * @param lock the lock */ public void recordLockAcquisition(Object lock) { if (!enabled || lock == null) return; @@ -67,6 +69,8 @@ public void recordLockAcquisition(Object lock) { /** * Record lock release. + * + * @param lock the lock */ public void recordLockRelease(Object lock) { if (!enabled || lock == null) return; @@ -85,6 +89,8 @@ public void recordLockRelease(Object lock) { /** * Validate lock ordering consistency. + * + * @return the validate lock order */ public LockOrderReport validateLockOrder() { LockOrderReport report = new LockOrderReport(); @@ -190,7 +196,9 @@ public static class LockOrderReport { /** The potential deadlock cycles. */ public final Set potentialDeadlockCycles = new HashSet<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !inconsistentOrderings.isEmpty() || !potentialDeadlockCycles.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockUpgradeDeadlockDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockUpgradeDeadlockDetector.java index c95444b1..6f936a3e 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockUpgradeDeadlockDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockUpgradeDeadlockDetector.java @@ -38,6 +38,10 @@ private static final class State { /** * Record acquisition of a read lock. + * + * @param lock the lock + * @param lockName the lock name + * @param thread the thread */ public void recordReadLockAcquired(ReentrantReadWriteLock lock, String lockName, Thread thread) { if (lock == null || thread == null) return; @@ -47,6 +51,9 @@ public void recordReadLockAcquired(ReentrantReadWriteLock lock, String lockName, /** * Record release of a read lock. + * + * @param lock the lock + * @param thread the thread */ public void recordReadLockReleased(ReentrantReadWriteLock lock, Thread thread) { if (lock == null || thread == null) return; @@ -59,6 +66,10 @@ public void recordReadLockReleased(ReentrantReadWriteLock lock, Thread thread) { /** * Record attempt to acquire a write lock. + * + * @param lock the lock + * @param lockName the lock name + * @param thread the thread */ public void recordWriteLockAcquisitionAttempt(ReentrantReadWriteLock lock, String lockName, Thread thread) { if (lock == null || thread == null) return; @@ -106,7 +117,9 @@ public static final class Report { /** The structured violations. */ public final List structuredViolations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MdcContextLeakDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MdcContextLeakDetector.java index 61b014be..62d38cc1 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MdcContextLeakDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MdcContextLeakDetector.java @@ -79,7 +79,9 @@ public void recordTaskEnd(Thread thread, Map mdcSnapshot) { snap.endMdc = mdcSnapshot != null ? new LinkedHashMap<>(mdcSnapshot) : Collections.emptyMap(); } - /** {@return report of threads that left MDC entries behind after task completion} */ + /** + * {@return report of threads that left MDC entries behind after task completion} + */ public MdcContextLeakReport analyze() { MdcContextLeakReport r = new MdcContextLeakReport(); for (TaskSnapshot snap : snapshots.values()) { @@ -100,7 +102,9 @@ public MdcContextLeakReport analyze() { public static class MdcContextLeakReport { final List violations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MemoryModelValidator.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MemoryModelValidator.java index f377c4a2..804f4a97 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MemoryModelValidator.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MemoryModelValidator.java @@ -25,6 +25,8 @@ public class MemoryModelValidator { /** * Run comprehensive JMM validation on the test framework. * This checks that all internal state transitions are properly synchronized. + * + * @return the validate */ public ValidationResult validate() { ValidationResult result = new ValidationResult(); @@ -174,12 +176,16 @@ public static class ValidationResult { /** The observations. */ public final List observations = Collections.synchronizedList(new ArrayList<>()); - /** {@return whether valid} */ + /** + * {@return whether valid} + */ public boolean isValid() { return testsRun > 0 && testsPassed == testsRun; } - /** {@return the pass rate} */ + /** + * {@return the pass rate} + */ public double getPassRate() { return testsRun == 0 ? 0 : 100.0 * testsPassed / testsRun; } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MemoryOrderingMonitor.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MemoryOrderingMonitor.java index 20f9848d..0e2e2bdd 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MemoryOrderingMonitor.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MemoryOrderingMonitor.java @@ -38,6 +38,9 @@ private static class MemoryAccess { /** * Record a memory read. + * + * @param location the location + * @param value the value */ public void recordRead(String location, Object value) { if (!enabled) return; @@ -46,6 +49,9 @@ public void recordRead(String location, Object value) { /** * Record a memory write. + * + * @param location the location + * @param value the value */ public void recordWrite(String location, Object value) { if (!enabled) return; @@ -54,6 +60,8 @@ public void recordWrite(String location, Object value) { /** * Analyze for memory ordering violations. + * + * @return the analyze ordering */ public MemoryOrderingReport analyzeOrdering() { MemoryOrderingReport report = new MemoryOrderingReport(); @@ -101,6 +109,8 @@ public MemoryOrderingReport analyzeOrdering() { /** * Standardized alias for {@link #analyzeOrdering()}. + * + * @return the analyze */ public MemoryOrderingReport analyze() { return analyzeOrdering(); @@ -142,7 +152,9 @@ public static class MemoryOrderingReport { */ public final Set suspiciousReorderings = new HashSet<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !staleCoreads.isEmpty() || !suspiciousReorderings.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MissedSignalDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MissedSignalDetector.java index 47a8f0a9..5a2844a2 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MissedSignalDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MissedSignalDetector.java @@ -118,6 +118,8 @@ public void recordNotifyAll(String conditionName) { /** * Analyses recorded signal/wait data and returns a report of conditions * that suffered missed signals. + * + * @return the analyze */ public MissedSignalReport analyze() { MissedSignalReport report = new MissedSignalReport(); @@ -150,7 +152,11 @@ public static class MissedSignalReport { final List missedConditions = new ArrayList<>(); - /** Returns {@code true} when at least one condition suffered a missed signal. */ + /** + * Returns {@code true} when at least one condition suffered a missed signal. + * + * @return the has issues + */ public boolean hasIssues() { return !missedConditions.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MutableMapKeyDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MutableMapKeyDetector.java index 06e4fb32..2cf642f4 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MutableMapKeyDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MutableMapKeyDetector.java @@ -119,7 +119,9 @@ public static class MutableMapKeyReport { final List mutatedKeys = new ArrayList<>(); final List mutationDetails = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !mutatedKeys.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NestedMonitorLockoutDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NestedMonitorLockoutDetector.java index 6bd96743..103d93f6 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NestedMonitorLockoutDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NestedMonitorLockoutDetector.java @@ -103,7 +103,9 @@ public NestedMonitorLockoutReport analyze() { public static class NestedMonitorLockoutReport { final List incidents = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !incidents.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NonAtomicConcurrentMapUpdateDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NonAtomicConcurrentMapUpdateDetector.java index ea19a1e5..7df56aaa 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NonAtomicConcurrentMapUpdateDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NonAtomicConcurrentMapUpdateDetector.java @@ -143,7 +143,9 @@ public static final class Report { /** The structured violations. */ public final List structuredViolations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NotifyAllValidator.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NotifyAllValidator.java index 1fef0bd4..5a53cf30 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NotifyAllValidator.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NotifyAllValidator.java @@ -155,7 +155,9 @@ public static class NotifyAllReport { /** The notify instead of notify all. */ public final Set notifyInsteadOfNotifyAll = new HashSet<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !notifyInsteadOfNotifyAll.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NotifyWithoutMonitorDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NotifyWithoutMonitorDetector.java index 444aebce..64063a23 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NotifyWithoutMonitorDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NotifyWithoutMonitorDetector.java @@ -90,7 +90,11 @@ public void recordNotifyAttempt(Object monitor, String label) { } } - /** Report produced by {@link #analyze()}. */ + /** + * Report produced by {@link #analyze()}. + * + * @return the analyze + */ public Report analyze() { Report r = new Report(); synchronized (attempts) { @@ -124,7 +128,9 @@ public static final class Report { /** The structured violations. */ public final List structuredViolations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/OptimisticReadValidationDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/OptimisticReadValidationDetector.java index de9558bc..1f4ee17a 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/OptimisticReadValidationDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/OptimisticReadValidationDetector.java @@ -53,7 +53,13 @@ private static String key(Object lock, Thread thread) { return System.identityHashCode(lock) + ":" + thread.threadId(); } - /** Call immediately after {@code StampedLock.tryOptimisticRead()}. */ + /** + * Call immediately after {@code StampedLock.tryOptimisticRead()}. + * + * @param lock the lock + * @param stamp the stamp + * @param thread the thread + */ public void recordOptimisticReadStarted(Object lock, long stamp, Thread thread) { if (lock == null || thread == null) return; OptimisticRead replaced = @@ -71,6 +77,10 @@ public void recordOptimisticReadStarted(Object lock, long stamp, Thread thread) * Call when reading a field whose value was obtained during an optimistic read. * * @param fieldName descriptive name for the data being read (for reports) + * + * @param lock the lock + * @param stamp the stamp + * @param thread the thread */ public void recordDataAccessed(Object lock, long stamp, Thread thread, String fieldName) { if (lock == null || thread == null) return; @@ -84,6 +94,10 @@ public void recordDataAccessed(Object lock, long stamp, Thread thread, String fi * Call immediately after {@code lock.validate(stamp)}. * * @param result the boolean returned by {@code validate()} + * + * @param lock the lock + * @param stamp the stamp + * @param thread the thread */ public void recordValidateCalled(Object lock, long stamp, boolean result, Thread thread) { if (lock == null || thread == null) return; @@ -103,7 +117,9 @@ public void recordValidateCalled(Object lock, long stamp, boolean result, Thread } } - /** {@return report of optimistic read validation failures} */ + /** + * {@return report of optimistic read validation failures} + */ public OptimisticReadValidationReport analyze() { OptimisticReadValidationReport r = new OptimisticReadValidationReport(); // reads still pending at analysis time were never validated @@ -126,7 +142,9 @@ private static String neverValidatedViolation(OptimisticRead read) { public static class OptimisticReadValidationReport { final List violations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ParallelStreamDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ParallelStreamDetector.java index 48389c45..3f7e7ba9 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ParallelStreamDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ParallelStreamDetector.java @@ -217,6 +217,8 @@ public static class ParallelStreamReport { /** * Check if any issues were detected. + * + * @return the has issues */ public boolean hasIssues() { return !statefulLambdas.isEmpty() || !nonThreadSafeCollectors.isEmpty() || !sideEffects.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/Phase1DetectorSet.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/Phase1DetectorSet.java index afcfcc83..45a06579 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/Phase1DetectorSet.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/Phase1DetectorSet.java @@ -77,6 +77,9 @@ private Phase1DetectorSet(@Nullable VisibilityMonitor visibility, *

Prefer {@link #from(AsyncTestConfig, AsyncTestContext)} whenever a context is * available (the runner always has one) — this overload always constructs fresh, * disconnected instances and is kept only for direct/unit-test construction. + * + * @param config the config + * @return the from */ public static Phase1DetectorSet from(AsyncTestConfig config) { return from(config, null); @@ -102,6 +105,10 @@ public static Phase1DetectorSet from(AsyncTestConfig config) { * disconnected-but-functional detector is safer than a {@code NullPointerException}. * * @since 1.7.0 + * + * @param config the config + * @param ctx the ctx + * @return the from */ public static Phase1DetectorSet from(AsyncTestConfig config, @Nullable AsyncTestContext ctx) { return new Phase1DetectorSet( @@ -162,6 +169,8 @@ public void printReports() { * so reporting them here too would double-count and double-print every finding. * * @since 1.7.0 + * + * @return the collect reports */ public Map collectReports() { Map out = new LinkedHashMap<>(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/PhaserDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/PhaserDetector.java index febe9d7c..96e4ff9a 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/PhaserDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/PhaserDetector.java @@ -23,6 +23,10 @@ public class PhaserDetector { /** * Register a Phaser for monitoring. + * + * @param phaser the phaser + * @param name the name + * @param parties the parties */ public void registerPhaser(Phaser phaser, String name, int parties) { phaserRegistry.put(phaser, new PhaserInfo(name, parties)); @@ -30,6 +34,8 @@ public void registerPhaser(Phaser phaser, String name, int parties) { /** * Record a thread arriving at the phaser. + * + * @param phaser the phaser */ public void recordArrive(Phaser phaser) { PhaserInfo info = phaserRegistry.get(phaser); @@ -40,6 +46,8 @@ public void recordArrive(Phaser phaser) { /** * Record a thread arriving and awaiting advance. + * + * @param phaser the phaser */ public void recordArriveAwaitAdvance(Phaser phaser) { PhaserInfo info = phaserRegistry.get(phaser); @@ -51,6 +59,8 @@ public void recordArriveAwaitAdvance(Phaser phaser) { /** * Record a phaser await that timed out. + * + * @param phaser the phaser */ public void recordTimeout(Phaser phaser) { timedOutPhasers.add(phaser); @@ -58,6 +68,8 @@ public void recordTimeout(Phaser phaser) { /** * Record a phaser that was terminated. + * + * @param phaser the phaser */ public void recordTermination(Phaser phaser) { terminatedPhasers.add(phaser); @@ -65,6 +77,9 @@ public void recordTermination(Phaser phaser) { /** * Record successful phaser phase completion. + * + * @param phaser the phaser + * @param phase the phase */ public void recordPhaseComplete(Phaser phaser, int phase) { PhaserInfo info = phaserRegistry.get(phaser); @@ -75,6 +90,8 @@ public void recordPhaseComplete(Phaser phaser, int phase) { /** * Analyze phaser usage and return report. + * + * @return the analyze */ public PhaserReport analyze() { return new PhaserReport( @@ -102,7 +119,9 @@ public PhaserReport( this.terminatedPhasers = Collections.unmodifiableSet(new HashSet<>(terminatedPhasers)); } - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !timedOutPhasers.isEmpty() || !terminatedPhasers.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/PipelineMonitor.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/PipelineMonitor.java index a6ed9f28..96e7795e 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/PipelineMonitor.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/PipelineMonitor.java @@ -38,6 +38,8 @@ private static class PipelineStage { /** * Register a pipeline stage. + * + * @param stageName the stage name */ public void registerStage(String stageName) { stages.putIfAbsent(stageName, new PipelineStage(stageName)); @@ -45,6 +47,9 @@ public void registerStage(String stageName) { /** * Record event published to a stage. + * + * @param stageName the stage name + * @param eventId the event id */ public void recordEventPublished(String stageName, String eventId) { if (!enabled) return; @@ -56,6 +61,9 @@ public void recordEventPublished(String stageName, String eventId) { /** * Record event processed by a stage. + * + * @param stageName the stage name + * @param eventId the event id */ public void recordEventProcessed(String stageName, String eventId) { if (!enabled) return; @@ -69,6 +77,10 @@ public void recordEventProcessed(String stageName, String eventId) { /** * Record event failure. + * + * @param stageName the stage name + * @param eventId the event id + * @param reason the reason */ public void recordEventFailed(String stageName, String eventId, String reason) { if (!enabled) return; @@ -83,6 +95,8 @@ public void recordEventFailed(String stageName, String eventId, String reason) { /** * Analyze pipeline for signal loss. + * + * @return the analyze pipeline */ public PipelineReport analyzePipeline() { PipelineReport report = new PipelineReport(); @@ -117,6 +131,8 @@ public PipelineReport analyzePipeline() { /** * Standardized alias for {@link #analyzePipeline()}. + * + * @return the analyze */ public PipelineReport analyze() { return analyzePipeline(); @@ -152,7 +168,9 @@ public static class PipelineReport { /** The low processing rate. */ public final Set lowProcessingRate = new HashSet<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !missingEvents.isEmpty() || !failedEvents.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/PublicLockExposureDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/PublicLockExposureDetector.java index 1a4ae1e8..f7dc90ce 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/PublicLockExposureDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/PublicLockExposureDetector.java @@ -63,7 +63,9 @@ public void recordObjectPublished(Object obj, String context) { if (context != null) publishContexts.put(id, context); } - /** {@return report of publicly exposed internal locks} */ + /** + * {@return report of publicly exposed internal locks} + */ public PublicLockExposureReport analyze() { PublicLockExposureReport r = new PublicLockExposureReport(); for (int id : synchronizedObjects) { @@ -83,7 +85,9 @@ public PublicLockExposureReport analyze() { public static class PublicLockExposureReport { final List violations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/RaceConditionDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/RaceConditionDetector.java index b1c7a600..803f56e6 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/RaceConditionDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/RaceConditionDetector.java @@ -161,6 +161,8 @@ public RaceConditionReport analyzeRaceConditions() { /** * Standardized alias for {@link #analyzeRaceConditions()}. + * + * @return the analyze */ public RaceConditionReport analyze() { return analyzeRaceConditions(); @@ -247,7 +249,9 @@ public static class RaceConditionReport { /** The potential races. */ public final Set potentialRaces = new HashSet<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !unsafeAccesses.isEmpty() || !potentialRaces.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ReadWriteLockMonitor.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ReadWriteLockMonitor.java index cc66d4c7..d509b109 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ReadWriteLockMonitor.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ReadWriteLockMonitor.java @@ -38,6 +38,9 @@ private static class LockState { /** * Register a read-write lock for monitoring. + * + * @param rwLock the rw lock + * @param name the name */ public void registerLock(Object rwLock, String name) { if (!enabled) return; @@ -48,6 +51,9 @@ public void registerLock(Object rwLock, String name) { /** * Record read lock acquisition. + * + * @param rwLock the rw lock + * @param waitTimeMs the wait time in milliseconds */ public void recordReadLockAcquired(Object rwLock, long waitTimeMs) { if (!enabled) return; @@ -63,6 +69,8 @@ public void recordReadLockAcquired(Object rwLock, long waitTimeMs) { /** * Record read lock release. + * + * @param rwLock the rw lock */ public void recordReadLockReleased(Object rwLock) { if (!enabled) return; @@ -76,6 +84,9 @@ public void recordReadLockReleased(Object rwLock) { /** * Record write lock acquisition. + * + * @param rwLock the rw lock + * @param waitTimeMs the wait time in milliseconds */ public void recordWriteLockAcquired(Object rwLock, long waitTimeMs) { if (!enabled) return; @@ -97,6 +108,8 @@ public void recordWriteLockAcquired(Object rwLock, long waitTimeMs) { /** * Record write lock release. + * + * @param rwLock the rw lock */ public void recordWriteLockReleased(Object rwLock) { if (!enabled) return; @@ -110,6 +123,8 @@ public void recordWriteLockReleased(Object rwLock) { /** * Analyze read-write lock fairness. + * + * @return the analyze fairness */ public ReadWriteLockReport analyzeFairness() { ReadWriteLockReport report = new ReadWriteLockReport(); @@ -160,6 +175,8 @@ public ReadWriteLockReport analyzeFairness() { /** * Standardized alias for {@link #analyzeFairness()}. + * + * @return the analyze */ public ReadWriteLockReport analyze() { return analyzeFairness(); @@ -198,7 +215,9 @@ public static class ReadWriteLockReport { /** The current read holders. */ public final Set currentReadHolders = new HashSet<>(); - /** {@return whether there are fairness issues} */ + /** + * {@return whether there are fairness issues} + */ public boolean hasFairnessIssues() { return !readerDominatedLocks.isEmpty() || !starvedWriters.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ReentrantLockDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ReentrantLockDetector.java index 478354a4..35f88caa 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ReentrantLockDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ReentrantLockDetector.java @@ -25,6 +25,9 @@ public class ReentrantLockDetector { /** * Register a ReentrantLock for monitoring. + * + * @param lock the lock + * @param name the name */ public void registerLock(ReentrantLock lock, String name) { lockRegistry.put(lock, new LockInfo(name)); @@ -32,6 +35,9 @@ public void registerLock(ReentrantLock lock, String name) { /** * Record a successful lock acquisition. + * + * @param lock the lock + * @param threadName the thread name */ public void recordLockAcquired(ReentrantLock lock, String threadName) { LockInfo info = lockRegistry.get(lock); @@ -42,6 +48,9 @@ public void recordLockAcquired(ReentrantLock lock, String threadName) { /** * Record a lock release. + * + * @param lock the lock + * @param threadName the thread name */ public void recordLockReleased(ReentrantLock lock, String threadName) { LockInfo info = lockRegistry.get(lock); @@ -52,6 +61,8 @@ public void recordLockReleased(ReentrantLock lock, String threadName) { /** * Record a tryLock() that timed out. + * + * @param lock the lock */ public void recordLockTimeout(ReentrantLock lock) { timeoutLocks.add(lock); @@ -59,6 +70,9 @@ public void recordLockTimeout(ReentrantLock lock) { /** * Record potential lock starvation (wait time exceeds threshold). + * + * @param threadName the thread name + * @param waitTimeMs the wait time in milliseconds */ public void recordStarvation(String threadName, long waitTimeMs) { starvationThreads.add(threadName + " (waited " + waitTimeMs + "ms)"); @@ -66,6 +80,8 @@ public void recordStarvation(String threadName, long waitTimeMs) { /** * Analyze lock usage and return report. + * + * @return the analyze */ public ReentrantLockReport analyze() { return new ReentrantLockReport( @@ -93,7 +109,9 @@ public ReentrantLockReport( this.starvationThreads = Collections.unmodifiableSet(new HashSet<>(starvationThreads)); } - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !timeoutLocks.isEmpty() || !starvationThreads.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ResourceLeakDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ResourceLeakDetector.java index ba309616..061c9d95 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ResourceLeakDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ResourceLeakDetector.java @@ -168,6 +168,8 @@ public static class ResourceLeakReport { /** * Check if any issues were detected. + * + * @return the has issues */ public boolean hasIssues() { return !resourceLeaks.isEmpty() || !openResources.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ScheduledExecutorDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ScheduledExecutorDetector.java index 5430c92d..d787b1c4 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ScheduledExecutorDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ScheduledExecutorDetector.java @@ -24,6 +24,10 @@ public class ScheduledExecutorDetector { /** * Register a ScheduledExecutorService for monitoring. + * + * @param executor the executor + * @param name the name + * @param corePoolSize the core pool size */ public void registerExecutor(ScheduledExecutorService executor, String name, int corePoolSize) { executorRegistry.put(executor, new ExecutorInfo(name, corePoolSize)); @@ -31,6 +35,10 @@ public void registerExecutor(ScheduledExecutorService executor, String name, int /** * Record a task being scheduled. + * + * @param executor the executor + * @param executorName the executor name + * @param taskName the task name */ public void recordSchedule(ScheduledExecutorService executor, String executorName, String taskName) { ExecutorInfo info = executorRegistry.get(executor); @@ -41,6 +49,10 @@ public void recordSchedule(ScheduledExecutorService executor, String executorNam /** * Record a task starting execution. + * + * @param executor the executor + * @param executorName the executor name + * @param taskName the task name */ public void recordTaskStart(ScheduledExecutorService executor, String executorName, String taskName) { ExecutorInfo info = executorRegistry.get(executor); @@ -51,6 +63,11 @@ public void recordTaskStart(ScheduledExecutorService executor, String executorNa /** * Record a task completing execution. + * + * @param executor the executor + * @param executorName the executor name + * @param taskName the task name + * @param durationMs the duration in milliseconds */ public void recordTaskComplete(ScheduledExecutorService executor, String executorName, String taskName, long durationMs) { ExecutorInfo info = executorRegistry.get(executor); @@ -64,6 +81,9 @@ public void recordTaskComplete(ScheduledExecutorService executor, String executo /** * Record an exception in a scheduled task. + * + * @param executor the executor + * @param executorName the executor name */ public void recordException(ScheduledExecutorService executor, String executorName) { exceptionInTasks++; @@ -71,6 +91,8 @@ public void recordException(ScheduledExecutorService executor, String executorNa /** * Record executor shutdown. + * + * @param executor the executor */ public void recordShutdown(ScheduledExecutorService executor) { ExecutorInfo info = executorRegistry.get(executor); @@ -92,6 +114,8 @@ public void checkShutdown() { /** * Analyze ScheduledExecutorService usage and return report. + * + * @return the analyze */ public ScheduledExecutorReport analyze() { checkShutdown(); @@ -124,7 +148,9 @@ public ScheduledExecutorReport( this.exceptionInTasks = exceptionInTasks; } - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !notShutdownExecutors.isEmpty() || !longRunningTasks.isEmpty() diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ScopedValueMisuseDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ScopedValueMisuseDetector.java index 0092f546..12ae9f03 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ScopedValueMisuseDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ScopedValueMisuseDetector.java @@ -182,22 +182,36 @@ public static class ScopedValueMisuseReport { this.unboundGetCount = unboundGetCount; } - /** {@return true if any ScopedValue misuse issues were detected} */ + /** + * {@return true if any ScopedValue misuse issues were detected} + */ public boolean hasIssues() { return !unboundGetIssues.isEmpty() || !rebindIssues.isEmpty(); } - /** {@return the unbound get issues} */ + /** + * {@return the unbound get issues} + */ public List getUnboundGetIssues() { return Collections.unmodifiableList(unboundGetIssues); } - /** {@return the rebind issues} */ + /** + * {@return the rebind issues} + */ public List getRebindIssues() { return Collections.unmodifiableList(rebindIssues); } - /** {@return the high binding warnings} */ + /** + * {@return the high binding warnings} + */ public List getHighBindingWarnings() { return Collections.unmodifiableList(highBindingWarnings); } - /** {@return the total bindings} */ + /** + * {@return the total bindings} + */ public int getTotalBindings() { return totalBindings; } - /** {@return the total get calls} */ + /** + * {@return the total get calls} + */ public int getTotalGetCalls() { return totalGetCalls; } - /** {@return the unbound get count} */ + /** + * {@return the unbound get count} + */ public int getUnboundGetCount() { return unboundGetCount; } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SemaphoreMisuseDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SemaphoreMisuseDetector.java index 7485648e..ba2a4c86 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SemaphoreMisuseDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SemaphoreMisuseDetector.java @@ -172,6 +172,8 @@ public static class SemaphoreMisuseReport { /** * Check if any issues were detected. + * + * @return the has issues */ public boolean hasIssues() { return !permitLeaks.isEmpty() || !overReleases.isEmpty() || !unreleasedPermits.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedByteBufferDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedByteBufferDetector.java index ecc962e7..4b5845d1 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedByteBufferDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedByteBufferDetector.java @@ -173,7 +173,9 @@ public static final class Report { /** The structured violations. */ public final List structuredViolations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedCharsetCoderDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedCharsetCoderDetector.java index dfbe3abd..b20a77ff 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedCharsetCoderDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedCharsetCoderDetector.java @@ -145,7 +145,9 @@ public static final class Report { /** The structured violations. */ public final List structuredViolations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedChecksumDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedChecksumDetector.java index 999a12f0..d2feb1f8 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedChecksumDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedChecksumDetector.java @@ -120,7 +120,9 @@ public static final class Report { /** The structured violations. */ public final List structuredViolations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedCollectionDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedCollectionDetector.java index c8b0fea5..c8da1b41 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedCollectionDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedCollectionDetector.java @@ -122,6 +122,8 @@ private CollectionState resolveState(Object collection, String name) { /** * Analyse collection usage and return a report. + * + * @return the analyze */ public SharedCollectionReport analyze() { SharedCollectionReport report = new SharedCollectionReport(); @@ -165,7 +167,11 @@ public static class SharedCollectionReport { final java.util.List mixedAccessViolations = new java.util.ArrayList<>(); final Map collectionActivity = new ConcurrentHashMap<>(); - /** Returns {@code true} when any concurrent-access violations were detected. */ + /** + * Returns {@code true} when any concurrent-access violations were detected. + * + * @return the has issues + */ public boolean hasIssues() { return !concurrentWriteViolations.isEmpty() || !mixedAccessViolations.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedDecimalFormatDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedDecimalFormatDetector.java index 37bb8271..4834881b 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedDecimalFormatDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedDecimalFormatDetector.java @@ -52,7 +52,9 @@ public void recordAccess(Object format, String name, Thread thread) { s.accessingThreadNames.add(thread.getName()); } - /** {@return report of DecimalFormat/NumberFormat instances accessed from multiple threads} */ + /** + * {@return report of DecimalFormat/NumberFormat instances accessed from multiple threads} + */ public SharedDecimalFormatReport analyze() { SharedDecimalFormatReport r = new SharedDecimalFormatReport(); for (FormatState s : formats.values()) { @@ -70,7 +72,9 @@ public SharedDecimalFormatReport analyze() { public static class SharedDecimalFormatReport { final List violations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedDeflaterDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedDeflaterDetector.java index 121de0b4..09a36fa1 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedDeflaterDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedDeflaterDetector.java @@ -138,7 +138,9 @@ public static final class Report { /** The structured violations. */ public final List structuredViolations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedFormatterDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedFormatterDetector.java index e26d4a6f..6a6bbe45 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedFormatterDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedFormatterDetector.java @@ -50,7 +50,9 @@ public void recordAccess(Object formatter, String name, Thread thread) { s.accessingThreadNames.add(thread.getName()); } - /** {@return report of formatters accessed from multiple threads} */ + /** + * {@return report of formatters accessed from multiple threads} + */ public SharedFormatterReport analyze() { SharedFormatterReport r = new SharedFormatterReport(); for (FormatterState s : formatters.values()) { @@ -68,7 +70,9 @@ public SharedFormatterReport analyze() { public static class SharedFormatterReport { final List violations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedIteratorDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedIteratorDetector.java index 292e0c56..fd420f14 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedIteratorDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedIteratorDetector.java @@ -154,7 +154,9 @@ public static final class Report { /** The structured violations. */ public final List structuredViolations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedJsonMapperReconfigDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedJsonMapperReconfigDetector.java index d5b58a65..9636a7a3 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedJsonMapperReconfigDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedJsonMapperReconfigDetector.java @@ -185,7 +185,9 @@ public static final class Report { /** The structured violations. */ public final List structuredViolations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedKdfDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedKdfDetector.java index efbd389c..4212848e 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedKdfDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedKdfDetector.java @@ -130,7 +130,9 @@ public static final class Report { /** The structured violations. */ public final List structuredViolations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedMatcherDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedMatcherDetector.java index 5ffda789..8ec3f1ed 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedMatcherDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedMatcherDetector.java @@ -51,7 +51,9 @@ public void recordAccess(Object matcher, String name, Thread thread) { s.accessingThreadNames.add(thread.getName()); } - /** {@return report of Matchers accessed from multiple threads} */ + /** + * {@return report of Matchers accessed from multiple threads} + */ public SharedMatcherReport analyze() { SharedMatcherReport r = new SharedMatcherReport(); for (MatcherState s : matchers.values()) { @@ -70,7 +72,9 @@ public SharedMatcherReport analyze() { public static class SharedMatcherReport { final List violations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedMessageDigestDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedMessageDigestDetector.java index 0f3c0827..2d3de64b 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedMessageDigestDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedMessageDigestDetector.java @@ -96,7 +96,9 @@ public void recordAccess(Object digest, String name, Thread thread) { SiteCapture.capture().ifPresent(s.accessSites::add); } - /** {@return report of JCA instances accessed from multiple threads} */ + /** + * {@return report of JCA instances accessed from multiple threads} + */ public SharedMessageDigestReport analyze() { SharedMessageDigestReport r = new SharedMessageDigestReport(); for (DigestState s : digests.values()) { @@ -167,7 +169,9 @@ public static class SharedMessageDigestReport { /** Structured mirror of {@link #violations} for {@link se.deversity.asynctest.report.Formatter}s. */ public final List structuredViolations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedRandomDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedRandomDetector.java index 5202684f..37722be0 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedRandomDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedRandomDetector.java @@ -156,6 +156,8 @@ public static class SharedRandomReport { /** * Check if any issues were detected. + * + * @return the has issues */ public boolean hasIssues() { return !sharedRandoms.isEmpty() || !highContention.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedSecureRandomDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedSecureRandomDetector.java index 9324e4ce..ee6ba942 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedSecureRandomDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedSecureRandomDetector.java @@ -143,7 +143,9 @@ public static final class Report { /** The structured violations. */ public final List structuredViolations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedStatefulCryptoDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedStatefulCryptoDetector.java index 69ce477c..5b987799 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedStatefulCryptoDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedStatefulCryptoDetector.java @@ -172,7 +172,9 @@ public static final class Report { /** The structured violations. */ public final List structuredViolations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedTimeZoneDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedTimeZoneDetector.java index e71a4a16..e1924646 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedTimeZoneDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedTimeZoneDetector.java @@ -55,7 +55,9 @@ public void recordMutation(Object timeZone, String operation, Thread thread) { s.mutatingThreadNames.add(thread.getName()); } - /** {@return report of TimeZone instances mutated from multiple threads} */ + /** + * {@return report of TimeZone instances mutated from multiple threads} + */ public SharedTimeZoneReport analyze() { SharedTimeZoneReport r = new SharedTimeZoneReport(); for (TzState s : timezones.values()) { @@ -75,7 +77,9 @@ public SharedTimeZoneReport analyze() { public static class SharedTimeZoneReport { final List violations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedXmlParserDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedXmlParserDetector.java index c1a2d8a8..0f22b593 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedXmlParserDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedXmlParserDetector.java @@ -61,7 +61,9 @@ public void recordAccess(Object parser, String parserType, Thread thread) { s.accessingThreadNames.add(thread.getName()); } - /** {@return report of XML parser instances accessed from multiple threads} */ + /** + * {@return report of XML parser instances accessed from multiple threads} + */ public SharedXmlParserReport analyze() { SharedXmlParserReport r = new SharedXmlParserReport(); for (ParserState s : parsers.values()) { @@ -81,7 +83,9 @@ public SharedXmlParserReport analyze() { public static class SharedXmlParserReport { final List violations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SimpleDateFormatDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SimpleDateFormatDetector.java index fb488ff0..ffdae614 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SimpleDateFormatDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SimpleDateFormatDetector.java @@ -200,6 +200,8 @@ public static class SimpleDateFormatReport { /** * Check if any issues were detected. + * + * @return the has issues */ public boolean hasIssues() { return !sharedFormatters.isEmpty() || !formattingErrors.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SiteCapture.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SiteCapture.java index ef2ec8c2..16b7a919 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SiteCapture.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SiteCapture.java @@ -67,6 +67,8 @@ private SiteCapture() {} /** * Returns the first stack frame outside the framework / JDK reflection / * JUnit, or {@link Optional#empty()} if none could be identified (rare). + * + * @return the capture */ public static Optional capture() { return WALKER.walk(stream -> stream @@ -115,7 +117,11 @@ static Site of(StackFrame f) { f.getLineNumber()); } - /** Human-readable {@code Class.method(File.java:42)} form. */ + /** + * Human-readable {@code Class.method(File.java:42)} form. + * + * @return the render + */ public String render() { String shortClass = className.contains(".") ? className.substring(className.lastIndexOf('.') + 1) diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SleepInLockDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SleepInLockDetector.java index 26dc02c4..573ce819 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SleepInLockDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SleepInLockDetector.java @@ -234,12 +234,16 @@ public static class SleepInLockReport { this.totalCount = totalCount; } - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !events.isEmpty(); } - /** {@return the events} */ + /** + * {@return the events} + */ public List getEvents() { return List.copyOf(events); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SpuriousWakeupDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SpuriousWakeupDetector.java index 3f813e3c..ff9baaea 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SpuriousWakeupDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SpuriousWakeupDetector.java @@ -36,6 +36,11 @@ private static final class State { /** * Record a wait/await operation. + * + * @param monitor the monitor + * @param monitorName the monitor name + * @param insideLoop the {@code insideLoop} flag + * @param thread the thread */ public void recordWait(Object monitor, String monitorName, boolean insideLoop, Thread thread) { if (monitor == null || thread == null) return; @@ -84,7 +89,9 @@ public static final class Report { /** The structured violations. */ public final List structuredViolations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StableValueMisuseDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StableValueMisuseDetector.java index 42b8205a..59764545 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StableValueMisuseDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StableValueMisuseDetector.java @@ -239,24 +239,38 @@ public static class StableValueMisuseReport { this.totalSets = totalSets; } - /** {@return true if any correctness-affecting StableValue misuse was detected} */ + /** + * {@return true if any correctness-affecting StableValue misuse was detected} + */ public boolean hasIssues() { return !readBeforeSetIssues.isEmpty() || !doubleSetIssues.isEmpty() || !reentrantIssues.isEmpty(); } - /** {@return the read before set issues} */ + /** + * {@return the read before set issues} + */ public List getReadBeforeSetIssues() { return Collections.unmodifiableList(readBeforeSetIssues); } - /** {@return the double set issues} */ + /** + * {@return the double set issues} + */ public List getDoubleSetIssues() { return Collections.unmodifiableList(doubleSetIssues); } - /** {@return the reentrant issues} */ + /** + * {@return the reentrant issues} + */ public List getReentrantIssues() { return Collections.unmodifiableList(reentrantIssues); } - /** {@return the contention warnings} */ + /** + * {@return the contention warnings} + */ public List getContentionWarnings() { return Collections.unmodifiableList(contentionWarnings); } - /** {@return the total reads} */ + /** + * {@return the total reads} + */ public int getTotalReads() { return totalReads; } - /** {@return the total sets} */ + /** + * {@return the total sets} + */ public int getTotalSets() { return totalSets; } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StampedLockDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StampedLockDetector.java index 74963447..a6b6de52 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StampedLockDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StampedLockDetector.java @@ -22,6 +22,9 @@ public class StampedLockDetector { /** * Register a StampedLock for monitoring. + * + * @param lock the lock + * @param name the name */ public void registerLock(StampedLock lock, String name) { lockRegistry.put(lock, new LockInfo(name)); @@ -29,6 +32,10 @@ public void registerLock(StampedLock lock, String name) { /** * Record an optimistic read stamp acquisition. + * + * @param lock the lock + * @param lockName the lock name + * @param stamp the stamp */ public void recordOptimisticRead(StampedLock lock, String lockName, long stamp) { LockInfo info = lockRegistry.get(lock); @@ -39,6 +46,11 @@ public void recordOptimisticRead(StampedLock lock, String lockName, long stamp) /** * Record validation of optimistic read. + * + * @param lock the lock + * @param lockName the lock name + * @param stamp the stamp + * @param validated the {@code validated} flag */ public void recordOptimisticValidation(StampedLock lock, String lockName, long stamp, boolean validated) { if (!validated) { @@ -48,6 +60,10 @@ public void recordOptimisticValidation(StampedLock lock, String lockName, long s /** * Record a read lock acquisition. + * + * @param lock the lock + * @param lockName the lock name + * @param stamp the stamp */ public void recordReadLock(StampedLock lock, String lockName, long stamp) { LockInfo info = lockRegistry.get(lock); @@ -58,6 +74,10 @@ public void recordReadLock(StampedLock lock, String lockName, long stamp) { /** * Record a write lock acquisition. + * + * @param lock the lock + * @param lockName the lock name + * @param stamp the stamp */ public void recordWriteLock(StampedLock lock, String lockName, long stamp) { LockInfo info = lockRegistry.get(lock); @@ -68,6 +88,10 @@ public void recordWriteLock(StampedLock lock, String lockName, long stamp) { /** * Record a lock release. + * + * @param lock the lock + * @param lockName the lock name + * @param stamp the stamp */ public void recordUnlock(StampedLock lock, String lockName, long stamp) { LockInfo info = lockRegistry.get(lock); @@ -78,6 +102,9 @@ public void recordUnlock(StampedLock lock, String lockName, long stamp) { /** * Record a stamp that was not released. + * + * @param lockName the lock name + * @param stamp the stamp */ public void recordStampNotReleased(String lockName, long stamp) { stampNotReleased.add(lockName + " (stamp: " + stamp + ")"); @@ -85,6 +112,8 @@ public void recordStampNotReleased(String lockName, long stamp) { /** * Analyze StampedLock usage and return report. + * + * @return the analyze */ public StampedLockReport analyze() { return new StampedLockReport( @@ -108,7 +137,9 @@ public StampedLockReport( this.stampNotReleased = Collections.unmodifiableSet(new HashSet<>(stampNotReleased)); } - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !unvalidatedOptimisticReads.isEmpty() || !stampNotReleased.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StatefulLambdaDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StatefulLambdaDetector.java index 6f938573..a1d8fd6b 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StatefulLambdaDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StatefulLambdaDetector.java @@ -79,7 +79,9 @@ public void recordCapturedMutation(Object lambda, String capturedName, Thread th s.mutationEvents.add(thread.getName() + " → " + label); } - /** {@return report of lambdas with concurrent captured-state mutations} */ + /** + * {@return report of lambdas with concurrent captured-state mutations} + */ public StatefulLambdaReport analyze() { StatefulLambdaReport r = new StatefulLambdaReport(); for (LambdaState s : lambdas.values()) { @@ -98,7 +100,9 @@ public StatefulLambdaReport analyze() { public static class StatefulLambdaReport { final List violations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StreamClosingDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StreamClosingDetector.java index 4c552667..7068943c 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StreamClosingDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StreamClosingDetector.java @@ -181,6 +181,8 @@ public static class StreamClosingReport { /** * Check if any issues were detected. + * + * @return the has issues */ public boolean hasIssues() { return !unclosedStreams.isEmpty() || diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StringBuilderDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StringBuilderDetector.java index 5dc15915..56b56904 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StringBuilderDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StringBuilderDetector.java @@ -161,6 +161,8 @@ private BuilderState resolve(StringBuilder builder, String name) { /** * Analyse StringBuilder usage and return a report. + * + * @return the analyze */ public StringBuilderReport analyze() { StringBuilderReport report = new StringBuilderReport(); @@ -210,7 +212,11 @@ public static class StringBuilderReport { final java.util.List builderErrors = new java.util.ArrayList<>(); final Map builderActivity = new ConcurrentHashMap<>(); - /** Returns {@code true} when shared-mutation or errors were detected. */ + /** + * Returns {@code true} when shared-mutation or errors were detected. + * + * @return the has issues + */ public boolean hasIssues() { return !sharedBuilderViolations.isEmpty() || !builderErrors.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StructuredConcurrencyMisuseDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StructuredConcurrencyMisuseDetector.java index b770acb1..5aed5935 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StructuredConcurrencyMisuseDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StructuredConcurrencyMisuseDetector.java @@ -211,7 +211,9 @@ public static class StructuredConcurrencyReport { this.maxNestingDepth = maxNestingDepth; } - /** {@return true if any structured concurrency issues were detected} */ + /** + * {@return true if any structured concurrency issues were detected} + */ public boolean hasIssues() { return !unclosedScopes.isEmpty() || !closedWithoutJoin.isEmpty() @@ -219,15 +221,25 @@ public boolean hasIssues() { || !emptyScopes.isEmpty(); } - /** {@return the unclosed scopes} */ + /** + * {@return the unclosed scopes} + */ public List getUnclosedScopes() { return Collections.unmodifiableList(unclosedScopes); } - /** {@return the closed without join} */ + /** + * {@return the closed without join} + */ public List getClosedWithoutJoin() { return Collections.unmodifiableList(closedWithoutJoin); } - /** {@return the result accessed before join} */ + /** + * {@return the result accessed before join} + */ public List getResultAccessedBeforeJoin() { return Collections.unmodifiableList(resultAccessedBeforeJoin); } - /** {@return the empty scopes} */ + /** + * {@return the empty scopes} + */ public List getEmptyScopes() { return Collections.unmodifiableList(emptyScopes); } - /** {@return the max nesting depth} */ + /** + * {@return the max nesting depth} + */ public int getMaxNestingDepth() { return maxNestingDepth; } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StructuredTaskScopeMisuseDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StructuredTaskScopeMisuseDetector.java index 678ec18f..5429f9a9 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StructuredTaskScopeMisuseDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StructuredTaskScopeMisuseDetector.java @@ -108,6 +108,9 @@ private static final class ScopeState { /** * Record that a {@code StructuredTaskScope.open(...)} returned a new scope, * confined to the opening (owner) thread. + * + * @param scopeId the scope id + * @param owner the owner */ public void recordScopeOpened(String scopeId, Thread owner) { if (scopeId == null || owner == null) return; @@ -118,6 +121,10 @@ public void recordScopeOpened(String scopeId, Thread owner) { /** * Record a {@code scope.fork(task)} call. Flags fork-after-join and * owner-confinement violations. + * + * @param scopeId the scope id + * @param subtaskId the subtask id + * @param thread the thread */ public void recordFork(String scopeId, String subtaskId, Thread thread) { if (scopeId == null || subtaskId == null || thread == null) return; @@ -147,6 +154,9 @@ public void recordFork(String scopeId, String subtaskId, Thread thread) { /** * Record a {@code scope.join()} call. Flags owner-confinement violations and * marks the scope as joined. + * + * @param scopeId the scope id + * @param thread the thread */ public void recordJoin(String scopeId, Thread thread) { if (scopeId == null || thread == null) return; @@ -168,6 +178,10 @@ public void recordJoin(String scopeId, Thread thread) { * Record a {@code Subtask.get()} call. Flags reads that happen before the * scope has been joined, and reads that happen after {@code join()} timed out * (the subtask state is not {@code SUCCESS}, so {@code get()} throws). + * + * @param scopeId the scope id + * @param subtaskId the subtask id + * @param thread the thread */ public void recordResultRead(String scopeId, String subtaskId, Thread thread) { if (scopeId == null || subtaskId == null || thread == null) return; @@ -199,6 +213,9 @@ public void recordResultRead(String scopeId, String subtaskId, Thread thread) { * cancelled. Marks the scope as no longer accepting result reads. * * @since 1.7.0 + * + * @param scopeId the scope id + * @param thread the thread */ public void recordJoinTimeout(String scopeId, Thread thread) { if (scopeId == null || thread == null) return; @@ -224,6 +241,9 @@ public void recordJoinTimeout(String scopeId, Thread thread) { * be half-applied, and the fallback must not depend on their state. * * @since 1.7.0 + * + * @param scopeId the scope id + * @param thread the thread */ public void recordTimeoutSwallowed(String scopeId, Thread thread) { if (scopeId == null || thread == null) return; @@ -247,6 +267,9 @@ public void recordTimeoutSwallowed(String scopeId, Thread thread) { /** * Record that the scope was closed (the try-with-resources block ended). * Flags a scope that forked subtasks but was never joined. + * + * @param scopeId the scope id + * @param thread the thread */ public void recordScopeClosed(String scopeId, Thread thread) { if (scopeId == null || thread == null) return; @@ -313,7 +336,9 @@ public static class StructuredTaskScopeMisuseReport { this.totalForks = totalForks; } - /** {@return true if any StructuredTaskScope misuse was detected} */ + /** + * {@return true if any StructuredTaskScope misuse was detected} + */ public boolean hasIssues() { return !forkAfterJoinIssues.isEmpty() || !resultBeforeJoinIssues.isEmpty() @@ -322,21 +347,41 @@ public boolean hasIssues() { || !resultAfterTimeoutIssues.isEmpty(); } - /** {@return the fork after join issues} */ + /** + * {@return the fork after join issues} + */ public List getForkAfterJoinIssues() { return Collections.unmodifiableList(forkAfterJoinIssues); } - /** {@return the result before join issues} */ + /** + * {@return the result before join issues} + */ public List getResultBeforeJoinIssues() { return Collections.unmodifiableList(resultBeforeJoinIssues); } - /** {@return the confinement issues} */ + /** + * {@return the confinement issues} + */ public List getConfinementIssues() { return Collections.unmodifiableList(confinementIssues); } - /** {@return the missing join issues} */ + /** + * {@return the missing join issues} + */ public List getMissingJoinIssues() { return Collections.unmodifiableList(missingJoinIssues); } - /** @since 1.7.0 */ + /** + * @since 1.7.0 + * + * @return the get result after timeout issues + */ public List getResultAfterTimeoutIssues() { return Collections.unmodifiableList(resultAfterTimeoutIssues); } - /** @since 1.7.0 */ + /** + * @since 1.7.0 + * + * @return the get timeout swallowed warnings + */ public List getTimeoutSwallowedWarnings() { return Collections.unmodifiableList(timeoutSwallowedWarnings); } - /** {@return the total scopes} */ + /** + * {@return the total scopes} + */ public int getTotalScopes() { return totalScopes; } - /** {@return the total forks} */ + /** + * {@return the total forks} + */ public int getTotalForks() { return totalForks; } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizedCollectionIterationDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizedCollectionIterationDetector.java index 9b4f7974..b0dd2988 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizedCollectionIterationDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizedCollectionIterationDetector.java @@ -45,7 +45,12 @@ private static class WrapperInfo { private final Map wrappers = new ConcurrentHashMap<>(); - /** Register a synchronized wrapper created by {@code Collections.synchronized*(collection)}. */ + /** + * Register a synchronized wrapper created by {@code Collections.synchronized*(collection)}. + * + * @param wrapper the wrapper + * @param name the name + */ public void recordWrapperCreated(Object wrapper, String name) { if (wrapper == null) return; String label = name != null ? name : "collection@" + System.identityHashCode(wrapper); @@ -70,7 +75,9 @@ public void recordIterationStarted(Object wrapper, Thread thread, boolean holdin thread.getName(), info.name, info.name)); } - /** {@return report of unsafe iterations} */ + /** + * {@return report of unsafe iterations} + */ public SynchronizedCollectionIterationReport analyze() { SynchronizedCollectionIterationReport r = new SynchronizedCollectionIterationReport(); for (WrapperInfo w : wrappers.values()) { @@ -88,7 +95,9 @@ public static class SynchronizedCollectionIterationReport { final List violations = new ArrayList<>(); final List details = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizedNonFinalDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizedNonFinalDetector.java index fc37cdab..c24fe144 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizedNonFinalDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizedNonFinalDetector.java @@ -77,6 +77,8 @@ public void recordLockObject(Object lockObject, String fieldId, Class ownerCl /** * Analyses recorded lock objects and returns a report of slots where the * monitor reference changed across invocations. + * + * @return the analyze */ public SynchronizedNonFinalReport analyze() { SynchronizedNonFinalReport report = new SynchronizedNonFinalReport(); @@ -101,7 +103,11 @@ public static class SynchronizedNonFinalReport { final List violations = new ArrayList<>(); - /** Returns {@code true} when any reassignable-lock violation was detected. */ + /** + * Returns {@code true} when any reassignable-lock violation was detected. + * + * @return the has issues + */ public boolean hasIssues() { return !violations.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizedOnLiteralDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizedOnLiteralDetector.java index fd2a580c..989ebc27 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizedOnLiteralDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizedOnLiteralDetector.java @@ -78,7 +78,9 @@ public void recordMonitorAcquired(Object monitor, Thread thread, String context) return null; } - /** {@return report of synchronized-on-literal usages} */ + /** + * {@return report of synchronized-on-literal usages} + */ public SynchronizedOnLiteralReport analyze() { SynchronizedOnLiteralReport r = new SynchronizedOnLiteralReport(); for (LiteralUsage u : literals.values()) { @@ -95,7 +97,9 @@ public SynchronizedOnLiteralReport analyze() { public static class SynchronizedOnLiteralReport { final List violations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizerMonitor.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizerMonitor.java index 1e423a30..252db0a2 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizerMonitor.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizerMonitor.java @@ -38,6 +38,9 @@ private static class BarrierState { /** * Register a synchronizer for monitoring. + * + * @param synchronizer the synchronizer + * @param expectedParties the expected parties */ public void registerSynchronizer(Object synchronizer, int expectedParties) { if (!enabled) return; @@ -51,6 +54,8 @@ public void registerSynchronizer(Object synchronizer, int expectedParties) { /** * Record thread arriving at barrier. + * + * @param synchronizer the synchronizer */ public void recordBarrierArrival(Object synchronizer) { if (!enabled) return; @@ -68,6 +73,8 @@ public void recordBarrierArrival(Object synchronizer) { /** * Record thread advancing past barrier. + * + * @param synchronizer the synchronizer */ public void recordBarrierAdvance(Object synchronizer) { if (!enabled) return; @@ -82,6 +89,8 @@ public void recordBarrierAdvance(Object synchronizer) { /** * Record barrier reset. + * + * @param synchronizer the synchronizer */ public void recordBarrierReset(Object synchronizer) { if (!enabled) return; @@ -97,6 +106,8 @@ public void recordBarrierReset(Object synchronizer) { /** * Analyze synchronizer behavior. + * + * @return the analyze synchronizers */ public SynchronizerReport analyzeSynchronizers() { SynchronizerReport report = new SynchronizerReport(); @@ -126,6 +137,8 @@ public SynchronizerReport analyzeSynchronizers() { /** * Standardized alias for {@link #analyzeSynchronizers()}. + * + * @return the analyze */ public SynchronizerReport analyze() { return analyzeSynchronizers(); @@ -158,7 +171,9 @@ public static class SynchronizerReport { /** The duplicate arrivals. */ public final Set duplicateArrivals = new HashSet<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !incompleteBarriers.isEmpty() || !duplicateArrivals.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SystemPropertyMutationDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SystemPropertyMutationDetector.java index d8e6de12..96ff8dab 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SystemPropertyMutationDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SystemPropertyMutationDetector.java @@ -72,7 +72,9 @@ public void recordClear(String key, Thread thread) { events.add(new MutationEvent(key, null, thread.threadId(), thread.getName(), "clear")); } - /** {@return report of concurrent system property mutations} */ + /** + * {@return report of concurrent system property mutations} + */ public SystemPropertyMutationReport analyze() { SystemPropertyMutationReport r = new SystemPropertyMutationReport(); @@ -118,7 +120,9 @@ public static class SystemPropertyMutationReport { final List violations = new ArrayList<>(); final List singleThreadMutations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThisEscapeDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThisEscapeDetector.java index 7a9f7698..5dcd1184 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThisEscapeDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThisEscapeDetector.java @@ -165,7 +165,9 @@ public static final class Report { /** The structured violations. */ public final List structuredViolations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadFactoryDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadFactoryDetector.java index 9eb4866a..55638050 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadFactoryDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadFactoryDetector.java @@ -23,6 +23,9 @@ public class ThreadFactoryDetector { /** * Register a ThreadFactory for monitoring. + * + * @param factory the factory + * @param name the name */ public void registerFactory(ThreadFactory factory, String name) { factoryRegistry.put(factory, new FactoryInfo(name)); @@ -30,6 +33,10 @@ public void registerFactory(ThreadFactory factory, String name) { /** * Record a thread created by factory. + * + * @param factory the factory + * @param factoryName the factory name + * @param thread the thread */ public void recordThreadCreated(ThreadFactory factory, String factoryName, Thread thread) { FactoryInfo info = factoryRegistry.get(factory); @@ -55,6 +62,8 @@ public void recordThreadCreated(ThreadFactory factory, String factoryName, Threa /** * Analyze ThreadFactory usage and return report. + * + * @return the analyze */ public ThreadFactoryReport analyze() { return new ThreadFactoryReport( @@ -82,7 +91,9 @@ public ThreadFactoryReport( this.unnamedThreads = Collections.unmodifiableSet(new HashSet<>(unnamedThreads)); } - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !missingExceptionHandler.isEmpty() || !nonDaemonThreads.isEmpty() diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLeakDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLeakDetector.java index 78449a03..a43c6c57 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLeakDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLeakDetector.java @@ -243,12 +243,16 @@ public static class ThreadLeakReport { this.autoMode = autoMode; } - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !leaks.isEmpty(); } - /** {@return the leaks} */ + /** + * {@return the leaks} + */ public List getLeaks() { return List.copyOf(leaks); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLocalContaminationDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLocalContaminationDetector.java index c42f1b96..cd22696b 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLocalContaminationDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLocalContaminationDetector.java @@ -39,7 +39,12 @@ private static final class ThreadState { private final Map threadStates = new ConcurrentHashMap<>(); private final List contaminations = new CopyOnWriteArrayList<>(); - /** Call at the start of each task submitted to a thread pool. */ + /** + * Call at the start of each task submitted to a thread pool. + * + * @param thread the thread + * @param taskName the task name + */ public void recordNewTask(Thread thread, String taskName) { if (thread == null) return; ThreadState s = threadStates.computeIfAbsent(thread.threadId(), id -> new ThreadState()); @@ -47,7 +52,13 @@ public void recordNewTask(Thread thread, String taskName) { s.currentTaskName = taskName != null ? taskName : "task-" + s.taskCount; } - /** Call after each {@code ThreadLocal.set()} inside a task. */ + /** + * Call after each {@code ThreadLocal.set()} inside a task. + * + * @param thread the thread + * @param tl the tl + * @param name the name + */ public void recordSet(Thread thread, Object tl, String name) { if (thread == null || tl == null) return; ThreadState s = threadStates.get(thread.threadId()); @@ -61,6 +72,10 @@ public void recordSet(Thread thread, Object tl, String name) { * Call after each {@code ThreadLocal.get()} inside a task. * * @param hasValue {@code true} if the get returned a non-null value + * + * @param thread the thread + * @param tl the tl + * @param name the name */ public void recordGet(Thread thread, Object tl, String name, boolean hasValue) { if (thread == null || tl == null || !hasValue) return; @@ -76,7 +91,9 @@ public void recordGet(Thread thread, Object tl, String name, boolean hasValue) { } } - /** {@return report of cross-task ThreadLocal contaminations} */ + /** + * {@return report of cross-task ThreadLocal contaminations} + */ public ThreadLocalContaminationReport analyze() { ThreadLocalContaminationReport r = new ThreadLocalContaminationReport(); r.contaminations.addAll(contaminations); @@ -87,7 +104,9 @@ public ThreadLocalContaminationReport analyze() { public static class ThreadLocalContaminationReport { final List contaminations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !contaminations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLocalMonitor.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLocalMonitor.java index 88b1f0c0..86f10947 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLocalMonitor.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLocalMonitor.java @@ -122,6 +122,8 @@ public ThreadLocalReport analyzeThreadLocalLeaks() { /** * Standardized alias for {@link #analyzeThreadLocalLeaks()}. + * + * @return the analyze */ public ThreadLocalReport analyze() { return analyzeThreadLocalLeaks(); @@ -157,7 +159,9 @@ public static class ThreadLocalReport { /** The thread local accumulation. */ public final Set threadLocalAccumulation = new HashSet<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !uncleanedThreadLocals.isEmpty() || !likelyLeaks.isEmpty() diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLocalRandomMisuseDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLocalRandomMisuseDetector.java index 7b9c17a8..99b005cd 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLocalRandomMisuseDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLocalRandomMisuseDetector.java @@ -141,7 +141,9 @@ public static final class Report { /** The structured violations. */ public final List structuredViolations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadPoolMonitor.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadPoolMonitor.java index 530e9ec1..67bd483f 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadPoolMonitor.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadPoolMonitor.java @@ -45,6 +45,12 @@ private static class PoolState { /** * Register a thread pool for monitoring. + * + * @param executor the executor + * @param name the name + * @param coreSize the core size + * @param maxSize the max size + * @param queueCapacity the queue capacity */ public void registerPool(Object executor, String name, int coreSize, int maxSize, int queueCapacity) { if (!enabled) return; @@ -55,6 +61,8 @@ public void registerPool(Object executor, String name, int coreSize, int maxSize /** * Record task submission. + * + * @param executor the executor */ public void recordTaskSubmitted(Object executor) { if (!enabled) return; @@ -69,6 +77,8 @@ public void recordTaskSubmitted(Object executor) { /** * Record task execution start. + * + * @param executor the executor */ public void recordTaskStarted(Object executor) { if (!enabled) return; @@ -83,6 +93,9 @@ public void recordTaskStarted(Object executor) { /** * Record task completion. + * + * @param executor the executor + * @param durationMs the duration in milliseconds */ public void recordTaskCompleted(Object executor, long durationMs) { if (!enabled) return; @@ -98,6 +111,9 @@ public void recordTaskCompleted(Object executor, long durationMs) { /** * Record task rejection. + * + * @param executor the executor + * @param reason the reason */ public void recordTaskRejected(Object executor, String reason) { if (!enabled) return; @@ -113,6 +129,8 @@ public void recordTaskRejected(Object executor, String reason) { /** * Analyze pool health. + * + * @return the analyze pool health */ public ThreadPoolReport analyzePoolHealth() { ThreadPoolReport report = new ThreadPoolReport(); @@ -152,6 +170,8 @@ public ThreadPoolReport analyzePoolHealth() { /** * Standardized alias for {@link #analyzePoolHealth()}. + * + * @return the analyze */ public ThreadPoolReport analyze() { return analyzePoolHealth(); @@ -188,7 +208,9 @@ public static class ThreadPoolReport { /** The thread starvation. */ public final Set threadStarvation = new HashSet<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !poolsWithRejections.isEmpty() || !saturatedQueues.isEmpty() || !longRunningTasks.isEmpty() || !threadStarvation.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadStarvationDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadStarvationDetector.java index b7750c3e..4b6b55f4 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadStarvationDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadStarvationDetector.java @@ -234,6 +234,8 @@ public void disable() { /** * Set the starvation threshold in milliseconds. + * + * @param thresholdMs the threshold in milliseconds */ public void setStarvationThresholdMs(long thresholdMs) { this.starvationThresholdMs = thresholdMs; @@ -278,12 +280,16 @@ public static class ThreadStarvationReport { this.maxWaitTimeMs = maxWaitTimeMs; } - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !events.isEmpty(); } - /** {@return the events} */ + /** + * {@return the events} + */ public List getEvents() { return List.copyOf(events); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/TimerDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/TimerDetector.java index bdb89580..e6d0d801 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/TimerDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/TimerDetector.java @@ -161,6 +161,8 @@ private TimerState resolve(java.util.Timer timer, String name) { /** * Analyse Timer usage and return a report. + * + * @return the analyze */ public TimerReport analyze() { TimerReport report = new TimerReport(); @@ -216,7 +218,11 @@ public static class TimerReport { final java.util.List usageWarnings = new java.util.ArrayList<>(); final Map timerActivity = new ConcurrentHashMap<>(); - /** Returns {@code true} when timer thread failures or long-running tasks were detected. */ + /** + * Returns {@code true} when timer thread failures or long-running tasks were detected. + * + * @return the has issues + */ public boolean hasIssues() { return !timerThreadFailures.isEmpty() || !longRunningTaskWarnings.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/TryLockMisuseDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/TryLockMisuseDetector.java index 7df20e41..b6bc565d 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/TryLockMisuseDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/TryLockMisuseDetector.java @@ -37,6 +37,11 @@ private static final class State { /** * Record the result of a tryLock() call. + * + * @param lock the lock + * @param lockName the lock name + * @param acquired the {@code acquired} flag + * @param thread the thread */ public void recordTryLockResult(Object lock, String lockName, boolean acquired, Thread thread) { if (lock == null || thread == null) return; @@ -46,6 +51,10 @@ public void recordTryLockResult(Object lock, String lockName, boolean acquired, /** * Record an unlock() call. + * + * @param lock the lock + * @param lockName the lock name + * @param thread the thread */ public void recordUnlock(Object lock, String lockName, Thread thread) { if (lock == null || thread == null) return; @@ -97,7 +106,9 @@ public static final class Report { /** The structured violations. */ public final List structuredViolations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/UnboundedQueueDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/UnboundedQueueDetector.java index 9d8cd2d6..388ec533 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/UnboundedQueueDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/UnboundedQueueDetector.java @@ -205,6 +205,8 @@ public void disable() { /** * Set the warning threshold for queue size. + * + * @param threshold the threshold */ public void setWarningThreshold(int threshold) { this.warningThreshold = threshold; @@ -249,12 +251,16 @@ public static class UnboundedQueueReport { this.totalTracked = totalTracked; } - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !events.isEmpty(); } - /** {@return the events} */ + /** + * {@return the events} + */ public List getEvents() { return List.copyOf(events); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/UncaughtExceptionHandlerDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/UncaughtExceptionHandlerDetector.java index f63c32e8..4867caf5 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/UncaughtExceptionHandlerDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/UncaughtExceptionHandlerDetector.java @@ -71,7 +71,9 @@ public void recordUncaughtException(Thread thread, Throwable throwable) { if (rec != null) rec.uncaughtException = throwable; } - /** {@return report of threads that threw without a custom UncaughtExceptionHandler} */ + /** + * {@return report of threads that threw without a custom UncaughtExceptionHandler} + */ public UncaughtExceptionHandlerReport analyze() { UncaughtExceptionHandlerReport r = new UncaughtExceptionHandlerReport(); for (ThreadRecord rec : threads.values()) { @@ -89,7 +91,9 @@ public UncaughtExceptionHandlerReport analyze() { public static class UncaughtExceptionHandlerReport { final List violations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/UncommittedChangesDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/UncommittedChangesDetector.java index ef6c9db9..adf87d22 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/UncommittedChangesDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/UncommittedChangesDetector.java @@ -154,6 +154,8 @@ private UncommittedChangesReport(GitStatus status) { /** * Check if any untracked or uncommitted changes were detected. + * + * @return the has issues */ public boolean hasIssues() { return !uncommittedFiles.isEmpty() || !untrackedFiles.isEmpty() || error != null; diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadCarrierExhaustionDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadCarrierExhaustionDetector.java index f0363d4a..267c8842 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadCarrierExhaustionDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadCarrierExhaustionDetector.java @@ -174,18 +174,28 @@ public static class CarrierExhaustionReport { this.carrierCount = carrierCount; } - /** {@return true if carrier exhaustion was reached or approached} */ + /** + * {@return true if carrier exhaustion was reached or approached} + */ public boolean hasIssues() { return exhaustionEventCount > 0; } - /** {@return the exhaustion details} */ + /** + * {@return the exhaustion details} + */ public List getExhaustionDetails() { return Collections.unmodifiableList(exhaustionDetails); } - /** {@return the peak concurrently blocked} */ + /** + * {@return the peak concurrently blocked} + */ public int getPeakConcurrentlyBlocked() { return peakConcurrentlyBlocked; } - /** {@return the exhaustion event count} */ + /** + * {@return the exhaustion event count} + */ public int getExhaustionEventCount() { return exhaustionEventCount; } - /** {@return the carrier count} */ + /** + * {@return the carrier count} + */ public int getCarrierCount() { return carrierCount; } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadContextLeakDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadContextLeakDetector.java index cac16366..5f649ce2 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadContextLeakDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadContextLeakDetector.java @@ -211,20 +211,32 @@ public static class VirtualThreadContextLeakReport { this.totalRemoves = totalRemoves; } - /** {@return true if any context leak issues were detected} */ + /** + * {@return true if any context leak issues were detected} + */ public boolean hasIssues() { return !leaks.isEmpty() || !inheritableInVirtualIssues.isEmpty(); } - /** {@return the leaks} */ + /** + * {@return the leaks} + */ public List getLeaks() { return Collections.unmodifiableList(leaks); } - /** {@return the inheritable in virtual issues} */ + /** + * {@return the inheritable in virtual issues} + */ public List getInheritableInVirtualIssues() { return Collections.unmodifiableList(inheritableInVirtualIssues); } - /** {@return the high count warnings} */ + /** + * {@return the high count warnings} + */ public List getHighCountWarnings() { return Collections.unmodifiableList(highCountWarnings); } - /** {@return the total sets} */ + /** + * {@return the total sets} + */ public int getTotalSets() { return totalSets; } - /** {@return the total removes} */ + /** + * {@return the total removes} + */ public int getTotalRemoves() { return totalRemoves; } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadCpuBoundTaskDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadCpuBoundTaskDetector.java index 13e67247..906f7580 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadCpuBoundTaskDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadCpuBoundTaskDetector.java @@ -217,20 +217,32 @@ public static class CpuBoundTaskReport { this.thresholdMs = thresholdMs; } - /** {@return true if any CPU-bound tasks were detected on virtual threads} */ + /** + * {@return true if any CPU-bound tasks were detected on virtual threads} + */ public boolean hasIssues() { return !violations.isEmpty(); } - /** {@return the violations} */ + /** + * {@return the violations} + */ public List getViolations() { return Collections.unmodifiableList(violations); } - /** {@return the total tasks} */ + /** + * {@return the total tasks} + */ public int getTotalTasks() { return totalTasks; } - /** {@return the average duration in milliseconds} */ + /** + * {@return the average duration in milliseconds} + */ public long getAverageDurationMs() { return averageDurationMs; } - /** {@return the max duration in milliseconds} */ + /** + * {@return the max duration in milliseconds} + */ public long getMaxDurationMs() { return maxDurationMs; } - /** {@return the threshold in milliseconds} */ + /** + * {@return the threshold in milliseconds} + */ public long getThresholdMs() { return thresholdMs; } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadPinningDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadPinningDetector.java index d9cb3790..20e7361d 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadPinningDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadPinningDetector.java @@ -80,6 +80,9 @@ public enum PinningCause { * Classifies a caller-supplied blocking-operation description. * * @since 1.7.0 + * + * @param blockingOperation the blocking operation + * @return the classify operation */ public static PinningCause classifyOperation(String blockingOperation) { if (blockingOperation == null) return PinningCause.OTHER; @@ -104,6 +107,10 @@ public static PinningCause classifyOperation(String blockingOperation) { * version (e.g. {@code 21}, {@code 24}, {@code 26}). * * @since 1.7.0 + * + * @param cause the cause + * @param jdkFeatureVersion the jdk feature version + * @return the still pins on */ public static boolean stillPinsOn(PinningCause cause, int jdkFeatureVersion) { return switch (cause) { @@ -355,6 +362,8 @@ public boolean hasEffectivePinningIssues() { * Returns how many recorded events no longer pin on the running JDK. * * @since 1.7.0 + * + * @return the get obsolete event count */ public long getObsoleteEventCount() { return events.stream().filter(PinningEventSnapshot::isObsoleteOnCurrentJdk).count(); @@ -447,6 +456,8 @@ public static class PinningEventSnapshot { * Returns the classified cause of this pinning event. * * @since 1.7.0 + * + * @return the get cause */ public PinningCause getCause() { return cause; @@ -458,6 +469,8 @@ public PinningCause getCause() { * waits on JDK 26+). * * @since 1.7.0 + * + * @return the is obsolete on current jdk */ public boolean isObsoleteOnCurrentJdk() { return obsoleteOnCurrentJdk; diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadStressConfig.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadStressConfig.java index 58867028..9e6aeb14 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadStressConfig.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadStressConfig.java @@ -47,33 +47,45 @@ public VirtualThreadStressConfig(StressLevel stressLevel, this.enableVirtualThreadEvents = enableVirtualThreadEvents; this.timeoutMs = timeoutMs; } - /** {@return the builder} */ + /** + * {@return the builder} + */ public static Builder builder() { return new Builder(); } - /** {@return the stress level} */ + /** + * {@return the stress level} + */ public StressLevel getStressLevel() { return stressLevel; } - /** {@return the thread count} */ + /** + * {@return the thread count} + */ public int getThreadCount() { return stressLevel.threadCount; } - /** {@return whether detect thread pinning} */ + /** + * {@return whether detect thread pinning} + */ public boolean isDetectThreadPinning() { return detectThreadPinning; } - /** {@return whether enable virtual thread events} */ + /** + * {@return whether enable virtual thread events} + */ public boolean isEnableVirtualThreadEvents() { return enableVirtualThreadEvents; } - /** {@return the timeout in milliseconds} */ + /** + * {@return the timeout in milliseconds} + */ public long getTimeoutMs() { return timeoutMs; } @@ -127,7 +139,9 @@ public Builder timeoutMs(long timeout) { this.timeoutMs = timeout; return this; } - /** {@return the build} */ + /** + * {@return the build} + */ public VirtualThreadStressConfig build() { return new VirtualThreadStressConfig(stressLevel, detectThreadPinning, @@ -137,6 +151,8 @@ public VirtualThreadStressConfig build() { /** * Helper to check if this JVM supports virtual threads (Java 21+). + * + * @return the is virtual thread supported */ public static boolean isVirtualThreadSupported() { try { @@ -150,6 +166,8 @@ public static boolean isVirtualThreadSupported() { /** * Utility to create a virtual thread executor with potential pinning detection. * Returns executor class name if virtual threads are available. + * + * @return the get virtual thread executor class */ public static String getVirtualThreadExecutorClass() { if (isVirtualThreadSupported()) { diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VisibilityMonitor.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VisibilityMonitor.java index 390f02ce..9b5cd86c 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VisibilityMonitor.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VisibilityMonitor.java @@ -38,6 +38,9 @@ private static final class FieldSnapshot { /** * Record a field access. Call this from test code to track when a field is read/written. * Format: className.fieldName + * + * @param fieldIdentifier the field identifier + * @param value the value */ public void recordFieldAccess(String fieldIdentifier, Object value) { if (!enabled) return; @@ -64,6 +67,8 @@ public void markInvocationStart() { /** * Analyze visibility issues. Returns a report of suspected visibility issues. + * + * @return the analyze visibility */ public VisibilityReport analyzeVisibility() { VisibilityReport report = new VisibilityReport(); @@ -95,6 +100,8 @@ public VisibilityReport analyzeVisibility() { /** * Standardized alias for {@link #analyzeVisibility()}. + * + * @return the analyze */ public VisibilityReport analyze() { return analyzeVisibility(); @@ -129,7 +136,9 @@ public static class VisibilityReport { /** The field value variations. */ public final Map>> fieldValueVariations = new HashMap<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !suspectedFields.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VolatileArrayDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VolatileArrayDetector.java index d4e39bc4..624b605f 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VolatileArrayDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VolatileArrayDetector.java @@ -28,6 +28,10 @@ public class VolatileArrayDetector { /** * Register a volatile array for monitoring. + * + * @param array the array + * @param name the name + * @param componentType the component type */ public void registerArray(Object array, String name, Class componentType) { ArrayInfo info = new ArrayInfo(name, array, componentType); @@ -36,6 +40,10 @@ public void registerArray(Object array, String name, Class componentType) { /** * Record a write to an array element. + * + * @param array the array + * @param index the index + * @param arrayName the array name */ public void recordElementWrite(Object array, int index, String arrayName) { ArrayInfo info = findArrayInfo(array, arrayName); @@ -61,6 +69,10 @@ public void recordElementWrite(Object array, int index, String arrayName) { /** * Record a read from an array element. + * + * @param array the array + * @param index the index + * @param arrayName the array name */ public void recordElementRead(Object array, int index, String arrayName) { ArrayInfo info = findArrayInfo(array, arrayName); @@ -84,6 +96,8 @@ public void recordElementRead(Object array, int index, String arrayName) { /** * Analyze array access patterns and return report. + * + * @return the analyze */ public VolatileArrayReport analyze() { return new VolatileArrayReport( @@ -103,7 +117,9 @@ public VolatileArrayReport( this.problematicArrays = Collections.unmodifiableSet(new HashSet<>(problematicArrays)); } - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !problematicArrays.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WaitTimeoutDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WaitTimeoutDetector.java index e54b83ab..754fb985 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WaitTimeoutDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WaitTimeoutDetector.java @@ -34,6 +34,10 @@ public class WaitTimeoutDetector { /** * Register a wait() call without timeout. + * + * @param monitor the monitor + * @param monitorName the monitor name + * @param threadName the thread name */ public void recordInfiniteWait(Object monitor, String monitorName, String threadName) { WaitInfo info = new WaitInfo(monitor, monitorName); @@ -44,6 +48,11 @@ public void recordInfiniteWait(Object monitor, String monitorName, String thread /** * Register a wait() call with timeout. + * + * @param monitor the monitor + * @param monitorName the monitor name + * @param threadName the thread name + * @param timeoutMs the timeout in milliseconds */ public void recordTimedWait(Object monitor, String monitorName, String threadName, long timeoutMs) { WaitInfo info = new WaitInfo(monitor, monitorName); @@ -53,6 +62,9 @@ public void recordTimedWait(Object monitor, String monitorName, String threadNam /** * Record a notify() call. + * + * @param monitor the monitor + * @param monitorName the monitor name */ public void recordNotify(Object monitor, String monitorName) { WaitInfo info = new WaitInfo(monitor, monitorName); @@ -62,6 +74,9 @@ public void recordNotify(Object monitor, String monitorName) { /** * Record a notifyAll() call. + * + * @param monitor the monitor + * @param monitorName the monitor name */ public void recordNotifyAll(Object monitor, String monitorName) { WaitInfo info = new WaitInfo(monitor, monitorName); @@ -71,6 +86,8 @@ public void recordNotifyAll(Object monitor, String monitorName) { /** * Analyze wait patterns and return report. + * + * @return the analyze */ public WaitTimeoutReport analyze() { return new WaitTimeoutReport(waitEvents, infiniteWaits); @@ -91,7 +108,9 @@ public WaitTimeoutReport( this.infiniteWaits = Collections.unmodifiableSet(new HashSet<>(infiniteWaits)); } - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !infiniteWaits.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WakeupDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WakeupDetector.java index 3171b354..00eefecb 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WakeupDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WakeupDetector.java @@ -38,6 +38,8 @@ private static class MonitorState { /** * Record that a thread is about to wait on a monitor. + * + * @param monitor the monitor */ public void recordWaitEnter(Object monitor) { if (!enabled) return; @@ -56,6 +58,9 @@ public void recordWaitEnter(Object monitor) { /** * Record that a thread has exited wait (either notified or spurious). + * + * @param monitor the monitor + * @param wasNotified the {@code wasNotified} flag */ public void recordWaitExit(Object monitor, boolean wasNotified) { if (!enabled) return; @@ -80,6 +85,9 @@ public void recordWaitExit(Object monitor, boolean wasNotified) { /** * Record a notify call on a monitor. + * + * @param monitor the monitor + * @param notifyAll the {@code notifyAll} flag */ public void recordNotify(Object monitor, boolean notifyAll) { if (!enabled) return; @@ -106,6 +114,8 @@ public void recordNotify(Object monitor, boolean notifyAll) { /** * Analyze wakeup patterns for issues. + * + * @return the analyze wakeups */ public WakeupReport analyzeWakeups() { WakeupReport report = new WakeupReport(); @@ -139,6 +149,8 @@ public WakeupReport analyzeWakeups() { /** * Standardized alias for {@link #analyzeWakeups()}. + * + * @return the analyze */ public WakeupReport analyze() { return analyzeWakeups(); @@ -173,7 +185,9 @@ public static class WakeupReport { /** The always notify without wait. */ public final Set alwaysNotifyWithoutWait = new HashSet<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !monitorsWithSpuriousWakeups.isEmpty() || !monitorsWithLostNotifications.isEmpty(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WeakHashMapSharedDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WeakHashMapSharedDetector.java index fd7f78f5..3a9b4d1f 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WeakHashMapSharedDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WeakHashMapSharedDetector.java @@ -133,7 +133,9 @@ public static final class Report { /** The structured violations. */ public final List structuredViolations = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WeakReferenceRaceDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WeakReferenceRaceDetector.java index 7c61754f..677dd180 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WeakReferenceRaceDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WeakReferenceRaceDetector.java @@ -93,7 +93,9 @@ public void recordNullDereference(Object ref, String name, Thread thread) { s.nullDerefs.add(thread.getName()); } - /** {@return report of weak-reference race and null-dereference issues} */ + /** + * {@return report of weak-reference race and null-dereference issues} + */ public WeakReferenceRaceReport analyze() { WeakReferenceRaceReport r = new WeakReferenceRaceReport(); for (RefState s : refs.values()) { @@ -119,7 +121,9 @@ public static class WeakReferenceRaceReport { final List violations = new ArrayList<>(); final List warnings = new ArrayList<>(); - /** {@return whether there are issues} */ + /** + * {@return whether there are issues} + */ public boolean hasIssues() { return !violations.isEmpty() || !warnings.isEmpty(); } @Override diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/report/Baseline.java b/async-test-lib/src/main/java/se/deversity/asynctest/report/Baseline.java index 4a452ad8..fbfde708 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/report/Baseline.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/report/Baseline.java @@ -67,6 +67,8 @@ private Baseline(Set entries) { * Resolves the active baseline from {@value #PATH_PROPERTY}; returns an empty * baseline when the property is unset. A missing file is treated as empty in * update mode (it will be created) and logged once otherwise. + * + * @return the from system properties */ public static Baseline fromSystemProperties() { String prop = System.getProperty(PATH_PROPERTY); @@ -76,12 +78,21 @@ public static Baseline fromSystemProperties() { return load(Path.of(prop)); } - /** Whether {@value #UPDATE_PROPERTY} is set, switching the gate to record mode. */ + /** + * Whether {@value #UPDATE_PROPERTY} is set, switching the gate to record mode. + * + * @return the update mode + */ public static boolean updateMode() { return Boolean.getBoolean(UPDATE_PROPERTY); } - /** Loads (with caching by last-modified time) the baseline at {@code path}. */ + /** + * Loads (with caching by last-modified time) the baseline at {@code path}. + * + * @param path the path + * @return the load + */ public static Baseline load(Path path) { if (!Files.exists(path)) { if (!updateMode()) { @@ -111,12 +122,22 @@ public static Baseline load(Path path) { } } - /** Returns {@code true} when the (test, detector) finding is baselined. */ + /** + * Returns {@code true} when the (test, detector) finding is baselined. + * + * @param testId the test id + * @param detectorName the detector name + * @return the contains + */ public boolean contains(String testId, String detectorName) { return entries.contains(key(testId, detectorName)); } - /** Number of entries in this baseline. */ + /** + * Number of entries in this baseline. + * + * @return the size + */ public int size() { return entries.size(); } @@ -127,6 +148,9 @@ public int size() { * present. Thread-safe across concurrently-running tests in the same JVM. * * @return the number of entries actually added + * + * @param testId the test id + * @param detectorNames the detector names */ public static int record(String testId, Collection detectorNames) { String prop = System.getProperty(PATH_PROPERTY); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/report/JUnitXmlReportListener.java b/async-test-lib/src/main/java/se/deversity/asynctest/report/JUnitXmlReportListener.java index 2803d60f..62aa21e6 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/report/JUnitXmlReportListener.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/report/JUnitXmlReportListener.java @@ -119,6 +119,8 @@ public void onStructuredReport(String detectorName, IssueSeverity severity, /** * Returns the number of accumulated findings (useful for assertions in tests of this listener). + * + * @return the get finding count */ public int getFindingCount() { return findings.size(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/report/JsonReportListener.java b/async-test-lib/src/main/java/se/deversity/asynctest/report/JsonReportListener.java index 9d0c5fe0..71d50613 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/report/JsonReportListener.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/report/JsonReportListener.java @@ -126,6 +126,8 @@ public void onStructuredReport(String detectorName, IssueSeverity severity, /** * Returns the number of accumulated findings. + * + * @return the get finding count */ public int getFindingCount() { return findings.size(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/runner/ConcurrencyRunner.java b/async-test-lib/src/main/java/se/deversity/asynctest/runner/ConcurrencyRunner.java index 7700a817..e75a7e71 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/runner/ConcurrencyRunner.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/runner/ConcurrencyRunner.java @@ -85,6 +85,9 @@ public class ConcurrencyRunner { * transitively, since both are derived from the round timeout — the {@code CyclicBarrier} * await and the async-body {@code CompletionStage} wait. See * {@link #resolveTimeoutMultiplier()} for the CI-scaling mechanism itself. + * + * @param invocationContext the invocation context + * @param config the config */ @AILoadBearing( invariant = "The timeoutAlreadyReported flag, and the per-step guarded cleanup in the " diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/runner/LicenseGuard.java b/async-test-lib/src/main/java/se/deversity/asynctest/runner/LicenseGuard.java index 2fe77214..c2da3218 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/runner/LicenseGuard.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/runner/LicenseGuard.java @@ -47,6 +47,8 @@ private LicenseGuard() {} /** * Validates the license for the given config. Throws {@link SecurityException} * if denied. Subsequent calls with the same fingerprint return immediately. + * + * @param config the config */ @AIIdempotent(reason = "ConcurrentHashMap.computeIfAbsent guarantees the underlying gate.check fires at most once per Fingerprint; repeat calls return immediately. Denied results consistently throw SecurityException for the same fingerprint.") public static void check(AsyncTestConfig config) { diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/spi/DetectorRegistry.java b/async-test-lib/src/main/java/se/deversity/asynctest/spi/DetectorRegistry.java index 5c1c96a7..f901525f 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/spi/DetectorRegistry.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/spi/DetectorRegistry.java @@ -70,6 +70,9 @@ private DetectorRegistry(Map detectors) { *

This is the addressability view, used to prove every {@link DetectorType} is reachable * through the SPI. It is not the path the runner takes: see * {@link #buildExternal(AsyncTestConfig)}. + * + * @param config the config + * @return the build */ public static DetectorRegistry build(AsyncTestConfig config) { Map detectors = new EnumMap<>(DetectorType.class); @@ -99,6 +102,9 @@ public static DetectorRegistry build(AsyncTestConfig config) { * #build(AsyncTestConfig)} reads, so runtime discovery sees only genuine third-party providers. * * @since 1.7.0 + * + * @param config the config + * @return the build external */ public static DetectorRegistry buildExternal(AsyncTestConfig config) { Map detectors = new EnumMap<>(DetectorType.class); @@ -175,7 +181,9 @@ private static DetectorFactory instantiate(String className) { } } - /** {@return {@code true} when no detector is active in this registry} */ + /** + * {@return {@code true} when no detector is active in this registry} + */ public boolean isEmpty() { return byType.isEmpty(); } @@ -195,17 +203,30 @@ public boolean isEmpty() { return null; } - /** Type-keyed lookup. */ + /** + * Type-keyed lookup. + * + * @param type the type + * @return the get + */ public @Nullable Detector get(DetectorType type) { return byType.get(type); } - /** All active detectors (snapshot). */ + /** + * All active detectors (snapshot). + * + * @return the all + */ public List all() { return new ArrayList<>(byType.values()); } - /** Aggregated violations from every active detector for the current round. */ + /** + * Aggregated violations from every active detector for the current round. + * + * @return the analyze all + */ @AIIdempotent(reason = "Each Detector.analyze() must return the same violations for the same observed state (the SPI contract). Calling analyzeAll() N times on a quiescent registry yields N identical lists; do not introduce stateful side-effects in analyze().") public List analyzeAll() { List out = new ArrayList<>(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/spi/adapters/LegacyDetectorAdapter.java b/async-test-lib/src/main/java/se/deversity/asynctest/spi/adapters/LegacyDetectorAdapter.java index 9a69e9d1..bcedde7a 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/spi/adapters/LegacyDetectorAdapter.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/spi/adapters/LegacyDetectorAdapter.java @@ -100,7 +100,11 @@ public List analyze() { } } - /** Exposed for callers that need direct access to the wrapped legacy detector. */ + /** + * Exposed for callers that need direct access to the wrapped legacy detector. + * + * @return the delegate + */ public D delegate() { return delegate; } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/spi/adapters/SharedMessageDigestDetectorFactory.java b/async-test-lib/src/main/java/se/deversity/asynctest/spi/adapters/SharedMessageDigestDetectorFactory.java index 588da597..60e6a446 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/spi/adapters/SharedMessageDigestDetectorFactory.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/spi/adapters/SharedMessageDigestDetectorFactory.java @@ -59,7 +59,11 @@ public List analyze() { return List.copyOf(delegate.analyze().structuredViolations); } - /** Exposed for legacy users that need direct access to the wrapped detector. */ + /** + * Exposed for legacy users that need direct access to the wrapped detector. + * + * @return the delegate + */ public SharedMessageDigestDetector delegate() { return delegate; } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/telemetry/TelemetryEventBuffer.java b/async-test-lib/src/main/java/se/deversity/asynctest/telemetry/TelemetryEventBuffer.java index 02728ce5..4db2786c 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/telemetry/TelemetryEventBuffer.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/telemetry/TelemetryEventBuffer.java @@ -154,7 +154,11 @@ public int drain(DrainCallback callback) { return count; } - /** Returns the number of events published so far (monotonically increasing). */ + /** + * Returns the number of events published so far (monotonically increasing). + * + * @return the published count + */ public long publishedCount() { return producerCursor.get() + 1; } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/telemetry/TelemetryRegistry.java b/async-test-lib/src/main/java/se/deversity/asynctest/telemetry/TelemetryRegistry.java index 917fc3e3..059fa032 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/telemetry/TelemetryRegistry.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/telemetry/TelemetryRegistry.java @@ -111,7 +111,9 @@ public static void start(TelemetryEventBuffer.@Nullable DrainCallback callback) Runtime.getRuntime().addShutdownHook(shutdownHook); } - /** Starts the registry with a no-op drain callback (events counted but not forwarded). */ + /** + * Starts the registry with a no-op drain callback (events counted but not forwarded). + */ public static void start() { start(null); } @@ -224,7 +226,11 @@ public static void stop() { drainOnce(); // final flush } - /** Exposes the shared buffer for testing and advanced consumers. */ + /** + * Exposes the shared buffer for testing and advanced consumers. + * + * @return the buffer + */ public static TelemetryEventBuffer buffer() { return BUFFER; } diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 39ac66ed..db1351d0 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -7,6 +7,52 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added — DetectorType's 127 constants documented, and its lock text corrected + +The enum a user types into `@AsyncTest(excludes = ...)` had no documentation on any of its 127 +constants, so the published javadoc listed 127 bare names. Each now carries the first sentence of +the detector it selects, taken from that detector's own class javadoc rather than invented, so +`DEADLOCKS` reads "Enhanced deadlock detector that analyzes thread dumps and identifies circular +lock dependencies..." The mapping is derived, not hand-maintained: `AsyncTestConfig.build()` gives +constant to flag, `DetectorRegistry`'s constructor gives flag to detector class, and all 127 resolve +with none left over. + +The file is `@AILocked`, and the lock was waived for this deliberately. Its own reason says a +constant needs synchronized edits in five places; a comment adds no constant and cannot break that. +The annotation now says so, so the next reader does not have to ask: the lock is on the constant +set, not on the file. + +Two pieces of that guardrail had also gone stale and are corrected in the same change. It still +described "both branches of `AsyncTestConfig.build()` (detectAll block + excludes block)", which has +been a single expression per detector for some time, and `@AIKeepInSync` still listed +`META-INF/services/…DetectorFactory` as the file that must agree, which stopped being true when the +built-in factories moved to `META-INF/async-test/builtin-detector-factories`. Both feed the +generated `CLAUDE.md`, so a stale guardrail misdirects every future contributor. + +### Added — 295 `@param` and 122 `@return` tags on already-documented members + +Tags were appended to existing blocks rather than blocks being rebuilt. That distinction is the +whole change: an earlier attempt rebuilt each block and replaced real prose with a generated stub, +turning `ConcurrencyRunner.execute`'s detailed javadoc into "Execute.". Appending cannot lose text. +287 single-line comments were expanded to multi-line first, as a separate pass, so no insertion had +to reason about indices that another insertion had already moved. + +### Documented — what happens on a first run with no licence key + +`LicenseGuard` denies a developer who has no key and has not set `-Dlicense.mock.mode=true`; mock +mode turns itself on only in CI. That is intended behaviour for a PolyForm Noncommercial library and +is left alone, but the README described it as "outcome depends on the configured backend", which +does not prepare anyone for a `SecurityException` before a single test body runs. + +The README now shows the actual error and says plainly that CI is silently mocked while a laptop is +not, which is why the same suite can pass in CI and stop locally. `TROUBLESHOOTING.md` gains a +section with the fix for Maven, Gradle and the IDE, and with the reason the gate is loud rather than +silently degrading: a run that was not licensed must never look like a run that found no bugs. + +Not changed: the 51 default-constructor javadoc warnings. Clearing them means adding 51 public +constructors to satisfy a style rule, which widens the documented API surface for no functional +gain, so the warnings stay. + ### Fixed — javadoc reaching consumers was missing ~260 tags, and the build was configured not to notice `maven-javadoc-plugin` ran with `all,-missing`: every check except `missing`. So diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md index cd41edb9..37e13703 100644 --- a/docs/TROUBLESHOOTING.md +++ b/docs/TROUBLESHOOTING.md @@ -59,3 +59,49 @@ Default timeouts (5,000ms) that work locally can easily trigger timeouts in reso * **Tuning Guide**: * **Local / High-Spec CI**: Use `virtualThreadStressMode = "MEDIUM"` (spawns 500 virtual threads). * **Low-Spec CI / Containerized Runs**: Use `virtualThreadStressMode = "LOW"` (spawns 100 virtual threads) or `OFF`. + +--- + +## 6. `SecurityException: LICENSE DENIED` before any test runs + +### Symptom + +A run stops immediately, before a single test body executes: + +``` +java.lang.SecurityException: LICENSE DENIED: + To run locally without a key: -Dlicense.mock.mode=true + In CI (GITHUB_ACTIONS or CI env var set, no key): mock mode activates automatically. +``` + +### Cause + +`LicenseGuard` runs once per configuration at the start of `ConcurrencyRunner.execute`, before the +`CyclicBarrier` is built. It is not reacting to anything your test did; it decided before the test +started. + +Mock mode, which bypasses the check, turns itself on in exactly two situations: + +* `-Dlicense.mock.mode=true` is set, or +* the run looks like CI (`GITHUB_ACTIONS` or `CI` is set in the environment) **and** no key is + configured. + +A developer machine with no key matches neither, so the gate consults the backend and can refuse. +This is why the same suite passes in CI and stops locally: CI is silently mocked, your laptop is not. + +### Fix + +For local development, set the flag once rather than per run: + +* **Maven**: `mvn test -Dlicense.mock.mode=true`, or add it to `.mvn/jvm.config`. +* **Gradle**: `systemProperty("license.mock.mode", "true")` in your `test { }` block. +* **IDE**: add `-Dlicense.mock.mode=true` to the default JUnit run configuration, so every new test + you create inherits it. + +With a real key, pass `-Dlicense.key=` and set `-Dlicense.user.email=you@example.com`. + +### Why it is not simply off by default + +The library is [PolyForm Noncommercial](../LICENSE); the gate is the mechanism behind that, not an +accident. It is deliberately loud rather than silently degrading, so that a run which was not +licensed never looks like a run that found no bugs. From 1c39a0aea5581bad96c7c57e653bf2b12397038d Mon Sep 17 00:00:00 2001 From: Peter Isberg Date: Mon, 3 Aug 2026 20:17:50 +0200 Subject: [PATCH 2/2] docs(javadoc): make 733 published descriptions say something, and gate it Closing every doclint warning did not make the javadoc useful. Doclint answers one question, is the tag present, so a mechanical pass that adds a tag per parameter turns a red build green without a reader learning anything. What that pass had left, all of it on public members and published to consumers: 432 @param tags, 166 @return tags and 135 one-line summaries that restated the identifier and nothing else. `@param lockName the lock name`. `@return the size` on `size()`. The worst read `/** The totcou races. */` over a public field recording time-of-check-to-time-of-use races, the generated prose having respelled a misspelled acronym into a non-word. All 733 now carry a fact the signature does not: the unit on sleepDuration, the null rule on SiteCapture.capture(), that detectors track their subjects by identity rather than equality, that distanceInBytes under a cache line is what makes two fields share one. Two defects surfaced that doclint cannot see, because it checks nothing below protected by default. SpinContentionBarrier's constructor javadoc had been placed between two cache-line padding fields, so it documented pad7 while the public constructor had none. And 183 stray blank lines sat between a javadoc block and its member. totcouRaces keeps its misspelled name: it is public API and renaming it breaks binary compatibility against the 1.6.0 baseline. The javadoc now states what it records and that the name is kept deliberately. JavadocDescribesRatherThanRestatesTest pins this, asking whether a description is anything more than its identifier respelled once a leading article is ignored. Verified in both directions: it failed on the real tree before the fix, naming two sites the sweep's acronym handling had missed, and reintroducing one placeholder afterwards turned it red again with the exact file and line. It also asserts it scanned more than 100 files, so it cannot pass by scanning nothing. Verified with `mvn verify` on all four modules: BUILD SUCCESS, including PMD, SpotBugs, Checkstyle, japicmp against 1.6.0, and javadoc:jar with doclint=all. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01AENSLJhmSuFzLhXDv4QKUA --- .../se/deversity/asynctest/AsyncAssert.java | 6 + .../deversity/asynctest/AsyncTestConfig.java | 14 +- .../deversity/asynctest/AsyncTestContext.java | 10 +- .../asynctest/AsyncTestListenerRegistry.java | 2 +- .../java/se/deversity/asynctest/Preset.java | 4 +- .../benchmark/BenchmarkComparator.java | 6 +- .../benchmark/BenchmarkComparisonResult.java | 43 ++-- .../benchmark/BenchmarkRecorder.java | 16 +- .../BenchmarkRegressionException.java | 7 +- .../asynctest/benchmark/BenchmarkResult.java | 38 ++- .../diagnostics/ABAProblemDetector.java | 25 +- .../AtomicNonAtomicUpdateDetector.java | 18 +- .../diagnostics/AtomicityValidator.java | 38 ++- .../asynctest/diagnostics/AutoFix.java | 10 +- .../diagnostics/BlockingQueueDetector.java | 10 +- .../diagnostics/BusyWaitDetector.java | 21 +- .../diagnostics/CacheConcurrencyDetector.java | 2 +- .../diagnostics/CalendarDetector.java | 4 +- ...letableFutureBlockingCallbackDetector.java | 17 +- .../CompletableFutureChainDetector.java | 2 +- .../CompletableFutureExceptionDetector.java | 2 +- .../CompletableFutureObtrudeDetector.java | 13 +- ...ConcurrentMapComputeRecursionDetector.java | 6 +- .../ConcurrentModificationDetector.java | 2 +- .../ConditionVariableDetector.java | 8 +- .../ConstructorSafetyValidator.java | 21 +- .../CopyOnWriteCollectionDetector.java | 4 +- .../diagnostics/CountDownLatchDetector.java | 22 +- .../diagnostics/CyclicBarrierDetector.java | 33 ++- .../DaemonThreadHygieneDetector.java | 8 +- .../diagnostics/DeadlockDetector.java | 17 +- .../DoubleCheckedLockingDetector.java | 13 +- .../diagnostics/ExchangerDetector.java | 29 ++- .../diagnostics/ExecutorDeadlockDetector.java | 24 +- .../diagnostics/FalseSharingDetector.java | 37 +-- .../FileChannelPositionRaceDetector.java | 7 +- .../diagnostics/ForkJoinPoolDetector.java | 44 ++-- .../ForkJoinTaskBlockingDetector.java | 4 +- .../diagnostics/FutureBlockingDetector.java | 25 +- .../HighContentionAtomicDetector.java | 4 +- .../HttpClientConcurrencyDetector.java | 2 +- .../diagnostics/InterruptMonitor.java | 24 +- .../JdbcConnectionSharedDetector.java | 7 +- .../diagnostics/LatchMisuseDetector.java | 20 +- .../diagnostics/LazyInitRaceDetector.java | 4 +- .../diagnostics/LazyInitValidator.java | 19 +- .../diagnostics/LivelockDetector.java | 13 +- .../diagnostics/LockContentionDetector.java | 4 +- .../diagnostics/LockLeakDetector.java | 6 +- .../diagnostics/LockOrderValidator.java | 13 +- .../LockUpgradeDeadlockDetector.java | 23 +- .../diagnostics/MemoryModelValidator.java | 8 +- .../diagnostics/MemoryOrderingMonitor.java | 15 +- .../diagnostics/MissedSignalDetector.java | 4 +- .../NonAtomicConcurrentMapUpdateDetector.java | 7 +- .../diagnostics/NotifyAllValidator.java | 21 +- .../NotifyWithoutMonitorDetector.java | 6 +- .../OptimisticReadValidationDetector.java | 18 +- .../diagnostics/ParallelStreamDetector.java | 10 +- .../diagnostics/Phase1DetectorSet.java | 26 +-- .../asynctest/diagnostics/PhaserDetector.java | 28 ++- .../diagnostics/PipelineMonitor.java | 29 ++- .../diagnostics/RaceConditionDetector.java | 31 +-- .../diagnostics/ReadWriteLockMonitor.java | 29 ++- .../diagnostics/ReentrantLockDetector.java | 26 ++- .../diagnostics/ResourceLeakDetector.java | 6 +- .../ScheduledExecutorDetector.java | 41 ++-- .../diagnostics/SemaphoreMisuseDetector.java | 6 +- .../diagnostics/SharedByteBufferDetector.java | 7 +- .../SharedCharsetCoderDetector.java | 7 +- .../diagnostics/SharedChecksumDetector.java | 7 +- .../diagnostics/SharedCollectionDetector.java | 4 +- .../diagnostics/SharedDeflaterDetector.java | 7 +- .../diagnostics/SharedIteratorDetector.java | 7 +- .../SharedJsonMapperReconfigDetector.java | 7 +- .../diagnostics/SharedKdfDetector.java | 7 +- .../SharedMessageDigestDetector.java | 4 +- .../diagnostics/SharedRandomDetector.java | 2 +- .../SharedSecureRandomDetector.java | 7 +- .../SharedStatefulCryptoDetector.java | 7 +- .../diagnostics/SimpleDateFormatDetector.java | 2 +- .../asynctest/diagnostics/SiteCapture.java | 4 +- .../diagnostics/SleepInLockDetector.java | 11 +- .../diagnostics/SpuriousWakeupDetector.java | 13 +- .../diagnostics/StampedLockDetector.java | 47 ++-- .../diagnostics/StreamClosingDetector.java | 2 +- .../diagnostics/StringBuilderDetector.java | 4 +- .../StructuredTaskScopeMisuseDetector.java | 40 ++-- ...nchronizedCollectionIterationDetector.java | 4 +- .../SynchronizedNonFinalDetector.java | 4 +- .../diagnostics/SynchronizerMonitor.java | 21 +- .../diagnostics/ThisEscapeDetector.java | 7 +- .../diagnostics/ThreadFactoryDetector.java | 20 +- .../diagnostics/ThreadLeakDetector.java | 9 +- .../ThreadLocalContaminationDetector.java | 16 +- .../diagnostics/ThreadLocalMonitor.java | 25 +- .../ThreadLocalRandomMisuseDetector.java | 7 +- .../diagnostics/ThreadPoolMonitor.java | 35 ++- .../diagnostics/ThreadStarvationDetector.java | 13 +- .../asynctest/diagnostics/TimerDetector.java | 4 +- .../diagnostics/TryLockMisuseDetector.java | 19 +- .../diagnostics/UnboundedQueueDetector.java | 15 +- .../UncommittedChangesDetector.java | 2 +- ...irtualThreadCarrierExhaustionDetector.java | 4 +- .../VirtualThreadCpuBoundTaskDetector.java | 10 +- .../VirtualThreadPinningDetector.java | 16 +- .../VirtualThreadStressConfig.java | 35 +-- .../diagnostics/VisibilityMonitor.java | 15 +- .../diagnostics/VolatileArrayDetector.java | 26 ++- .../diagnostics/WaitTimeoutDetector.java | 29 ++- .../asynctest/diagnostics/WakeupDetector.java | 19 +- .../WeakHashMapSharedDetector.java | 7 +- .../AsyncTestInvocationInterceptor.java | 9 +- .../deversity/asynctest/report/Baseline.java | 20 +- .../asynctest/report/DetectorFinding.java | 15 +- .../deversity/asynctest/report/Formatter.java | 3 + .../report/JUnitXmlReportListener.java | 4 +- .../asynctest/report/JsonReportListener.java | 4 +- .../asynctest/runner/ConcurrencyRunner.java | 7 +- .../asynctest/runner/LicenseGuard.java | 2 +- .../runner/SpinContentionBarrier.java | 10 + .../se/deversity/asynctest/spi/Detector.java | 2 + .../asynctest/spi/DetectorFactory.java | 10 +- .../asynctest/spi/DetectorRegistry.java | 22 +- .../spi/adapters/LegacyDetectorAdapter.java | 10 +- .../SharedMessageDigestDetectorFactory.java | 8 +- .../telemetry/TelemetryEventBuffer.java | 4 +- .../telemetry/TelemetryRegistry.java | 2 +- ...avadocDescribesRatherThanRestatesTest.java | 221 ++++++++++++++++++ docs/CHANGELOG.md | 44 ++++ 130 files changed, 1163 insertions(+), 854 deletions(-) create mode 100644 async-test-lib/src/test/java/se/deversity/asynctest/architecture/JavadocDescribesRatherThanRestatesTest.java diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/AsyncAssert.java b/async-test-lib/src/main/java/se/deversity/asynctest/AsyncAssert.java index 34c2f47e..3e8e85a1 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/AsyncAssert.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/AsyncAssert.java @@ -133,18 +133,24 @@ public void awaitDone(Duration timeout) { } /** + * Get result. + * * @return the future's resolved value, or {@code null} if it has not completed * successfully yet (or completed exceptionally) */ public @Nullable T getResult() { return result.get(); } /** + * Get error. + * * @return the exception the future completed with, or {@code null} if it has not * completed exceptionally */ public @Nullable Throwable getError() { return error.get(); } /** + * Is complete. + * * @return {@code true} once the observed future has completed, successfully or not */ public boolean isComplete() { return complete; } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/AsyncTestConfig.java b/async-test-lib/src/main/java/se/deversity/asynctest/AsyncTestConfig.java index 40c0b1b7..45038791 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/AsyncTestConfig.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/AsyncTestConfig.java @@ -531,8 +531,8 @@ private AsyncTestConfig(Builder b) { /** * Builds a config from an {@link AsyncTest} annotation instance. * - * @param ann the ann - * @return the from + * @param ann the annotation instance to read the declared values from + * @return the resolved configuration for this run */ public static AsyncTestConfig from(AsyncTest ann) { return from(ann, ann.threads()); @@ -546,9 +546,9 @@ public static AsyncTestConfig from(AsyncTest ann) { * * @since 1.6.0 * - * @param ann the ann - * @param threadsOverride the threads override - * @return the from + * @param ann the annotation instance to read the declared values from + * @param threadsOverride thread count to use instead of {@link AsyncTest#threads()}, as supplied by a parameterised template + * @return the resolved configuration for this run */ public static AsyncTestConfig from(AsyncTest ann, int threadsOverride) { // Check for global benchmarking system property @@ -1780,8 +1780,8 @@ public Builder excludes(DetectorType[] v) { * * @since 1.7.0 * - * @param v the v - * @return the includes + * @param v the detectors to enable exclusively; {@code null} or empty leaves the selection untouched + * @return this builder */ public Builder includes(DetectorType[] v) { if (v != null && v.length > 0) { diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/AsyncTestContext.java b/async-test-lib/src/main/java/se/deversity/asynctest/AsyncTestContext.java index adb7e37a..c79a85b5 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/AsyncTestContext.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/AsyncTestContext.java @@ -353,7 +353,11 @@ public final class AsyncTestContext { // Exposed via atomicityValidator() so se.deversity.asynctest.telemetry.TelemetryBridge // can route drained agent field-access events into the live per-test detector. final @Nullable AtomicityValidator atomicityValidator; - + /** + * Creates a AsyncTestContext. + * + * @param cfg the resolved configuration deciding which detectors this context installs + */ public AsyncTestContext(AsyncTestConfig cfg) { this.registry = new DetectorRegistry(cfg); // Mirror registry references so package-private field access still works @@ -633,7 +637,7 @@ public AsyncTestContext(AsyncTestConfig cfg) { /** * Installs {@code ctx} into the calling thread's ThreadLocal. * - * @param ctx the ctx + * @param ctx the context to bind to the calling thread; must be paired with an {@code uninstall()} in a {@code finally} */ @AICallersOnly({"se.deversity.asynctest.runner.ConcurrencyRunner"}) public static void install(AsyncTestContext ctx) { @@ -691,7 +695,7 @@ public static long replaySeed() { /** * Internal: set by {@code ConcurrencyRunner} before each invocation round. * - * @param seed the seed + * @param seed the seed for this round, so a reported interleaving can be replayed */ public void setReplaySeedForRound(long seed) { this.currentRoundSeed = seed; diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/AsyncTestListenerRegistry.java b/async-test-lib/src/main/java/se/deversity/asynctest/AsyncTestListenerRegistry.java index 84d99342..b1f5526f 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/AsyncTestListenerRegistry.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/AsyncTestListenerRegistry.java @@ -146,7 +146,7 @@ public static void fireTestFailed(Throwable cause) { *

Severity is parsed from the report text using {@link IssueSeverity} markers * (emoji or keyword). Reports with no recognisable marker default to {@link IssueSeverity#HIGH}. * - * @param detectorName the detector name + * @param detectorName the reporting detector, as it appears in the report * @param report the report content */ public static void fireDetectorReport(String detectorName, String report) { diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/Preset.java b/async-test-lib/src/main/java/se/deversity/asynctest/Preset.java index 0c6e3e68..b94d1b7f 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/Preset.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/Preset.java @@ -106,7 +106,7 @@ public enum Preset { * exactly this set", which matters when new detectors are added in future * releases. * - * @return the enabled + * @return the detectors this preset turns on, as an unmodifiable set */ @SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "enabled is already an unmodifiable Set.copyOf snapshot stored in a final field") public @Nullable Set enabled() { @@ -116,7 +116,7 @@ public enum Preset { /** * True when the preset is the legacy default. * - * @return the is all + * @return {@code true} when this preset enables every {@link DetectorType} */ public boolean isAll() { return this == ALL || this == STRICT; diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkComparator.java b/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkComparator.java index 197c4675..35e7e728 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkComparator.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkComparator.java @@ -171,8 +171,8 @@ private String buildRegressionMessage(BenchmarkComparisonResult result) { /** * Load baseline for a specific benchmark key. * - * @param benchmarkKey the benchmark key - * @return the load baseline + * @param benchmarkKey identifies the stored baseline, normally {@code testClass#testMethod} + * @return the stored baseline for that key, or empty when none has been recorded */ public Optional loadBaseline(String benchmarkKey) { File storeFile = benchmarkStorePath.toFile(); @@ -228,7 +228,7 @@ private Map readStore(File storeFile) throws IOExceptio /** * Save a benchmark result as the new baseline. * - * @param result the result + * @param result the run to store as the new baseline, replacing any existing one for its key */ public void saveBaseline(BenchmarkResult result) { Map store = loadAllBaselines(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkComparisonResult.java b/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkComparisonResult.java index e9edd3d0..53c06594 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkComparisonResult.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkComparisonResult.java @@ -30,8 +30,8 @@ private BenchmarkComparisonResult(Builder builder) { /** * Create a result for the first run (no baseline exists). * - * @param currentResult the current result - * @return the first run + * @param currentResult the run just measured + * @return a result marked as a first run, with no comparison performed */ public static BenchmarkComparisonResult firstRun(BenchmarkResult currentResult) { return builder() @@ -96,7 +96,7 @@ public double getThresholdPercent() { /** * Check if the result is within acceptable bounds (not a regression or improvement). * - * @return the is within threshold + * @return {@code true} when the change stayed inside the configured threshold */ public boolean isWithinThreshold() { return !isRegression && !isImprovement; @@ -150,7 +150,6 @@ public int hashCode() { /** * {@return the builder} */ - public static Builder builder() { return new Builder(); } @@ -166,10 +165,9 @@ public static class Builder { /** * Current result. * - * @param currentResult the current result - * @return the current result + * @param currentResult the run just measured + * @return this builder */ - public Builder currentResult(BenchmarkResult currentResult) { this.currentResult = currentResult; return this; @@ -177,10 +175,9 @@ public Builder currentResult(BenchmarkResult currentResult) { /** * Baseline result. * - * @param baselineResult the baseline result - * @return the baseline result + * @param baselineResult the previously stored run being compared against + * @return this builder */ - public Builder baselineResult(BenchmarkResult baselineResult) { this.baselineResult = baselineResult; return this; @@ -188,10 +185,9 @@ public Builder baselineResult(BenchmarkResult baselineResult) { /** * Percent change. * - * @param percentChange the percent change - * @return the percent change + * @param percentChange change against the baseline, positive when the current run is slower + * @return this builder */ - public Builder percentChange(double percentChange) { this.percentChange = percentChange; return this; @@ -199,10 +195,9 @@ public Builder percentChange(double percentChange) { /** * Is regression. * - * @param isRegression the is regression - * @return the is regression + * @param isRegression {@code true} when the slowdown exceeded the configured threshold + * @return this builder */ - public Builder isRegression(boolean isRegression) { this.isRegression = isRegression; return this; @@ -210,10 +205,9 @@ public Builder isRegression(boolean isRegression) { /** * Is improvement. * - * @param isImprovement the is improvement - * @return the is improvement + * @param isImprovement {@code true} when the current run was measurably faster than the baseline + * @return this builder */ - public Builder isImprovement(boolean isImprovement) { this.isImprovement = isImprovement; return this; @@ -221,10 +215,9 @@ public Builder isImprovement(boolean isImprovement) { /** * Is first run. * - * @param isFirstRun the is first run - * @return the is first run + * @param isFirstRun {@code true} when no baseline existed, so nothing was compared + * @return this builder */ - public Builder isFirstRun(boolean isFirstRun) { this.isFirstRun = isFirstRun; return this; @@ -232,10 +225,9 @@ public Builder isFirstRun(boolean isFirstRun) { /** * Threshold percent. * - * @param thresholdPercent the threshold percent - * @return the threshold percent + * @param thresholdPercent the slowdown, in percent, above which a change counts as a regression + * @return this builder */ - public Builder thresholdPercent(double thresholdPercent) { this.thresholdPercent = thresholdPercent; return this; @@ -243,7 +235,6 @@ public Builder thresholdPercent(double thresholdPercent) { /** * {@return the build} */ - public BenchmarkComparisonResult build() { return new BenchmarkComparisonResult(this); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkRecorder.java b/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkRecorder.java index 30ae6eb1..b97aa7b6 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkRecorder.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkRecorder.java @@ -43,21 +43,33 @@ public class BenchmarkRecorder { private boolean benchmarkingEnabled; /** + * Creates a BenchmarkRecorder. + * * @see #BenchmarkRecorder(AsyncTestConfig, String, String, int) — prefer that * overload when the actual thread count may differ from {@code config.threads} * (e.g. {@code virtualThreadStressMode} overrides it). This overload records * {@code config.threads} as-is and exists for direct/unit-test construction. + * + * @param config the configuration whose thread and invocation counts the recorded run is keyed by + * @param testClass the fully-qualified test class, forming half of the benchmark key + * @param testMethod the test method, forming the other half of the benchmark key */ public BenchmarkRecorder(AsyncTestConfig config, String testClass, String testMethod) { this(config, testClass, testMethod, config.threads); } /** + * Creates a BenchmarkRecorder. + * * @param actualThreads the thread count actually used for this run, which may * differ from {@code config.threads} when * {@code virtualThreadStressMode} overrides it; recorded on * the baseline so comparisons are labeled correctly. * @since 1.7.0 + * + * @param config the configuration whose thread and invocation counts the recorded run is keyed by + * @param testClass the fully-qualified test class, forming half of the benchmark key + * @param testMethod the test method, forming the other half of the benchmark key */ public BenchmarkRecorder(AsyncTestConfig config, String testClass, String testMethod, int actualThreads) { this.config = config; @@ -87,7 +99,7 @@ public BenchmarkRecorder(AsyncTestConfig config, String testClass, String testMe /** * Check if benchmarking is enabled. * - * @return the is benchmarking enabled + * @return {@code true} when benchmarking is switched on for this run */ public boolean isBenchmarkingEnabled() { return benchmarkingEnabled; @@ -221,7 +233,7 @@ public long getTotalExecutionTimeNanos() { /** * Get the number of recorded invocations. * - * @return the get invocation count + * @return the number of invocations recorded so far */ public int getInvocationCount() { synchronized (invocationTimesNanos) { diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkRegressionException.java b/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkRegressionException.java index 0957ec0d..175c3da6 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkRegressionException.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkRegressionException.java @@ -13,7 +13,12 @@ public class BenchmarkRegressionException extends RuntimeException { // Not serialized: BenchmarkComparisonResult does not implement Serializable. // The message string (from super) carries the human-readable detail. private final transient BenchmarkComparisonResult comparisonResult; - + /** + * Creates a BenchmarkRegressionException. + * + * @param message the assertion text shown to whoever the failing build lands on + * @param comparisonResult the measured comparison, retained so a reporter can show both runs + */ public BenchmarkRegressionException(String message, BenchmarkComparisonResult comparisonResult) { super(message); this.comparisonResult = comparisonResult; diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkResult.java b/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkResult.java index ecfe372f..e4d0873e 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkResult.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/benchmark/BenchmarkResult.java @@ -115,7 +115,7 @@ public List getInvocationTimesNanos() { /** * Get a unique key for this benchmark (class + method). * - * @return the get benchmark key + * @return the key this result is stored under, {@code testClass#testMethod} */ public String getBenchmarkKey() { return testClass + "#" + testMethod; @@ -124,7 +124,7 @@ public String getBenchmarkKey() { /** * Calculate the standard deviation of invocation times. * - * @return the get standard deviation + * @return the standard deviation of the recorded timings, in nanoseconds */ public double getStandardDeviation() { if (invocationTimesNanos.size() <= 1) { @@ -143,7 +143,7 @@ public double getStandardDeviation() { * Format time in nanoseconds to a human-readable string. * * @param nanos the in nanoseconds - * @return the format time + * @return the duration rendered with a unit, for display in a report */ public static String formatTime(long nanos) { if (nanos < 1_000) { @@ -190,7 +190,6 @@ public int hashCode() { /** * {@return the builder} */ - public static Builder builder() { return new Builder(); } @@ -209,10 +208,9 @@ public static class Builder { /** * Test class. * - * @param testClass the test class - * @return the test class + * @param testClass the fully-qualified test class this measurement came from + * @return this builder */ - public Builder testClass(String testClass) { this.testClass = testClass; return this; @@ -220,10 +218,9 @@ public Builder testClass(String testClass) { /** * Test method. * - * @param testMethod the test method - * @return the test method + * @param testMethod the test method this measurement came from + * @return this builder */ - public Builder testMethod(String testMethod) { this.testMethod = testMethod; return this; @@ -231,10 +228,9 @@ public Builder testMethod(String testMethod) { /** * Timestamp. * - * @param timestamp the timestamp - * @return the timestamp + * @param timestamp when the measurement was taken + * @return this builder */ - public Builder timestamp(LocalDateTime timestamp) { this.timestamp = timestamp; return this; @@ -242,10 +238,9 @@ public Builder timestamp(LocalDateTime timestamp) { /** * Threads. * - * @param threads the threads - * @return the threads + * @param threads the number of threads the measured run used + * @return this builder */ - public Builder threads(int threads) { this.threads = threads; return this; @@ -253,10 +248,9 @@ public Builder threads(int threads) { /** * Invocations. * - * @param invocations the invocations - * @return the invocations + * @param invocations the number of invocations the measured run performed + * @return this builder */ - public Builder invocations(int invocations) { this.invocations = invocations; return this; @@ -267,7 +261,6 @@ public Builder invocations(int invocations) { * @param totalExecutionTimeNanos the total execution time in nanoseconds * @return the total execution time in nanoseconds */ - public Builder totalExecutionTimeNanos(long totalExecutionTimeNanos) { this.totalExecutionTimeNanos = totalExecutionTimeNanos; return this; @@ -278,7 +271,6 @@ public Builder totalExecutionTimeNanos(long totalExecutionTimeNanos) { * @param avgTimePerInvocationNanos the avg time per invocation in nanoseconds * @return the avg time per invocation in nanoseconds */ - public Builder avgTimePerInvocationNanos(long avgTimePerInvocationNanos) { this.avgTimePerInvocationNanos = avgTimePerInvocationNanos; return this; @@ -289,7 +281,6 @@ public Builder avgTimePerInvocationNanos(long avgTimePerInvocationNanos) { * @param minTimePerInvocationNanos the min time per invocation in nanoseconds * @return the min time per invocation in nanoseconds */ - public Builder minTimePerInvocationNanos(long minTimePerInvocationNanos) { this.minTimePerInvocationNanos = minTimePerInvocationNanos; return this; @@ -300,7 +291,6 @@ public Builder minTimePerInvocationNanos(long minTimePerInvocationNanos) { * @param maxTimePerInvocationNanos the max time per invocation in nanoseconds * @return the max time per invocation in nanoseconds */ - public Builder maxTimePerInvocationNanos(long maxTimePerInvocationNanos) { this.maxTimePerInvocationNanos = maxTimePerInvocationNanos; return this; @@ -311,7 +301,6 @@ public Builder maxTimePerInvocationNanos(long maxTimePerInvocationNanos) { * @param invocationTimesNanos the invocation times in nanoseconds * @return the invocation times in nanoseconds */ - public Builder invocationTimesNanos(List invocationTimesNanos) { this.invocationTimesNanos = new ArrayList<>(invocationTimesNanos); return this; @@ -319,7 +308,6 @@ public Builder invocationTimesNanos(List invocationTimesNanos) { /** * {@return the build} */ - public BenchmarkResult build() { return new BenchmarkResult(this); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ABAProblemDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ABAProblemDetector.java index 86a844e9..6e15fde3 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ABAProblemDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ABAProblemDetector.java @@ -76,9 +76,9 @@ private static class CASAttempt { /** * Record a value change in an atomic variable. * - * @param variableName the variable name - * @param oldValue the old value - * @param newValue the new value + * @param variableName a label identifying the variable in the report + * @param oldValue the value present before the write + * @param newValue the value being written */ public void recordValueChange(String variableName, Object oldValue, Object newValue) { if (!enabled) return; @@ -99,11 +99,11 @@ public void recordValueChange(String variableName, Object oldValue, Object newVa /** * Record a CAS (Compare-And-Swap) attempt. * - * @param variableName the variable name - * @param expectedValue the expected value - * @param newValue the new value + * @param variableName a label identifying the variable in the report + * @param expectedValue the value the compare-and-set expected to find + * @param newValue the value being written * @param succeeded the {@code succeeded} flag - * @param actualCurrentValue the actual current value + * @param actualCurrentValue the value actually found, when it differed from the expected one */ public void recordCASAttempt(String variableName, Object expectedValue, Object newValue, boolean succeeded, Object actualCurrentValue) { @@ -194,7 +194,7 @@ private boolean detectABA(AtomicValueHistory history, CASAttempt attempt) { /** * Analyze for ABA problems. * - * @return the analyze ABA + * @return the findings this detector collected during the run */ public ABAReport analyzeABA() { ABAReport report = new ABAReport(); @@ -222,7 +222,7 @@ public ABAReport analyzeABA() { /** * Standardized alias for {@link #analyzeABA()}. * - * @return the analyze + * @return the findings this detector collected during the run */ public ABAReport analyze() { return analyzeABA(); @@ -230,29 +230,26 @@ public ABAReport analyze() { /** * Clears recorded the observation so this instance can be reused for the next run. */ - public void reset() { trackedVariables.clear(); } /** * Disable. */ - public void disable() { enabled = false; } /** * Enable. */ - public void enable() { enabled = true; } public static class ABAReport { - /** The variables with cycles. */ + /** How many A-B-A cycles were observed per variable. */ public final Map variablesWithCycles = new HashMap<>(); - /** The successful ABA cases. */ + /** Compare-and-set calls that succeeded even though the value had changed and changed back. */ public final Set successfulABACases = new HashSet<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/AtomicNonAtomicUpdateDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/AtomicNonAtomicUpdateDetector.java index 8533d746..946fea99 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/AtomicNonAtomicUpdateDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/AtomicNonAtomicUpdateDetector.java @@ -48,9 +48,9 @@ private AtomicState stateFor(Object atomic, String name) { /** * Call after {@code atomic.get()}. * - * @param atomic the atomic - * @param name the name - * @param thread the thread + * @param atomic the atomic being recorded, tracked by identity + * @param name a label identifying the atomic in the report + * @param thread the thread performing the operation */ public void recordGet(Object atomic, String name, Thread thread) { if (atomic == null || thread == null) return; @@ -62,9 +62,9 @@ public void recordGet(Object atomic, String name, Thread thread) { * If the same thread previously called {@link #recordGet} without an intervening CAS, * the sequence is flagged as a lost-update race. * - * @param atomic the atomic - * @param name the name - * @param thread the thread + * @param atomic the atomic being recorded, tracked by identity + * @param name a label identifying the atomic in the report + * @param thread the thread performing the operation */ public void recordSet(Object atomic, String name, Thread thread) { if (atomic == null || thread == null) return; @@ -81,9 +81,9 @@ public void recordSet(Object atomic, String name, Thread thread) { /** * Call after a successful {@code atomic.compareAndSet()} — clears the pending-get flag. * - * @param atomic the atomic - * @param name the name - * @param thread the thread + * @param atomic the atomic being recorded, tracked by identity + * @param name a label identifying the atomic in the report + * @param thread the thread performing the operation */ public void recordCas(Object atomic, String name, Thread thread) { if (atomic == null || thread == null) return; diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/AtomicityValidator.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/AtomicityValidator.java index dba21b16..8b349e70 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/AtomicityValidator.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/AtomicityValidator.java @@ -50,9 +50,8 @@ private static class FieldAccessRecord { /** * Records compound operation start so it can be analysed at the end of the run. * - * @param operationName the operation name + * @param operationName a label identifying the operation in the report */ - public void recordCompoundOperationStart(String operationName) { if (!enabled || operationName == null || operationName.isBlank()) { return; @@ -64,9 +63,8 @@ public void recordCompoundOperationStart(String operationName) { /** * Records compound operation end so it can be analysed at the end of the run. * - * @param operationName the operation name + * @param operationName a label identifying the operation in the report */ - public void recordCompoundOperationEnd(String operationName) { if (!enabled || operationName == null || operationName.isBlank()) { return; @@ -77,11 +75,10 @@ public void recordCompoundOperationEnd(String operationName) { /** * Records field access so it can be analysed at the end of the run. * - * @param fieldName the field name - * @param value the value - * @param isWrite the is write + * @param fieldName the field involved, as it should appear in the report + * @param value the value read or written + * @param isWrite {@code true} for a write, {@code false} for a read */ - public void recordFieldAccess(String fieldName, @Nullable Object value, boolean isWrite) { recordFieldAccess(fieldName, value, isWrite, Thread.currentThread().threadId()); } @@ -142,13 +139,12 @@ public void recordFieldAccess(String fieldName, @Nullable Object value, boolean /** * Detect check then act violation. * - * @param fieldName the field name - * @param checkValue the check value - * @param expectedValue the expected value - * @param wouldAct the would act - * @return the detect check then act violation + * @param fieldName the field involved, as it should appear in the report + * @param checkValue the value observed by the check + * @param expectedValue the value the caller expected to find + * @param wouldAct {@code true} when the caller would have acted on the checked value + * @return {@code true} when a check-then-act sequence was observed on that field */ - public boolean detectCheckThenActViolation(String fieldName, Object checkValue, Object expectedValue, boolean wouldAct) { if (!enabled || !wouldAct) { @@ -167,9 +163,8 @@ public boolean detectCheckThenActViolation(String fieldName, Object checkValue, /** * Analyses what has been recorded about atomicity and builds the report for it. * - * @return the analyze atomicity + * @return the findings this detector collected during the run */ - public AtomicityReport analyzeAtomicity() { AtomicityReport report = new AtomicityReport(); report.checkThenActViolations.addAll(atomicityViolations); @@ -209,7 +204,7 @@ public AtomicityReport analyzeAtomicity() { /** * Standardized alias for {@link #analyzeAtomicity()}. * - * @return the analyze + * @return the findings this detector collected during the run */ public AtomicityReport analyze() { return analyzeAtomicity(); @@ -221,7 +216,6 @@ private String operationKey(String operationName) { /** * Clears recorded the observation so this instance can be reused for the next run. */ - public void reset() { activeOperations.clear(); fieldHistory.clear(); @@ -230,24 +224,22 @@ public void reset() { /** * Disable. */ - public void disable() { enabled = false; } /** * Enable. */ - public void enable() { enabled = true; } public static class AtomicityReport { - /** The check then act violations. */ + /** Fields checked and then acted on without holding a lock across both. */ public final Set checkThenActViolations = new HashSet<>(); - /** The unsafe field accesses. */ + /** Fields with mixed reads and writes from more than one thread. */ public final Set unsafeFieldAccesses = new HashSet<>(); - /** The totcou races. */ + /** Fields whose state changed between the check and the use (TOCTOU). The field name misspells the acronym; it is public API and kept as-is for compatibility. */ public final Set totcouRaces = new HashSet<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/AutoFix.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/AutoFix.java index 5ab981ac..6fed9116 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/AutoFix.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/AutoFix.java @@ -95,7 +95,7 @@ public static String getRaceConditionFix() { /** * Deposit. * - * @param amount the amount + * @param amount how many, for the operation being recorded */ // Before: public void deposit(long amount) { @@ -104,7 +104,7 @@ public void deposit(long amount) { /** * Deposit. * - * @param amount the amount + * @param amount how many, for the operation being recorded */ // After: @@ -120,9 +120,8 @@ public synchronized void deposit(long amount) { /** * Deposit. * - * @param amount the amount + * @param amount how many, for the operation being recorded */ - public void deposit(long amount) { lock.lock(); try { @@ -171,14 +170,12 @@ public static String getVisibilityFix() { /** * Set ready. */ - public synchronized void setReady() { ready = true; } /** * {@return the is ready} */ - public synchronized boolean isReady() { return ready; } @@ -464,7 +461,6 @@ public static String getAtomicityViolationFix() { /** * Increment. */ - public synchronized void increment() { counter++; // Now atomic } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/BlockingQueueDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/BlockingQueueDetector.java index a33f5c25..ef71a702 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/BlockingQueueDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/BlockingQueueDetector.java @@ -83,7 +83,7 @@ public void registerQueue(BlockingQueue queue, String name, int capacity) { /** * Record an offer() call. * - * @param queue the queue + * @param queue the queue being recorded, tracked by identity * @param name the queue name (should match registration) * @param success true if offer succeeded, false if queue was full */ @@ -107,7 +107,7 @@ public void recordOffer(BlockingQueue queue, String name, boolean success) { /** * Record a poll() call. * - * @param queue the queue + * @param queue the queue being recorded, tracked by identity * @param name the queue name (should match registration) * @param success true if poll returned an element, false if queue was empty */ @@ -131,7 +131,7 @@ public void recordPoll(BlockingQueue queue, String name, boolean success) { /** * Record a put() call (blocking insert). * - * @param queue the queue + * @param queue the queue being recorded, tracked by identity * @param name the queue name (should match registration) */ public void recordPut(BlockingQueue queue, String name) { @@ -149,7 +149,7 @@ public void recordPut(BlockingQueue queue, String name) { /** * Record a take() call (blocking retrieval). * - * @param queue the queue + * @param queue the queue being recorded, tracked by identity * @param name the queue name (should match registration) */ public void recordTake(BlockingQueue queue, String name) { @@ -242,7 +242,7 @@ public static class BlockingQueueReport { /** * Check if any issues were detected. * - * @return the has issues + * @return {@code true} when this detector recorded something worth reporting */ public boolean hasIssues() { return !silentFailures.isEmpty() || !emptyPolls.isEmpty() || diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/BusyWaitDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/BusyWaitDetector.java index 5dff5013..60a24350 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/BusyWaitDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/BusyWaitDetector.java @@ -42,7 +42,6 @@ private static class SpinEvent { /** * Records loop iteration so it can be analysed at the end of the run. */ - public void recordLoopIteration() { if (!enabled) { return; @@ -64,7 +63,6 @@ public void recordLoopIteration() { /** * Records yield so it can be analysed at the end of the run. */ - public void recordYield() { if (!enabled) { return; @@ -92,10 +90,9 @@ public void recordYield() { /** * Report spin loop. * - * @param description the description - * @param iterations the iterations + * @param description free text describing the event, shown in the report + * @param iterations how many iterations the spin ran for */ - public void reportSpinLoop(String description, long iterations) { if (!enabled) { return; @@ -118,9 +115,8 @@ private String inferCallSite() { /** * Analyses what has been recorded about busy waiting and builds the report for it. * - * @return the analyze busy waiting + * @return the findings this detector collected during the run */ - public BusyWaitReport analyzeBusyWaiting() { BusyWaitReport report = new BusyWaitReport(); @@ -174,7 +170,7 @@ private static void addToReport(BusyWaitReport report, long threadId, SpinEvent /** * Standardized alias for {@link #analyzeBusyWaiting()}. * - * @return the analyze + * @return the findings this detector collected during the run */ public BusyWaitReport analyze() { return analyzeBusyWaiting(); @@ -182,31 +178,28 @@ public BusyWaitReport analyze() { /** * Clears recorded the observation so this instance can be reused for the next run. */ - public void reset() { threadActivities.clear(); } /** * Disable. */ - public void disable() { enabled = false; } /** * Enable. */ - public void enable() { enabled = true; } public static class BusyWaitReport { - /** The busy wait loops. */ + /** Loops that spun waiting for a condition instead of blocking. */ public final Set busyWaitLoops = new HashSet<>(); - /** The tight loops. */ + /** Loops that spun with no back-off at all. */ public final Set tightLoops = new HashSet<>(); - /** The cpu wasted. */ + /** Nanoseconds of CPU time spent spinning rather than blocking. */ public long cpuWasted; /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CacheConcurrencyDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CacheConcurrencyDetector.java index be662f32..587a694f 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CacheConcurrencyDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CacheConcurrencyDetector.java @@ -223,7 +223,7 @@ public static class CacheConcurrencyReport { /** * Check if any issues were detected. * - * @return the has issues + * @return {@code true} when this detector recorded something worth reporting */ public boolean hasIssues() { return !concurrentReadWrite.isEmpty() || diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CalendarDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CalendarDetector.java index 419d7105..8ed7f781 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CalendarDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CalendarDetector.java @@ -140,7 +140,7 @@ private void recordAccess(Calendar calendar, String name, String method) { /** * Analyse Calendar usage and return a report. * - * @return the analyze + * @return the findings this detector collected during the run */ public CalendarReport analyze() { CalendarReport report = new CalendarReport(); @@ -195,7 +195,7 @@ public static class CalendarReport { /** * Returns {@code true} when shared-access or errors were detected. * - * @return the has issues + * @return {@code true} when this detector recorded something worth reporting */ public boolean hasIssues() { return !sharedCalendars.isEmpty() || !calendarErrors.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureBlockingCallbackDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureBlockingCallbackDetector.java index 76b346f0..02009fb0 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureBlockingCallbackDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureBlockingCallbackDetector.java @@ -38,8 +38,8 @@ private static final class State { /** * Record entry into a CompletableFuture callback. * - * @param callbackName the callback name - * @param thread the thread + * @param callbackName a label identifying the callback in the report + * @param thread the thread performing the operation */ public void recordEnterCallback(String callbackName, Thread thread) { if (thread == null) return; @@ -49,7 +49,7 @@ public void recordEnterCallback(String callbackName, Thread thread) { /** * Record exit from a CompletableFuture callback. * - * @param thread the thread + * @param thread the thread performing the operation */ public void recordExitCallback(Thread thread) { if (thread == null) return; @@ -59,8 +59,8 @@ public void recordExitCallback(Thread thread) { /** * Record a blocking call executed on a thread. * - * @param thread the thread - * @param blockingApiName the blocking api name + * @param thread the thread performing the operation + * @param blockingApiName the blocking API that was called, as it should appear in the report */ public void recordBlockingCall(Thread thread, String blockingApiName) { if (thread == null) return; @@ -73,9 +73,8 @@ public void recordBlockingCall(Thread thread, String blockingApiName) { /** * Analyses what has been recorded about the observation and builds the report for it. * - * @return the analyze + * @return the findings this detector collected during the run */ - public Report analyze() { Report r = new Report(); for (State s : violations.values()) { @@ -100,9 +99,9 @@ public Report analyze() { } public static final class Report { - /** The violations. */ + /** Findings as human-readable lines, for the text report. */ public final List violations = new ArrayList<>(); - /** The structured violations. */ + /** The same findings as {@link se.deversity.asynctest.report.Violation} objects, for machine-readable reports. */ public final List structuredViolations = new ArrayList<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureChainDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureChainDetector.java index 5aed16bc..05a6f842 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureChainDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureChainDetector.java @@ -244,7 +244,7 @@ public static class CompletableFutureChainReport { /** * Check if any issues were detected. * - * @return the has issues + * @return {@code true} when this detector recorded something worth reporting */ public boolean hasIssues() { return !unjoinedFutures.isEmpty() || diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureExceptionDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureExceptionDetector.java index 30f58402..1271c9a7 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureExceptionDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureExceptionDetector.java @@ -190,7 +190,7 @@ public static class CompletableFutureExceptionReport { /** * Check if any issues were detected. * - * @return the has issues + * @return {@code true} when this detector recorded something worth reporting */ public boolean hasIssues() { return !unhandledExceptions.isEmpty() || !missingHandlers.isEmpty() || !swallowedExceptions.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureObtrudeDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureObtrudeDetector.java index 94479b47..f231d658 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureObtrudeDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CompletableFutureObtrudeDetector.java @@ -40,9 +40,9 @@ private static final class State { /** * Record an obtrude action on a CompletableFuture. * - * @param future the future - * @param label the label - * @param thread the thread + * @param future the future being recorded, tracked by identity + * @param label a label identifying it in the report + * @param thread the thread performing the operation */ public void recordObtrude(CompletableFuture future, String label, Thread thread) { if (future == null || thread == null) return; @@ -55,9 +55,8 @@ public void recordObtrude(CompletableFuture future, String label, Thread thre /** * Analyses what has been recorded about the observation and builds the report for it. * - * @return the analyze + * @return the findings this detector collected during the run */ - public Report analyze() { Report r = new Report(); for (State s : obtrudes.values()) { @@ -83,9 +82,9 @@ public Report analyze() { } public static final class Report { - /** The violations. */ + /** Findings as human-readable lines, for the text report. */ public final List violations = new ArrayList<>(); - /** The structured violations. */ + /** The same findings as {@link se.deversity.asynctest.report.Violation} objects, for machine-readable reports. */ public final List structuredViolations = new ArrayList<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConcurrentMapComputeRecursionDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConcurrentMapComputeRecursionDetector.java index 83cc6d59..9f2b03b2 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConcurrentMapComputeRecursionDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConcurrentMapComputeRecursionDetector.java @@ -64,9 +64,9 @@ public void recordComputeStart(Map map, Object key, Thread thread, String /** * Record exit from a {@code compute*} / {@code merge} mapping function. * - * @param map the map - * @param key the key - * @param thread the thread + * @param map the map the computation was running on, tracked by identity + * @param key the key the entry is stored under + * @param thread the thread performing the operation */ public void recordComputeEnd(Map map, Object key, Thread thread) { if (map == null || key == null || thread == null) return; diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConcurrentModificationDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConcurrentModificationDetector.java index 0fcc827f..f8ee0369 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConcurrentModificationDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConcurrentModificationDetector.java @@ -207,7 +207,7 @@ public static class ConcurrentModificationReport { /** * Check if any issues were detected. * - * @return the has issues + * @return {@code true} when this detector recorded something worth reporting */ public boolean hasIssues() { return !concurrentModifications.isEmpty() || !concurrentIterations.isEmpty() || !concurrentMutations.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConditionVariableDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConditionVariableDetector.java index f81b22b4..f83c3479 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConditionVariableDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConditionVariableDetector.java @@ -80,7 +80,7 @@ public void registerCondition(Condition condition, String name) { /** * Record an await() call. * - * @param condition the Condition + * @param condition the condition being awaited or signalled, tracked by identity * @param name the condition name (should match registration) */ public void recordAwait(Condition condition, String name) { @@ -98,7 +98,7 @@ public void recordAwait(Condition condition, String name) { /** * Record an await() exit (normal or timeout). * - * @param condition the Condition + * @param condition the condition being awaited or signalled, tracked by identity * @param name the condition name (should match registration) * @param timedOut true if await timed out, false if signaled */ @@ -116,7 +116,7 @@ public void recordAwaitExit(Condition condition, String name, boolean timedOut) /** * Record a signal() call. * - * @param condition the Condition + * @param condition the condition being awaited or signalled, tracked by identity * @param name the condition name (should match registration) * @param isSignalAll true if signalAll(), false if signal() */ @@ -202,7 +202,7 @@ public static class ConditionVariableReport { /** * Check if any issues were detected. * - * @return the has issues + * @return {@code true} when this detector recorded something worth reporting */ public boolean hasIssues() { return !lostSignals.isEmpty() || !stuckWaiters.isEmpty() || !missingSignals.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConstructorSafetyValidator.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConstructorSafetyValidator.java index 68f088c2..2701b0f0 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConstructorSafetyValidator.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ConstructorSafetyValidator.java @@ -52,7 +52,7 @@ private static final class FieldAccessInfo { /** * Mark the start of object construction. * - * @param object the object + * @param object the object the access is on, tracked by identity */ public void recordConstructionStart(Object object) { if (!enabled || object == null) return; @@ -65,7 +65,7 @@ public void recordConstructionStart(Object object) { /** * Mark the end of object construction. * - * @param object the object + * @param object the object the access is on, tracked by identity */ public void recordConstructionEnd(Object object) { if (!enabled) return; @@ -81,9 +81,9 @@ public void recordConstructionEnd(Object object) { /** * Record a field access to a partially constructed object. * - * @param object the object - * @param fieldName the field name - * @param timestamp the timestamp + * @param object the object the access is on, tracked by identity + * @param fieldName the field involved, as it should appear in the report + * @param timestamp when the event happened, in nanoseconds */ public void recordFieldAccess(Object object, String fieldName, long timestamp) { if (!enabled) return; @@ -116,7 +116,7 @@ public void recordFieldAccess(Object object, String fieldName, long timestamp) { /** * Validate constructor safety. * - * @return the validate constructor safety + * @return the findings this detector collected during the run */ public ConstructorSafetyReport validateConstructorSafety() { ConstructorSafetyReport report = new ConstructorSafetyReport(); @@ -167,31 +167,28 @@ public ConstructorSafetyReport validateConstructorSafety() { /** * Clears recorded the observation so this instance can be reused for the next run. */ - public void reset() { objects.clear(); } /** * Disable. */ - public void disable() { enabled = false; } /** * Enable. */ - public void enable() { enabled = true; } public static class ConstructorSafetyReport { - /** The unsafe objects. */ + /** Objects whose reference escaped their constructor. */ public final Set unsafeObjects = new HashSet<>(); - /** The possibly incomplete constructions. */ + /** Objects published before construction finished. */ public final Set possiblyIncompleteConstructions = new HashSet<>(); - /** The fields accessed during construction. */ + /** Fields read by another thread before the constructor returned. */ public final Set fieldsAccessedDuringConstruction = new HashSet<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CopyOnWriteCollectionDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CopyOnWriteCollectionDetector.java index 84f31bfd..a1909b9d 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CopyOnWriteCollectionDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CopyOnWriteCollectionDetector.java @@ -118,7 +118,7 @@ private CoWState resolve(Object collection, String name) { /** * Analyse Copy-on-Write collection usage and return a report. * - * @return the analyze + * @return the findings this detector collected during the run */ public CopyOnWriteReport analyze() { CopyOnWriteReport report = new CopyOnWriteReport(); @@ -163,7 +163,7 @@ public static class CopyOnWriteReport { /** * Returns {@code true} when any write-heavy violations were detected. * - * @return the has issues + * @return {@code true} when this detector recorded something worth reporting */ public boolean hasIssues() { return !writeHeavyViolations.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CountDownLatchDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CountDownLatchDetector.java index da9eceba..80637816 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CountDownLatchDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CountDownLatchDetector.java @@ -24,9 +24,9 @@ public class CountDownLatchDetector { /** * Register a CountDownLatch for monitoring. * - * @param latch the latch - * @param name the name - * @param initialCount the initial count + * @param latch the latch being recorded, tracked by identity + * @param name a label identifying the latch in the report + * @param initialCount the count the latch was created with */ public void registerLatch(CountDownLatch latch, String name, int initialCount) { latchRegistry.put(latch, new LatchInfo(name, initialCount)); @@ -35,7 +35,7 @@ public void registerLatch(CountDownLatch latch, String name, int initialCount) { /** * Record a countDown() call. * - * @param latch the latch + * @param latch the latch being recorded, tracked by identity */ public void recordCountDown(CountDownLatch latch) { LatchInfo info = latchRegistry.get(latch); @@ -50,7 +50,7 @@ public void recordCountDown(CountDownLatch latch) { /** * Record an await() call that timed out. * - * @param latch the latch + * @param latch the latch being recorded, tracked by identity */ public void recordTimeout(CountDownLatch latch) { timedOutLatches.add(latch); @@ -59,7 +59,7 @@ public void recordTimeout(CountDownLatch latch) { /** * Record a successful await() call. * - * @param latch the latch + * @param latch the latch being recorded, tracked by identity */ public void recordAwaitSuccess(CountDownLatch latch) { LatchInfo info = latchRegistry.get(latch); @@ -71,7 +71,7 @@ public void recordAwaitSuccess(CountDownLatch latch) { /** * Analyze latch usage and return report. * - * @return the analyze + * @return the findings this detector collected during the run */ public CountDownLatchReport analyze() { return new CountDownLatchReport( @@ -88,7 +88,13 @@ public static class CountDownLatchReport { private final Map latchRegistry; private final Set timedOutLatches; private final Set extraCountDownLatches; - + /** + * Creates a CountDownLatchReport. + * + * @param latchRegistry every registered latch and what was observed on it + * @param timedOutLatches the latches whose await timed out + * @param extraCountDownLatches the latches counted down more times than they were created for + */ public CountDownLatchReport( Map latchRegistry, Set timedOutLatches, diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CyclicBarrierDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CyclicBarrierDetector.java index 9a3f1233..a1610346 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CyclicBarrierDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/CyclicBarrierDetector.java @@ -25,9 +25,9 @@ public class CyclicBarrierDetector { /** * Register a CyclicBarrier for monitoring. * - * @param barrier the barrier - * @param name the name - * @param parties the parties + * @param barrier the barrier being recorded, tracked by identity + * @param name a label identifying the barrier in the report + * @param parties the number of parties the barrier was created for */ public void registerBarrier(CyclicBarrier barrier, String name, int parties) { barrierRegistry.put(barrier, new BarrierInfo(name, parties)); @@ -36,7 +36,7 @@ public void registerBarrier(CyclicBarrier barrier, String name, int parties) { /** * Record a thread arriving at the barrier. * - * @param barrier the barrier + * @param barrier the barrier being recorded, tracked by identity */ public void recordArrival(CyclicBarrier barrier) { BarrierInfo info = barrierRegistry.get(barrier); @@ -48,7 +48,7 @@ public void recordArrival(CyclicBarrier barrier) { /** * Record a barrier await() that timed out. * - * @param barrier the barrier + * @param barrier the barrier being recorded, tracked by identity */ public void recordTimeout(CyclicBarrier barrier) { timedOutBarriers.add(barrier); @@ -57,7 +57,7 @@ public void recordTimeout(CyclicBarrier barrier) { /** * Record a barrier that was broken. * - * @param barrier the barrier + * @param barrier the barrier being recorded, tracked by identity */ public void recordBroken(CyclicBarrier barrier) { brokenBarriers.add(barrier); @@ -67,7 +67,7 @@ public void recordBroken(CyclicBarrier barrier) { * Record a barrier that was reset, repairing it after it broke. * Subsequent await() calls are no longer considered reuse-after-broken. * - * @param barrier the barrier + * @param barrier the barrier being recorded, tracked by identity */ public void recordReset(CyclicBarrier barrier) { brokenBarriers.remove(barrier); @@ -78,7 +78,7 @@ public void recordReset(CyclicBarrier barrier) { * currently broken and has not been reset since, this is flagged as * reuse of a broken barrier without an intervening reset(). * - * @param barrier the barrier + * @param barrier the barrier being recorded, tracked by identity */ public void recordAwait(CyclicBarrier barrier) { if (brokenBarriers.contains(barrier)) { @@ -89,7 +89,7 @@ public void recordAwait(CyclicBarrier barrier) { /** * Record successful barrier completion. * - * @param barrier the barrier + * @param barrier the barrier being recorded, tracked by identity */ public void recordBarrierComplete(CyclicBarrier barrier) { BarrierInfo info = barrierRegistry.get(barrier); @@ -101,7 +101,7 @@ public void recordBarrierComplete(CyclicBarrier barrier) { /** * Analyze barrier usage and return report. * - * @return the analyze + * @return the findings this detector collected during the run */ public CyclicBarrierReport analyze() { return new CyclicBarrierReport( @@ -120,7 +120,14 @@ public static class CyclicBarrierReport { private final Set timedOutBarriers; private final Set brokenBarriers; private final Set reuseAfterBrokenBarriers; - + /** + * Creates a CyclicBarrierReport. + * + * @param barrierRegistry every registered barrier and what was observed on it + * @param timedOutBarriers the barriers whose await timed out + * @param brokenBarriers the barriers left in a broken state + * @param reuseAfterBrokenBarriers the barriers used again after they had broken + */ public CyclicBarrierReport( Map barrierRegistry, Set timedOutBarriers, @@ -139,6 +146,10 @@ public CyclicBarrierReport( * * @deprecated since 1.7.0 — use the four-argument constructor; this overload * reports no reuse-after-broken barriers. + * + * @param barrierRegistry every registered barrier and what was observed on it + * @param timedOutBarriers the barriers whose await timed out + * @param brokenBarriers the barriers left in a broken state */ @Deprecated(since = "1.7.0") public CyclicBarrierReport( diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DaemonThreadHygieneDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DaemonThreadHygieneDetector.java index 5ca8b3ba..7815027f 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DaemonThreadHygieneDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DaemonThreadHygieneDetector.java @@ -100,7 +100,7 @@ public void recordThread(Thread thread, String label) { * registration time, and (2) is still alive (or never started) at analysis * time — i.e. has not cleanly terminated. * - * @return the analyze + * @return the findings this detector collected during the run */ public Report analyze() { Report r = new Report(); @@ -168,11 +168,11 @@ public Report analyze() { /** Report produced by {@link #analyze()}. */ public static final class Report { - /** The violations. */ + /** Findings as human-readable lines, for the text report. */ public final List violations = new ArrayList<>(); - /** The flagged. */ + /** Threads whose daemon status does not match what the run expects. */ public final Set flagged = new LinkedHashSet<>(); - /** The structured violations. */ + /** The same findings as {@link se.deversity.asynctest.report.Violation} objects, for machine-readable reports. */ public final List structuredViolations = new ArrayList<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DeadlockDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DeadlockDetector.java index fa5eebd3..93ac90c8 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DeadlockDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DeadlockDetector.java @@ -27,7 +27,9 @@ public class DeadlockDetector { * remain valid exclusion keys for the detector's lifetime. */ private final Set preexistingDeadlockedThreads; - + /** + * Creates a DeadlockDetector. + */ public DeadlockDetector() { long[] existing = ManagementFactory.getThreadMXBean().findDeadlockedThreads(); if (existing == null || existing.length == 0) { @@ -43,7 +45,6 @@ public DeadlockDetector() { /** * Prints thread dump to the report output. */ - public static void printThreadDump() { ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); @@ -198,7 +199,7 @@ private static void printLockChain(ThreadInfo thread, Map thre *

This is a JVM-wide snapshot: unlike {@link #analyze()}, it also reports deadlocks * that predate any particular detector instance. * - * @return the has deadlock + * @return {@code true} when a lock-order cycle was observed */ public static boolean hasDeadlock() { ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); @@ -209,7 +210,7 @@ public static boolean hasDeadlock() { /** * Get a summary of current lock contention. * - * @return the get lock contention summary + * @return a human-readable summary of lock contention, for the report */ @SuppressWarnings("PMD.AssignmentInOperand") // counter++ in switch arrow case is idiomatic public static String getLockContentionSummary() { @@ -248,7 +249,7 @@ public static void printLearningAndFix() { *

Deadlocks that already existed when this detector was constructed are excluded, * so a report with issues always means the monitored test introduced a new deadlock. * - * @return the analyze + * @return the findings this detector collected during the run */ public DeadlockReport analyze() { long[] current = ManagementFactory.getThreadMXBean().findDeadlockedThreads(); @@ -265,7 +266,11 @@ public DeadlockReport analyze() { public static class DeadlockReport { private final boolean deadlocked; - + /** + * Creates a DeadlockReport. + * + * @param deadlocked the {@code deadlocked} flag + */ public DeadlockReport(boolean deadlocked) { this.deadlocked = deadlocked; } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DoubleCheckedLockingDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DoubleCheckedLockingDetector.java index 36d593c6..6e9744bf 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DoubleCheckedLockingDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/DoubleCheckedLockingDetector.java @@ -32,7 +32,7 @@ public class DoubleCheckedLockingDetector { /** * Register a double-checked locking pattern for monitoring. * - * @param fieldName the field name + * @param fieldName the field involved, as it should appear in the report * @param isVolatile the {@code isVolatile} flag * @param hasFirstCheck the {@code hasFirstCheck} flag * @param hasSecondCheck the {@code hasSecondCheck} flag @@ -53,7 +53,7 @@ public void registerDCL(String fieldName, boolean isVolatile, boolean hasFirstCh /** * Record access to a field that might use DCL. * - * @param fieldName the field name + * @param fieldName the field involved, as it should appear in the report * @param isRead the {@code isRead} flag * @param isWrite the {@code isWrite} flag */ @@ -68,7 +68,7 @@ public void recordAccess(String fieldName, boolean isRead, boolean isWrite) { /** * Analyze DCL patterns and return report. * - * @return the analyze + * @return the findings this detector collected during the run */ public DoubleCheckedLockingReport analyze() { return new DoubleCheckedLockingReport(dclRegistry, brokenDCLs); @@ -80,7 +80,12 @@ public DoubleCheckedLockingReport analyze() { public static class DoubleCheckedLockingReport { private final Map dclRegistry; private final Set brokenDCLs; - + /** + * Creates a DoubleCheckedLockingReport. + * + * @param dclRegistry every registered double-checked-locking site and what was observed on it + * @param brokenDCLs the double-checked-locking sites whose guard was not safe + */ public DoubleCheckedLockingReport( Map dclRegistry, Set brokenDCLs diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ExchangerDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ExchangerDetector.java index fd989972..fa571910 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ExchangerDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ExchangerDetector.java @@ -25,8 +25,8 @@ public class ExchangerDetector { /** * Register an Exchanger for monitoring. * - * @param exchanger the exchanger - * @param name the name + * @param exchanger the exchanger being recorded, tracked by identity + * @param name a label identifying the exchanger in the report */ public void registerExchanger(Exchanger exchanger, String name) { exchangerRegistry.put(exchanger, new ExchangerInfo(name)); @@ -35,8 +35,8 @@ public void registerExchanger(Exchanger exchanger, String name) { /** * Record a thread starting an exchange. * - * @param exchanger the exchanger - * @param exchangerName the exchanger name + * @param exchanger the exchanger being recorded, tracked by identity + * @param exchangerName a label identifying the exchanger in the report */ public void recordExchangeStart(Exchanger exchanger, String exchangerName) { ExchangerInfo info = exchangerRegistry.get(exchanger); @@ -48,9 +48,9 @@ public void recordExchangeStart(Exchanger exchanger, String exchangerName) { /** * Record a successful exchange completion. * - * @param exchanger the exchanger - * @param exchangerName the exchanger name - * @param value the value + * @param exchanger the exchanger being recorded, tracked by identity + * @param exchangerName a label identifying the exchanger in the report + * @param value the value handed to the partner in the exchange */ public void recordExchangeComplete(Exchanger exchanger, String exchangerName, Object value) { ExchangerInfo info = exchangerRegistry.get(exchanger); @@ -65,7 +65,7 @@ public void recordExchangeComplete(Exchanger exchanger, String exchangerName, /** * Record an exchange that timed out. * - * @param exchanger the exchanger + * @param exchanger the exchanger being recorded, tracked by identity */ public void recordTimeout(Exchanger exchanger) { timedOutExchangers.add(exchanger); @@ -74,7 +74,7 @@ public void recordTimeout(Exchanger exchanger) { /** * Record an exchange that was interrupted. * - * @param exchanger the exchanger + * @param exchanger the exchanger being recorded, tracked by identity */ public void recordInterrupted(Exchanger exchanger) { interruptedExchangers.add(exchanger); @@ -83,7 +83,7 @@ public void recordInterrupted(Exchanger exchanger) { /** * Analyze Exchanger usage and return report. * - * @return the analyze + * @return the findings this detector collected during the run */ public ExchangerReport analyze() { return new ExchangerReport( @@ -102,7 +102,14 @@ public static class ExchangerReport { private final Set> timedOutExchangers; private final Set> interruptedExchangers; private final int nullValueExchanges; - + /** + * Creates a ExchangerReport. + * + * @param exchangerRegistry every registered exchanger and what was observed on it + * @param timedOutExchangers the exchangers whose exchange timed out + * @param interruptedExchangers the exchangers whose exchange was interrupted + * @param nullValueExchanges the exchanges that transferred {@code null} + */ public ExchangerReport( Map, ExchangerInfo> exchangerRegistry, Set> timedOutExchangers, diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ExecutorDeadlockDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ExecutorDeadlockDetector.java index 0775a475..557597ec 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ExecutorDeadlockDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ExecutorDeadlockDetector.java @@ -33,11 +33,10 @@ private static class ExecutorState { /** * Registers executor for tracking. * - * @param executor the executor - * @param name the name - * @param maxThreads the max threads + * @param executor the executor being recorded, tracked by identity + * @param name a label identifying the executor in the report + * @param maxThreads the configured maximum thread count */ - public void registerExecutor(Object executor, String name, int maxThreads) { if (executor == null) { return; @@ -48,9 +47,8 @@ public void registerExecutor(Object executor, String name, int maxThreads) { /** * Records task submitted so it can be analysed at the end of the run. * - * @param executor the executor + * @param executor the executor being recorded, tracked by identity */ - public void recordTaskSubmitted(Object executor) { ExecutorState state = stateFor(executor); if (state != null) { @@ -60,9 +58,8 @@ public void recordTaskSubmitted(Object executor) { /** * Records task started so it can be analysed at the end of the run. * - * @param executor the executor + * @param executor the executor being recorded, tracked by identity */ - public void recordTaskStarted(Object executor) { ExecutorState state = stateFor(executor); if (state != null) { @@ -72,9 +69,8 @@ public void recordTaskStarted(Object executor) { /** * Records waiting on sibling so it can be analysed at the end of the run. * - * @param executor the executor + * @param executor the executor being recorded, tracked by identity */ - public void recordWaitingOnSibling(Object executor) { ExecutorState state = stateFor(executor); if (state != null) { @@ -84,9 +80,8 @@ public void recordWaitingOnSibling(Object executor) { /** * Records task completed so it can be analysed at the end of the run. * - * @param executor the executor + * @param executor the executor being recorded, tracked by identity */ - public void recordTaskCompleted(Object executor) { ExecutorState state = stateFor(executor); if (state != null) { @@ -100,9 +95,8 @@ public void recordTaskCompleted(Object executor) { /** * Analyses what has been recorded about the observation and builds the report for it. * - * @return the analyze + * @return the findings this detector collected during the run */ - public ExecutorDeadlockReport analyze() { ExecutorDeadlockReport report = new ExecutorDeadlockReport(); @@ -122,7 +116,7 @@ public ExecutorDeadlockReport analyze() { } public static class ExecutorDeadlockReport { - /** The self deadlocks. */ + /** Tasks that blocked waiting for another task on the same single-threaded executor. */ public final Set selfDeadlocks = new HashSet<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FalseSharingDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FalseSharingDetector.java index 387a5232..a46f5178 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FalseSharingDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FalseSharingDetector.java @@ -50,9 +50,9 @@ private static class AccessEvent { /** * Record a field access. Call this when a field is accessed in your test. * - * @param object the object - * @param fieldName the field name - * @param fieldType the field type + * @param object the object the access is on, tracked by identity + * @param fieldName the field involved, as it should appear in the report + * @param fieldType the declared type of the field */ public void recordFieldAccess(Object object, String fieldName, Class fieldType) { if (!enabled || object == null) return; @@ -77,7 +77,7 @@ public void recordFieldAccess(Object object, String fieldName, Class fieldTyp /** * Analyze for false sharing patterns. * - * @return the analyze false sharing + * @return the findings this detector collected during the run */ public FalseSharingReport analyzeFalseSharing() { FalseSharingReport report = new FalseSharingReport(); @@ -122,7 +122,7 @@ public FalseSharingReport analyzeFalseSharing() { /** * Standardized alias for {@link #analyzeFalseSharing()}. * - * @return the analyze + * @return the findings this detector collected during the run */ public FalseSharingReport analyze() { return analyzeFalseSharing(); @@ -176,7 +176,6 @@ private long getFieldSize(Class type) { /** * Clears recorded the observation so this instance can be reused for the next run. */ - public void reset() { fieldAccess.clear(); accessHistory.clear(); @@ -184,31 +183,37 @@ public void reset() { /** * Disable. */ - public void disable() { enabled = false; } /** * Enable. */ - public void enable() { enabled = true; } public static class FalseSharingReport { public static class ContentionPair { - /** The field 1. */ + /** First field of the contending pair. */ public final String field1; - /** The field 2. */ + /** Second field of the contending pair. */ public final String field2; - /** The distance in bytes. */ + /** Distance between the two fields; under a cache line means they share one. */ public final long distanceInBytes; - /** The accesses 1. */ + /** How many times the first field of the pair was accessed. */ public final long accesses1; - /** The accesses 2. */ + /** How many times the second field of the pair was accessed. */ public final long accesses2; - + /** + * Creates a ContentionPair. + * + * @param f1 the first field of the contending pair + * @param f2 the second field of the contending pair + * @param dist the distance between the two fields in bytes; under a cache line means they share one + * @param acc1 how many times the first field was accessed + * @param acc2 how many times the second field was accessed + */ public ContentionPair(String f1, String f2, long dist, long acc1, long acc2) { this.field1 = f1; this.field2 = f2; @@ -218,9 +223,9 @@ public ContentionPair(String f1, String f2, long dist, long acc1, long acc2) { } } - /** The false shared pairs. */ + /** Field pairs close enough to share a cache line and written from different threads. */ public final Set falseSharedPairs = new HashSet<>(); - /** The high contention fields. */ + /** Fields written often enough for cache-line sharing to matter. */ public final Set highContentionFields = new HashSet<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FileChannelPositionRaceDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FileChannelPositionRaceDetector.java index 36f57ee8..182e1211 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FileChannelPositionRaceDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FileChannelPositionRaceDetector.java @@ -119,9 +119,8 @@ private State stateFor(Object channel) { /** * Analyses what has been recorded about the observation and builds the report for it. * - * @return the analyze + * @return the findings this detector collected during the run */ - public Report analyze() { Report r = new Report(); for (State s : instances.values()) { @@ -151,9 +150,9 @@ public Report analyze() { } public static final class Report { - /** The violations. */ + /** Findings as human-readable lines, for the text report. */ public final List violations = new ArrayList<>(); - /** The structured violations. */ + /** The same findings as {@link se.deversity.asynctest.report.Violation} objects, for machine-readable reports. */ public final List structuredViolations = new ArrayList<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ForkJoinPoolDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ForkJoinPoolDetector.java index 6567385f..e060eaf1 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ForkJoinPoolDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ForkJoinPoolDetector.java @@ -24,9 +24,9 @@ public class ForkJoinPoolDetector { /** * Register a ForkJoinPool for monitoring. * - * @param pool the pool - * @param name the name - * @param parallelism the parallelism + * @param pool the pool being recorded, tracked by identity + * @param name a label identifying the pool in the report + * @param parallelism the configured parallelism of the pool */ public void registerPool(ForkJoinPool pool, String name, int parallelism) { poolRegistry.put(pool, new PoolInfo(name, parallelism)); @@ -35,9 +35,9 @@ public void registerPool(ForkJoinPool pool, String name, int parallelism) { /** * Record a task being forked. * - * @param pool the pool - * @param poolName the pool name - * @param taskName the task name + * @param pool the pool being recorded, tracked by identity + * @param poolName a label identifying the pool in the report + * @param taskName a label identifying the task in the report */ public void recordFork(ForkJoinPool pool, String poolName, String taskName) { PoolInfo info = poolRegistry.get(pool); @@ -49,9 +49,9 @@ public void recordFork(ForkJoinPool pool, String poolName, String taskName) { /** * Record a task being joined. * - * @param pool the pool - * @param poolName the pool name - * @param taskName the task name + * @param pool the pool being recorded, tracked by identity + * @param poolName a label identifying the pool in the report + * @param taskName a label identifying the task in the report */ public void recordJoin(ForkJoinPool pool, String poolName, String taskName) { PoolInfo info = poolRegistry.get(pool); @@ -63,8 +63,8 @@ public void recordJoin(ForkJoinPool pool, String poolName, String taskName) { /** * Record a task that was forked but never joined. * - * @param poolName the pool name - * @param taskName the task name + * @param poolName a label identifying the pool in the report + * @param taskName a label identifying the task in the report */ public void recordForkWithoutJoin(String poolName, String taskName) { forkedWithoutJoin.add(poolName + ":" + taskName); @@ -73,9 +73,9 @@ public void recordForkWithoutJoin(String poolName, String taskName) { /** * Record an exception in a forked task. * - * @param poolName the pool name - * @param taskName the task name - * @param t the t + * @param poolName a label identifying the pool in the report + * @param taskName a label identifying the task in the report + * @param t the throwable the task failed with */ public void recordException(String poolName, String taskName, Throwable t) { exceptionsInTasks.add(poolName + ":" + taskName + " (" + t.getClass().getSimpleName() + ")"); @@ -84,7 +84,7 @@ public void recordException(String poolName, String taskName, Throwable t) { /** * Record work stealing event. * - * @param pool the pool + * @param pool the pool being recorded, tracked by identity */ public void recordWorkSteal(ForkJoinPool pool) { taskStealCount++; @@ -93,8 +93,8 @@ public void recordWorkSteal(ForkJoinPool pool) { /** * Record task execution time. * - * @param pool the pool - * @param poolName the pool name + * @param pool the pool being recorded, tracked by identity + * @param poolName a label identifying the pool in the report * @param timeMs the time in milliseconds */ public void recordTaskTime(ForkJoinPool pool, String poolName, long timeMs) { @@ -107,7 +107,7 @@ public void recordTaskTime(ForkJoinPool pool, String poolName, long timeMs) { /** * Analyze ForkJoinPool usage and return report. * - * @return the analyze + * @return the findings this detector collected during the run */ public ForkJoinPoolReport analyze() { return new ForkJoinPoolReport( @@ -124,7 +124,13 @@ public static class ForkJoinPoolReport { private final Set forkedWithoutJoin; private final Set exceptionsInTasks; private final int taskStealCount; - + /** + * Creates a ForkJoinPoolReport. + * + * @param forkedWithoutJoin the tasks forked but never joined + * @param exceptionsInTasks the exceptions thrown inside pool tasks + * @param taskStealCount how many tasks were stolen between workers + */ public ForkJoinPoolReport( Set forkedWithoutJoin, Set exceptionsInTasks, diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ForkJoinTaskBlockingDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ForkJoinTaskBlockingDetector.java index a568b59d..d943d805 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ForkJoinTaskBlockingDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ForkJoinTaskBlockingDetector.java @@ -35,7 +35,7 @@ public class ForkJoinTaskBlockingDetector { /** * Call at the start of a {@code ForkJoinTask.compute()} or {@code exec()} body. * - * @param thread the thread + * @param thread the thread performing the operation */ public void recordForkJoinTaskEntered(Thread thread) { if (thread == null) return; @@ -45,7 +45,7 @@ public void recordForkJoinTaskEntered(Thread thread) { /** * Call at the end of a {@code ForkJoinTask.compute()} or {@code exec()} body. * - * @param thread the thread + * @param thread the thread performing the operation */ public void recordForkJoinTaskExited(Thread thread) { if (thread == null) return; diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FutureBlockingDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FutureBlockingDetector.java index 8e68227f..1f1a44fa 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FutureBlockingDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/FutureBlockingDetector.java @@ -34,7 +34,6 @@ private static class ExecutorState { /** * Disable. */ - public void disable() { enabled = false; } /** * Enable. @@ -43,11 +42,10 @@ private static class ExecutorState { /** * Registers executor for tracking. * - * @param executor the executor - * @param name the name - * @param maxThreads the max threads + * @param executor the executor being recorded, tracked by identity + * @param name a label identifying the executor in the report + * @param maxThreads the configured maximum thread count */ - public void registerExecutor(Object executor, String name, int maxThreads) { if (!enabled || executor == null) { return; @@ -58,9 +56,8 @@ public void registerExecutor(Object executor, String name, int maxThreads) { /** * Records task submitted so it can be analysed at the end of the run. * - * @param executor the executor + * @param executor the executor being recorded, tracked by identity */ - public void recordTaskSubmitted(Object executor) { ExecutorState state = stateFor(executor); if (state != null) { @@ -70,9 +67,8 @@ public void recordTaskSubmitted(Object executor) { /** * Records task started so it can be analysed at the end of the run. * - * @param executor the executor + * @param executor the executor being recorded, tracked by identity */ - public void recordTaskStarted(Object executor) { ExecutorState state = stateFor(executor); if (state != null) { @@ -82,9 +78,8 @@ public void recordTaskStarted(Object executor) { /** * Records blocking wait so it can be analysed at the end of the run. * - * @param executor the executor + * @param executor the executor being recorded, tracked by identity */ - public void recordBlockingWait(Object executor) { ExecutorState state = stateFor(executor); if (state != null) { @@ -94,9 +89,8 @@ public void recordBlockingWait(Object executor) { /** * Records task completed so it can be analysed at the end of the run. * - * @param executor the executor + * @param executor the executor being recorded, tracked by identity */ - public void recordTaskCompleted(Object executor) { ExecutorState state = stateFor(executor); if (state != null) { @@ -113,9 +107,8 @@ public void recordTaskCompleted(Object executor) { /** * Analyses what has been recorded about the observation and builds the report for it. * - * @return the analyze + * @return the findings this detector collected during the run */ - public FutureBlockingReport analyze() { FutureBlockingReport report = new FutureBlockingReport(); @@ -136,7 +129,7 @@ public FutureBlockingReport analyze() { } public static class FutureBlockingReport { - /** The starvation risks. */ + /** Blocking calls made from a pool thread, which can exhaust the pool. */ public final Set starvationRisks = new HashSet<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/HighContentionAtomicDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/HighContentionAtomicDetector.java index 9ad898cc..e370158e 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/HighContentionAtomicDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/HighContentionAtomicDetector.java @@ -191,9 +191,9 @@ public Report analyze() { /** Report produced by {@link #analyze()}. */ public static final class Report { - /** The violations. */ + /** Findings as human-readable lines, for the text report. */ public final List violations = new ArrayList<>(); - /** The structured violations. */ + /** The same findings as {@link se.deversity.asynctest.report.Violation} objects, for machine-readable reports. */ public final List structuredViolations = new ArrayList<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/HttpClientConcurrencyDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/HttpClientConcurrencyDetector.java index 0bf0b22a..5b4ffa8b 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/HttpClientConcurrencyDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/HttpClientConcurrencyDetector.java @@ -212,7 +212,7 @@ public static class HttpClientConcurrencyReport { /** * Check if any issues were detected. * - * @return the has issues + * @return {@code true} when this detector recorded something worth reporting */ public boolean hasIssues() { return !pendingRequests.isEmpty() || diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/InterruptMonitor.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/InterruptMonitor.java index 0ea96ac0..21d5e494 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/InterruptMonitor.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/InterruptMonitor.java @@ -34,9 +34,8 @@ private static class InterruptEvent { /** * Records interrupt exception so it can be analysed at the end of the run. * - * @param ex the ex + * @param ex the interruption that ended the wait */ - public void recordInterruptException(InterruptedException ex) { if (!enabled) { return; @@ -57,7 +56,6 @@ public void recordInterruptException(InterruptedException ex) { /** * Records interrupt restored so it can be analysed at the end of the run. */ - public void recordInterruptRestored() { if (!enabled) { return; @@ -77,9 +75,8 @@ public void recordInterruptRestored() { /** * Records ignored exception so it can be analysed at the end of the run. * - * @param description the description + * @param description free text describing the event, shown in the report */ - public void recordIgnoredException(String description) { if (!enabled) { return; @@ -95,9 +92,8 @@ public void recordIgnoredException(String description) { /** * Records blocking operation without interrupt handling so it can be analysed at the end of the run. * - * @param operationName the operation name + * @param operationName a label identifying the operation in the report */ - public void recordBlockingOperationWithoutInterruptHandling(String operationName) { if (!enabled) { return; @@ -118,9 +114,8 @@ private String inferCallSite() { /** * Analyses what has been recorded about interrupt handling and builds the report for it. * - * @return the analyze interrupt handling + * @return the findings this detector collected during the run */ - public InterruptReport analyzeInterruptHandling() { InterruptReport report = new InterruptReport(); @@ -166,7 +161,7 @@ public InterruptReport analyzeInterruptHandling() { /** * Standardized alias for {@link #analyzeInterruptHandling()}. * - * @return the analyze + * @return the findings this detector collected during the run */ public InterruptReport analyze() { return analyzeInterruptHandling(); @@ -174,7 +169,6 @@ public InterruptReport analyze() { /** * Clears recorded the observation so this instance can be reused for the next run. */ - public void reset() { synchronized (interruptEvents) { interruptEvents.clear(); @@ -185,24 +179,22 @@ public void reset() { /** * Disable. */ - public void disable() { enabled = false; } /** * Enable. */ - public void enable() { enabled = true; } public static class InterruptReport { - /** The ignored interrupts. */ + /** Interrupts caught and discarded without restoring the flag. */ public final Set ignoredInterrupts = new HashSet<>(); - /** The repeated ignored interrupts. */ + /** Threads that ignored an interrupt more than once. */ public final Set repeatedIgnoredInterrupts = new HashSet<>(); - /** The blocking without handling. */ + /** Blocking calls made without handling {@code InterruptedException}. */ public final Set blockingWithoutHandling = new HashSet<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/JdbcConnectionSharedDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/JdbcConnectionSharedDetector.java index d92167ed..b43254f2 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/JdbcConnectionSharedDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/JdbcConnectionSharedDetector.java @@ -106,9 +106,8 @@ public void recordAccess(Object resource, String name, Thread thread) { /** * Analyses what has been recorded about the observation and builds the report for it. * - * @return the analyze + * @return the findings this detector collected during the run */ - public Report analyze() { Report r = new Report(); for (State s : instances.values()) { @@ -149,9 +148,9 @@ public Report analyze() { } public static final class Report { - /** The violations. */ + /** Findings as human-readable lines, for the text report. */ public final List violations = new ArrayList<>(); - /** The structured violations. */ + /** The same findings as {@link se.deversity.asynctest.report.Violation} objects, for machine-readable reports. */ public final List structuredViolations = new ArrayList<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LatchMisuseDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LatchMisuseDetector.java index 0d94fe3e..0cf6bed4 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LatchMisuseDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LatchMisuseDetector.java @@ -32,11 +32,10 @@ private static class LatchState { /** * Registers latch for tracking. * - * @param latch the latch - * @param name the name - * @param initialCount the initial count + * @param latch the latch being recorded, tracked by identity + * @param name a label identifying the latch in the report + * @param initialCount the count the latch was created with */ - public void registerLatch(Object latch, String name, int initialCount) { if (latch == null) { return; @@ -47,9 +46,8 @@ public void registerLatch(Object latch, String name, int initialCount) { /** * Records await so it can be analysed at the end of the run. * - * @param latch the latch + * @param latch the latch being recorded, tracked by identity */ - public void recordAwait(Object latch) { LatchState state = stateFor(latch); if (state != null) { @@ -59,9 +57,8 @@ public void recordAwait(Object latch) { /** * Records count down so it can be analysed at the end of the run. * - * @param latch the latch + * @param latch the latch being recorded, tracked by identity */ - public void recordCountDown(Object latch) { LatchState state = stateFor(latch); if (state != null) { @@ -75,9 +72,8 @@ public void recordCountDown(Object latch) { /** * Analyses what has been recorded about the observation and builds the report for it. * - * @return the analyze + * @return the findings this detector collected during the run */ - public LatchMisuseReport analyze() { LatchMisuseReport report = new LatchMisuseReport(); @@ -105,9 +101,9 @@ public LatchMisuseReport analyze() { } public static class LatchMisuseReport { - /** The missing count downs. */ + /** Latches never counted down to zero. */ public final Set missingCountDowns = new HashSet<>(); - /** The extra count downs. */ + /** Latches counted down more times than they were created for. */ public final Set extraCountDowns = new HashSet<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LazyInitRaceDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LazyInitRaceDetector.java index d453653e..0ef9abc9 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LazyInitRaceDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LazyInitRaceDetector.java @@ -109,7 +109,7 @@ public void recordInitialization(String fieldId) { * Analyses recorded initialization data and returns a report of fields * where duplicate initialization was detected. * - * @return the analyze + * @return the findings this detector collected during the run */ public LazyInitRaceReport analyze() { LazyInitRaceReport report = new LazyInitRaceReport(); @@ -158,7 +158,7 @@ public static class LazyInitRaceReport { /** * Returns {@code true} when any lazy-init race or visibility risk was detected. * - * @return the has issues + * @return {@code true} when this detector recorded something worth reporting */ public boolean hasIssues() { return !races.isEmpty() || !visibilityRisks.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LazyInitValidator.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LazyInitValidator.java index e83fedb6..c136fc7c 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LazyInitValidator.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LazyInitValidator.java @@ -30,13 +30,12 @@ private static class LazyFieldState { /** * Records access so it can be analysed at the end of the run. * - * @param fieldName the field name - * @param observedNull the observed null - * @param initializedValue the initialized value - * @param synchronizedAccess the synchronized access - * @param volatileField the volatile field + * @param fieldName the field involved, as it should appear in the report + * @param observedNull {@code true} when the reading thread saw {@code null} + * @param initializedValue {@code true} when the value had already been initialised + * @param synchronizedAccess {@code true} when the access was made while holding the lock + * @param volatileField {@code true} when the field is declared {@code volatile} */ - public void recordAccess(String fieldName, boolean observedNull, boolean initializedValue, boolean synchronizedAccess, boolean volatileField) { if (!enabled || fieldName == null || fieldName.isBlank()) { @@ -68,9 +67,8 @@ public void recordAccess(String fieldName, boolean observedNull, boolean initial /** * Analyses what has been recorded about the observation and builds the report for it. * - * @return the analyze + * @return the findings this detector collected during the run */ - public LazyInitReport analyze() { LazyInitReport report = new LazyInitReport(); @@ -99,15 +97,14 @@ public LazyInitReport analyze() { /** * Clears recorded the observation so this instance can be reused for the next run. */ - public void reset() { fields.clear(); } public static class LazyInitReport { - /** The multiple initializations. */ + /** Fields initialised more than once because the guard was not atomic. */ public final Set multipleInitializations = new HashSet<>(); - /** The unsafe publication. */ + /** Fields published without the ordering a reader would need to see them fully. */ public final Set unsafePublication = new HashSet<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LivelockDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LivelockDetector.java index 89e19c5a..a8dfb9c7 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LivelockDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LivelockDetector.java @@ -86,7 +86,7 @@ public void captureSnapshot() { /** * Analyze captured snapshots for livelock and starvation patterns. * - * @return the analyze livelocks + * @return the findings this detector collected during the run */ public LivelockReport analyzeLivelocks() { LivelockReport report = new LivelockReport(); @@ -119,7 +119,7 @@ public LivelockReport analyzeLivelocks() { /** * Standardized alias for {@link #analyzeLivelocks()}. * - * @return the analyze + * @return the findings this detector collected during the run */ public LivelockReport analyze() { return analyzeLivelocks(); @@ -178,7 +178,6 @@ private boolean madeProgress(List snapshots) { /** * Clears recorded the observation so this instance can be reused for the next run. */ - public void reset() { threadHistory.clear(); observedThreads.clear(); @@ -186,24 +185,22 @@ public void reset() { /** * Disable. */ - public void disable() { enabled = false; } /** * Enable. */ - public void enable() { enabled = true; } public static class LivelockReport { - /** The starved threads. */ + /** Threads that never got to run during the observation window. */ public final Set starvedThreads = new HashSet<>(); - /** The livelock candidates. */ + /** Threads that kept running while making no progress. */ public final Set livelockCandidates = new HashSet<>(); - /** The no progress threads. */ + /** Threads that reported no progress for the whole observation window. */ public final Set noProgressThreads = new HashSet<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockContentionDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockContentionDetector.java index addcdd19..44164bfa 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockContentionDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockContentionDetector.java @@ -120,7 +120,7 @@ public void recordReleased(Object monitor, String name) { /** * Analyses recorded data and returns a contention report. * - * @return the analyze + * @return the findings this detector collected during the run */ public LockContentionReport analyze() { LockContentionReport report = new LockContentionReport(); @@ -163,7 +163,7 @@ public static class LockContentionReport { /** * Returns {@code true} when any monitor exceeds the contention threshold. * - * @return the has issues + * @return {@code true} when this detector recorded something worth reporting */ public boolean hasIssues() { return !hotLocks.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockLeakDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockLeakDetector.java index 3f5f7c5a..a66380fd 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockLeakDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockLeakDetector.java @@ -79,7 +79,7 @@ public void registerLock(Lock lock, String name) { /** * Record that a lock was acquired. * - * @param lock the Lock + * @param lock the lock being recorded, tracked by identity rather than equality * @param name the lock name (should match registration) */ public void recordLockAcquired(Lock lock, String name) { @@ -102,7 +102,7 @@ public void recordLockAcquired(Lock lock, String name) { /** * Record that a lock was released. * - * @param lock the Lock + * @param lock the lock being recorded, tracked by identity rather than equality * @param name the lock name (should match registration) */ public void recordLockReleased(Lock lock, String name) { @@ -190,7 +190,7 @@ public static class LockLeakReport { /** * Check if any issues were detected. * - * @return the has issues + * @return {@code true} when this detector recorded something worth reporting */ public boolean hasIssues() { return !lockLeaks.isEmpty() || !heldLocks.isEmpty() || !excessiveHoldTimes.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockOrderValidator.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockOrderValidator.java index 9de60d11..c85b61cd 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockOrderValidator.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockOrderValidator.java @@ -43,7 +43,7 @@ private static class LockSequence { /** * Record a lock acquisition. * - * @param lock the lock + * @param lock the lock being recorded, tracked by identity rather than equality */ public void recordLockAcquisition(Object lock) { if (!enabled || lock == null) return; @@ -70,7 +70,7 @@ public void recordLockAcquisition(Object lock) { /** * Record lock release. * - * @param lock the lock + * @param lock the lock being recorded, tracked by identity rather than equality */ public void recordLockRelease(Object lock) { if (!enabled || lock == null) return; @@ -90,7 +90,7 @@ public void recordLockRelease(Object lock) { /** * Validate lock ordering consistency. * - * @return the validate lock order + * @return the findings this detector collected during the run */ public LockOrderReport validateLockOrder() { LockOrderReport report = new LockOrderReport(); @@ -171,29 +171,26 @@ private boolean hasCycle(String node, Map> graph, /** * Clears recorded the observation so this instance can be reused for the next run. */ - public void reset() { threadLockOrders.clear(); } /** * Disable. */ - public void disable() { enabled = false; } /** * Enable. */ - public void enable() { enabled = true; } public static class LockOrderReport { - /** The inconsistent orderings. */ + /** Lock pairs acquired in one order by one thread and the reverse by another. */ public final Set inconsistentOrderings = new HashSet<>(); - /** The potential deadlock cycles. */ + /** Cycles in the observed lock-acquisition graph. */ public final Set potentialDeadlockCycles = new HashSet<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockUpgradeDeadlockDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockUpgradeDeadlockDetector.java index 6f936a3e..c6ce28e4 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockUpgradeDeadlockDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/LockUpgradeDeadlockDetector.java @@ -39,9 +39,9 @@ private static final class State { /** * Record acquisition of a read lock. * - * @param lock the lock - * @param lockName the lock name - * @param thread the thread + * @param lock the lock being recorded, tracked by identity rather than equality + * @param lockName a label identifying the lock in the report + * @param thread the thread performing the operation */ public void recordReadLockAcquired(ReentrantReadWriteLock lock, String lockName, Thread thread) { if (lock == null || thread == null) return; @@ -52,8 +52,8 @@ public void recordReadLockAcquired(ReentrantReadWriteLock lock, String lockName, /** * Record release of a read lock. * - * @param lock the lock - * @param thread the thread + * @param lock the lock being recorded, tracked by identity rather than equality + * @param thread the thread performing the operation */ public void recordReadLockReleased(ReentrantReadWriteLock lock, Thread thread) { if (lock == null || thread == null) return; @@ -67,9 +67,9 @@ public void recordReadLockReleased(ReentrantReadWriteLock lock, Thread thread) { /** * Record attempt to acquire a write lock. * - * @param lock the lock - * @param lockName the lock name - * @param thread the thread + * @param lock the lock being recorded, tracked by identity rather than equality + * @param lockName a label identifying the lock in the report + * @param thread the thread performing the operation */ public void recordWriteLockAcquisitionAttempt(ReentrantReadWriteLock lock, String lockName, Thread thread) { if (lock == null || thread == null) return; @@ -85,9 +85,8 @@ public void recordWriteLockAcquisitionAttempt(ReentrantReadWriteLock lock, Strin /** * Analyses what has been recorded about the observation and builds the report for it. * - * @return the analyze + * @return the findings this detector collected during the run */ - public Report analyze() { Report r = new Report(); for (State s : violations.values()) { @@ -112,9 +111,9 @@ public Report analyze() { } public static final class Report { - /** The violations. */ + /** Findings as human-readable lines, for the text report. */ public final List violations = new ArrayList<>(); - /** The structured violations. */ + /** The same findings as {@link se.deversity.asynctest.report.Violation} objects, for machine-readable reports. */ public final List structuredViolations = new ArrayList<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MemoryModelValidator.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MemoryModelValidator.java index 804f4a97..d6f2c349 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MemoryModelValidator.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MemoryModelValidator.java @@ -26,7 +26,7 @@ public class MemoryModelValidator { * Run comprehensive JMM validation on the test framework. * This checks that all internal state transitions are properly synchronized. * - * @return the validate + * @return the findings this detector collected during the run */ public ValidationResult validate() { ValidationResult result = new ValidationResult(); @@ -169,11 +169,11 @@ private void testAtomicVisibility(ValidationResult result) { } public static class ValidationResult { - /** The tests run. */ + /** How many ordering checks were run. */ public int testsRun = 0; - /** The tests passed. */ + /** How many of the ordering checks held. */ public int testsPassed = 0; - /** The observations. */ + /** Every recorded observation, in the order it was made. */ public final List observations = Collections.synchronizedList(new ArrayList<>()); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MemoryOrderingMonitor.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MemoryOrderingMonitor.java index 0e2e2bdd..9e1fefbe 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MemoryOrderingMonitor.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MemoryOrderingMonitor.java @@ -39,8 +39,8 @@ private static class MemoryAccess { /** * Record a memory read. * - * @param location the location - * @param value the value + * @param location where in the code this happened, shown in the report + * @param value the value read or written */ public void recordRead(String location, Object value) { if (!enabled) return; @@ -50,8 +50,8 @@ public void recordRead(String location, Object value) { /** * Record a memory write. * - * @param location the location - * @param value the value + * @param location where in the code this happened, shown in the report + * @param value the value read or written */ public void recordWrite(String location, Object value) { if (!enabled) return; @@ -61,7 +61,7 @@ public void recordWrite(String location, Object value) { /** * Analyze for memory ordering violations. * - * @return the analyze ordering + * @return the findings this detector collected during the run */ public MemoryOrderingReport analyzeOrdering() { MemoryOrderingReport report = new MemoryOrderingReport(); @@ -110,7 +110,7 @@ public MemoryOrderingReport analyzeOrdering() { /** * Standardized alias for {@link #analyzeOrdering()}. * - * @return the analyze + * @return the findings this detector collected during the run */ public MemoryOrderingReport analyze() { return analyzeOrdering(); @@ -118,21 +118,18 @@ public MemoryOrderingReport analyze() { /** * Clears recorded the observation so this instance can be reused for the next run. */ - public void reset() { accessLog.clear(); } /** * Disable. */ - public void disable() { enabled = false; } /** * Enable. */ - public void enable() { enabled = true; } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MissedSignalDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MissedSignalDetector.java index 5a2844a2..50e89a84 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MissedSignalDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/MissedSignalDetector.java @@ -119,7 +119,7 @@ public void recordNotifyAll(String conditionName) { * Analyses recorded signal/wait data and returns a report of conditions * that suffered missed signals. * - * @return the analyze + * @return the findings this detector collected during the run */ public MissedSignalReport analyze() { MissedSignalReport report = new MissedSignalReport(); @@ -155,7 +155,7 @@ public static class MissedSignalReport { /** * Returns {@code true} when at least one condition suffered a missed signal. * - * @return the has issues + * @return {@code true} when this detector recorded something worth reporting */ public boolean hasIssues() { return !missedConditions.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NonAtomicConcurrentMapUpdateDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NonAtomicConcurrentMapUpdateDetector.java index 7df56aaa..2b46b48c 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NonAtomicConcurrentMapUpdateDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NonAtomicConcurrentMapUpdateDetector.java @@ -105,9 +105,8 @@ public void recordCheckThenAct(ConcurrentMap map, Object key, String opera /** * Analyses what has been recorded about the observation and builds the report for it. * - * @return the analyze + * @return the findings this detector collected during the run */ - public Report analyze() { Report r = new Report(); for (State s : sites.values()) { @@ -138,9 +137,9 @@ public Report analyze() { } public static final class Report { - /** The violations. */ + /** Findings as human-readable lines, for the text report. */ public final List violations = new ArrayList<>(); - /** The structured violations. */ + /** The same findings as {@link se.deversity.asynctest.report.Violation} objects, for machine-readable reports. */ public final List structuredViolations = new ArrayList<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NotifyAllValidator.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NotifyAllValidator.java index 5a53cf30..e857d2e0 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NotifyAllValidator.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NotifyAllValidator.java @@ -35,10 +35,9 @@ private static class MonitorState { /** * Records waiter added so it can be analysed at the end of the run. * - * @param monitor the monitor - * @param monitorName the monitor name + * @param monitor the object being used as a monitor, tracked by identity + * @param monitorName a label identifying the monitor in the report */ - public void recordWaiterAdded(Object monitor, String monitorName) { if (!enabled || monitor == null) { return; @@ -56,9 +55,8 @@ public void recordWaiterAdded(Object monitor, String monitorName) { /** * Records waiter released so it can be analysed at the end of the run. * - * @param monitor the monitor + * @param monitor the object being used as a monitor, tracked by identity */ - public void recordWaiterReleased(Object monitor) { if (!enabled || monitor == null) { return; @@ -72,10 +70,9 @@ public void recordWaiterReleased(Object monitor) { /** * Records notify so it can be analysed at the end of the run. * - * @param monitor the monitor - * @param notifyAll the notify all + * @param monitor the object being used as a monitor, tracked by identity + * @param notifyAll {@code true} when {@code notifyAll} was called rather than {@code notify} */ - public void recordNotify(Object monitor, boolean notifyAll) { if (!enabled || monitor == null) { return; @@ -101,9 +98,8 @@ public void recordNotify(Object monitor, boolean notifyAll) { /** * Analyses what has been recorded about the observation and builds the report for it. * - * @return the analyze + * @return the findings this detector collected during the run */ - public NotifyAllReport analyze() { NotifyAllReport report = new NotifyAllReport(); @@ -132,27 +128,24 @@ public NotifyAllReport analyze() { /** * Clears recorded the observation so this instance can be reused for the next run. */ - public void reset() { monitors.clear(); } /** * Disable. */ - public void disable() { enabled = false; } /** * Enable. */ - public void enable() { enabled = true; } public static class NotifyAllReport { - /** The notify instead of notify all. */ + /** Monitors signalled with {@code notify} where waiters await different conditions. */ public final Set notifyInsteadOfNotifyAll = new HashSet<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NotifyWithoutMonitorDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NotifyWithoutMonitorDetector.java index 64063a23..41787ea1 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NotifyWithoutMonitorDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/NotifyWithoutMonitorDetector.java @@ -93,7 +93,7 @@ public void recordNotifyAttempt(Object monitor, String label) { /** * Report produced by {@link #analyze()}. * - * @return the analyze + * @return the findings this detector collected during the run */ public Report analyze() { Report r = new Report(); @@ -123,9 +123,9 @@ public Report analyze() { /** Report. */ public static final class Report { - /** The violations. */ + /** Findings as human-readable lines, for the text report. */ public final List violations = new ArrayList<>(); - /** The structured violations. */ + /** The same findings as {@link se.deversity.asynctest.report.Violation} objects, for machine-readable reports. */ public final List structuredViolations = new ArrayList<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/OptimisticReadValidationDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/OptimisticReadValidationDetector.java index 1f4ee17a..96d1a6e3 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/OptimisticReadValidationDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/OptimisticReadValidationDetector.java @@ -56,9 +56,9 @@ private static String key(Object lock, Thread thread) { /** * Call immediately after {@code StampedLock.tryOptimisticRead()}. * - * @param lock the lock - * @param stamp the stamp - * @param thread the thread + * @param lock the lock being recorded, tracked by identity rather than equality + * @param stamp the stamp returned by the {@code StampedLock} operation + * @param thread the thread performing the operation */ public void recordOptimisticReadStarted(Object lock, long stamp, Thread thread) { if (lock == null || thread == null) return; @@ -78,9 +78,9 @@ public void recordOptimisticReadStarted(Object lock, long stamp, Thread thread) * * @param fieldName descriptive name for the data being read (for reports) * - * @param lock the lock - * @param stamp the stamp - * @param thread the thread + * @param lock the lock being recorded, tracked by identity rather than equality + * @param stamp the stamp returned by the {@code StampedLock} operation + * @param thread the thread performing the operation */ public void recordDataAccessed(Object lock, long stamp, Thread thread, String fieldName) { if (lock == null || thread == null) return; @@ -95,9 +95,9 @@ public void recordDataAccessed(Object lock, long stamp, Thread thread, String fi * * @param result the boolean returned by {@code validate()} * - * @param lock the lock - * @param stamp the stamp - * @param thread the thread + * @param lock the lock being recorded, tracked by identity rather than equality + * @param stamp the stamp returned by the {@code StampedLock} operation + * @param thread the thread performing the operation */ public void recordValidateCalled(Object lock, long stamp, boolean result, Thread thread) { if (lock == null || thread == null) return; diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ParallelStreamDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ParallelStreamDetector.java index 3f7e7ba9..12c2bed3 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ParallelStreamDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ParallelStreamDetector.java @@ -74,7 +74,7 @@ public void recordParallelStream(String streamName) { /** * Record a stateful lambda operation (bug in parallel stream). * - * @param streamName the stream name + * @param streamName a label identifying the stream in the report * @param operation the operation type (forEach, map, filter, etc.) */ public void recordStatefulOperation(String streamName, String operation) { @@ -84,7 +84,7 @@ public void recordStatefulOperation(String streamName, String operation) { /** * Record a stateless operation (safe in parallel stream). * - * @param streamName the stream name + * @param streamName a label identifying the stream in the report * @param operation the operation type */ public void recordStatelessOperation(String streamName, String operation) { @@ -94,7 +94,7 @@ public void recordStatelessOperation(String streamName, String operation) { /** * Record use of a non-thread-safe collector. * - * @param streamName the stream name + * @param streamName a label identifying the stream in the report * @param collectorType the collector type (ArrayList, HashMap, etc.) */ public void recordNonThreadSafeCollector(String streamName, String collectorType) { @@ -111,7 +111,7 @@ public void recordNonThreadSafeCollector(String streamName, String collectorType /** * Record side effects in parallel stream (bug). * - * @param streamName the stream name + * @param streamName a label identifying the stream in the report * @param sideEffectType the type of side effect */ public void recordSideEffect(String streamName, String sideEffectType) { @@ -218,7 +218,7 @@ public static class ParallelStreamReport { /** * Check if any issues were detected. * - * @return the has issues + * @return {@code true} when this detector recorded something worth reporting */ public boolean hasIssues() { return !statefulLambdas.isEmpty() || !nonThreadSafeCollectors.isEmpty() || !sideEffects.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/Phase1DetectorSet.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/Phase1DetectorSet.java index 45a06579..6a4e31a5 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/Phase1DetectorSet.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/Phase1DetectorSet.java @@ -21,19 +21,19 @@ */ public final class Phase1DetectorSet { - /** The visibility. */ + /** The visibility monitor for this run, or {@code null} when it is disabled. */ public final @Nullable VisibilityMonitor visibility; - /** The livelock. */ + /** The livelock detector for this run, or {@code null} when it is disabled. */ public final @Nullable LivelockDetector livelock; - /** The race. */ + /** The race-condition detector for this run, or {@code null} when it is disabled. */ public final @Nullable RaceConditionDetector race; - /** The thread local. */ + /** The thread-local monitor for this run, or {@code null} when it is disabled. */ public final @Nullable ThreadLocalMonitor threadLocal; - /** The busy wait. */ + /** The busy-wait detector for this run, or {@code null} when it is disabled. */ public final @Nullable BusyWaitDetector busyWait; - /** The atomicity. */ + /** The atomicity validator for this run, or {@code null} when it is disabled. */ public final @Nullable AtomicityValidator atomicity; - /** The interrupt. */ + /** The interrupt monitor for this run, or {@code null} when it is disabled. */ public final @Nullable InterruptMonitor interrupt; /** @@ -78,8 +78,8 @@ private Phase1DetectorSet(@Nullable VisibilityMonitor visibility, * available (the runner always has one) — this overload always constructs fresh, * disconnected instances and is kept only for direct/unit-test construction. * - * @param config the config - * @return the from + * @param config the resolved configuration for this run + * @return the detector set matching that configuration */ public static Phase1DetectorSet from(AsyncTestConfig config) { return from(config, null); @@ -106,9 +106,9 @@ public static Phase1DetectorSet from(AsyncTestConfig config) { * * @since 1.7.0 * - * @param config the config - * @param ctx the ctx - * @return the from + * @param config the resolved configuration for this run + * @param ctx the context this detector reports into + * @return the detector set matching that configuration */ public static Phase1DetectorSet from(AsyncTestConfig config, @Nullable AsyncTestContext ctx) { return new Phase1DetectorSet( @@ -170,7 +170,7 @@ public void printReports() { * * @since 1.7.0 * - * @return the collect reports + * @return the reports collected from each detector, keyed by detector name */ public Map collectReports() { Map out = new LinkedHashMap<>(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/PhaserDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/PhaserDetector.java index 96e4ff9a..2879f8fc 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/PhaserDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/PhaserDetector.java @@ -24,9 +24,9 @@ public class PhaserDetector { /** * Register a Phaser for monitoring. * - * @param phaser the phaser - * @param name the name - * @param parties the parties + * @param phaser the phaser being recorded, tracked by identity + * @param name a label identifying the phaser in the report + * @param parties the number of parties the barrier was created for */ public void registerPhaser(Phaser phaser, String name, int parties) { phaserRegistry.put(phaser, new PhaserInfo(name, parties)); @@ -35,7 +35,7 @@ public void registerPhaser(Phaser phaser, String name, int parties) { /** * Record a thread arriving at the phaser. * - * @param phaser the phaser + * @param phaser the phaser being recorded, tracked by identity */ public void recordArrive(Phaser phaser) { PhaserInfo info = phaserRegistry.get(phaser); @@ -47,7 +47,7 @@ public void recordArrive(Phaser phaser) { /** * Record a thread arriving and awaiting advance. * - * @param phaser the phaser + * @param phaser the phaser being recorded, tracked by identity */ public void recordArriveAwaitAdvance(Phaser phaser) { PhaserInfo info = phaserRegistry.get(phaser); @@ -60,7 +60,7 @@ public void recordArriveAwaitAdvance(Phaser phaser) { /** * Record a phaser await that timed out. * - * @param phaser the phaser + * @param phaser the phaser being recorded, tracked by identity */ public void recordTimeout(Phaser phaser) { timedOutPhasers.add(phaser); @@ -69,7 +69,7 @@ public void recordTimeout(Phaser phaser) { /** * Record a phaser that was terminated. * - * @param phaser the phaser + * @param phaser the phaser being recorded, tracked by identity */ public void recordTermination(Phaser phaser) { terminatedPhasers.add(phaser); @@ -78,8 +78,8 @@ public void recordTermination(Phaser phaser) { /** * Record successful phaser phase completion. * - * @param phaser the phaser - * @param phase the phase + * @param phaser the phaser being recorded, tracked by identity + * @param phase the phase number this event belongs to */ public void recordPhaseComplete(Phaser phaser, int phase) { PhaserInfo info = phaserRegistry.get(phaser); @@ -91,7 +91,7 @@ public void recordPhaseComplete(Phaser phaser, int phase) { /** * Analyze phaser usage and return report. * - * @return the analyze + * @return the findings this detector collected during the run */ public PhaserReport analyze() { return new PhaserReport( @@ -108,7 +108,13 @@ public static class PhaserReport { private final Map phaserRegistry; private final Set timedOutPhasers; private final Set terminatedPhasers; - + /** + * Creates a PhaserReport. + * + * @param phaserRegistry every registered phaser and what was observed on it + * @param timedOutPhasers the phasers whose await timed out + * @param terminatedPhasers the phasers that reached termination + */ public PhaserReport( Map phaserRegistry, Set timedOutPhasers, diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/PipelineMonitor.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/PipelineMonitor.java index 96e7795e..1439d88e 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/PipelineMonitor.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/PipelineMonitor.java @@ -39,7 +39,7 @@ private static class PipelineStage { /** * Register a pipeline stage. * - * @param stageName the stage name + * @param stageName a label identifying the stage in the report */ public void registerStage(String stageName) { stages.putIfAbsent(stageName, new PipelineStage(stageName)); @@ -48,8 +48,8 @@ public void registerStage(String stageName) { /** * Record event published to a stage. * - * @param stageName the stage name - * @param eventId the event id + * @param stageName a label identifying the stage in the report + * @param eventId correlates the calls belonging to one event */ public void recordEventPublished(String stageName, String eventId) { if (!enabled) return; @@ -62,8 +62,8 @@ public void recordEventPublished(String stageName, String eventId) { /** * Record event processed by a stage. * - * @param stageName the stage name - * @param eventId the event id + * @param stageName a label identifying the stage in the report + * @param eventId correlates the calls belonging to one event */ public void recordEventProcessed(String stageName, String eventId) { if (!enabled) return; @@ -78,9 +78,9 @@ public void recordEventProcessed(String stageName, String eventId) { /** * Record event failure. * - * @param stageName the stage name - * @param eventId the event id - * @param reason the reason + * @param stageName a label identifying the stage in the report + * @param eventId correlates the calls belonging to one event + * @param reason why the event was recorded, shown in the report */ public void recordEventFailed(String stageName, String eventId, String reason) { if (!enabled) return; @@ -96,7 +96,7 @@ public void recordEventFailed(String stageName, String eventId, String reason) { /** * Analyze pipeline for signal loss. * - * @return the analyze pipeline + * @return the findings this detector collected during the run */ public PipelineReport analyzePipeline() { PipelineReport report = new PipelineReport(); @@ -132,7 +132,7 @@ public PipelineReport analyzePipeline() { /** * Standardized alias for {@link #analyzePipeline()}. * - * @return the analyze + * @return the findings this detector collected during the run */ public PipelineReport analyze() { return analyzePipeline(); @@ -140,7 +140,6 @@ public PipelineReport analyze() { /** * Clears recorded the observation so this instance can be reused for the next run. */ - public void reset() { stages.clear(); eventLog.clear(); @@ -148,24 +147,22 @@ public void reset() { /** * Disable. */ - public void disable() { enabled = false; } /** * Enable. */ - public void enable() { enabled = true; } public static class PipelineReport { - /** The missing events. */ + /** Events entering a stage but never leaving it. */ public final Set missingEvents = new HashSet<>(); - /** The failed events. */ + /** Events that failed, keyed by the stage they failed in. */ public final Map> failedEvents = new HashMap<>(); - /** The low processing rate. */ + /** Stages that processed events more slowly than the threshold. */ public final Set lowProcessingRate = new HashSet<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/RaceConditionDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/RaceConditionDetector.java index 803f56e6..d3fe0d5b 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/RaceConditionDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/RaceConditionDetector.java @@ -43,10 +43,9 @@ private static class ObjectFieldState { /** * Records field read so it can be analysed at the end of the run. * - * @param object the object - * @param fieldName the field name + * @param object the object the access is on, tracked by identity + * @param fieldName the field involved, as it should appear in the report */ - public void recordFieldRead(Object object, String fieldName) { if (!enabled || object == null || fieldName == null || fieldName.isBlank()) { return; @@ -56,10 +55,9 @@ public void recordFieldRead(Object object, String fieldName) { /** * Records field write so it can be analysed at the end of the run. * - * @param object the object - * @param fieldName the field name + * @param object the object the access is on, tracked by identity + * @param fieldName the field involved, as it should appear in the report */ - public void recordFieldWrite(Object object, String fieldName) { if (!enabled || object == null || fieldName == null || fieldName.isBlank()) { return; @@ -80,9 +78,8 @@ private void recordAccess(Object object, String fieldName, boolean write) { /** * Analyses what has been recorded about race conditions and builds the report for it. * - * @return the analyze race conditions + * @return the findings this detector collected during the run */ - public RaceConditionReport analyzeRaceConditions() { RaceConditionReport report = new RaceConditionReport(); @@ -162,7 +159,7 @@ public RaceConditionReport analyzeRaceConditions() { /** * Standardized alias for {@link #analyzeRaceConditions()}. * - * @return the analyze + * @return the findings this detector collected during the run */ public RaceConditionReport analyze() { return analyzeRaceConditions(); @@ -170,7 +167,6 @@ public RaceConditionReport analyze() { /** * Clears recorded the observation so this instance can be reused for the next run. */ - public void reset() { objects.clear(); deduplicator.clear(); @@ -178,14 +174,12 @@ public void reset() { /** * Disable. */ - public void disable() { enabled = false; } /** * Enable. */ - public void enable() { enabled = true; } @@ -208,7 +202,14 @@ public static class RaceConditionEvent implements DeduplicatableEvent { private final String location; private final int lineNumber; private final long threadId; - + /** + * Creates a RaceConditionEvent. + * + * @param type the kind of event being recorded, shown in the report + * @param location where in the code this happened, shown in the report + * @param lineNumber the source line the access came from + * @param threadId the id of the thread performing the operation + */ public RaceConditionEvent(String type, String location, int lineNumber, long threadId) { this.type = type; this.location = location; @@ -244,9 +245,9 @@ public String getType() { } public static class RaceConditionReport { - /** The unsafe accesses. */ + /** Individual accesses that took part in a suspected race. */ public final Set unsafeAccesses = new HashSet<>(); - /** The potential races. */ + /** Fields accessed from more than one thread without synchronization. */ public final Set potentialRaces = new HashSet<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ReadWriteLockMonitor.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ReadWriteLockMonitor.java index d509b109..82fb3b79 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ReadWriteLockMonitor.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ReadWriteLockMonitor.java @@ -39,8 +39,8 @@ private static class LockState { /** * Register a read-write lock for monitoring. * - * @param rwLock the rw lock - * @param name the name + * @param rwLock the read-write lock being recorded, tracked by identity + * @param name a label identifying the rw lock in the report */ public void registerLock(Object rwLock, String name) { if (!enabled) return; @@ -52,7 +52,7 @@ public void registerLock(Object rwLock, String name) { /** * Record read lock acquisition. * - * @param rwLock the rw lock + * @param rwLock the read-write lock being recorded, tracked by identity * @param waitTimeMs the wait time in milliseconds */ public void recordReadLockAcquired(Object rwLock, long waitTimeMs) { @@ -70,7 +70,7 @@ public void recordReadLockAcquired(Object rwLock, long waitTimeMs) { /** * Record read lock release. * - * @param rwLock the rw lock + * @param rwLock the read-write lock being recorded, tracked by identity */ public void recordReadLockReleased(Object rwLock) { if (!enabled) return; @@ -85,7 +85,7 @@ public void recordReadLockReleased(Object rwLock) { /** * Record write lock acquisition. * - * @param rwLock the rw lock + * @param rwLock the read-write lock being recorded, tracked by identity * @param waitTimeMs the wait time in milliseconds */ public void recordWriteLockAcquired(Object rwLock, long waitTimeMs) { @@ -109,7 +109,7 @@ public void recordWriteLockAcquired(Object rwLock, long waitTimeMs) { /** * Record write lock release. * - * @param rwLock the rw lock + * @param rwLock the read-write lock being recorded, tracked by identity */ public void recordWriteLockReleased(Object rwLock) { if (!enabled) return; @@ -124,7 +124,7 @@ public void recordWriteLockReleased(Object rwLock) { /** * Analyze read-write lock fairness. * - * @return the analyze fairness + * @return the findings this detector collected during the run */ public ReadWriteLockReport analyzeFairness() { ReadWriteLockReport report = new ReadWriteLockReport(); @@ -176,7 +176,7 @@ public ReadWriteLockReport analyzeFairness() { /** * Standardized alias for {@link #analyzeFairness()}. * - * @return the analyze + * @return the findings this detector collected during the run */ public ReadWriteLockReport analyze() { return analyzeFairness(); @@ -184,35 +184,32 @@ public ReadWriteLockReport analyze() { /** * Clears recorded the observation so this instance can be reused for the next run. */ - public void reset() { locks.clear(); } /** * Disable. */ - public void disable() { enabled = false; } /** * Enable. */ - public void enable() { enabled = true; } public static class ReadWriteLockReport { - /** The reader dominated locks. */ + /** Locks where readers arrived often enough to hold writers off. */ public final Set readerDominatedLocks = new HashSet<>(); - /** The starved writers. */ + /** Writers that never acquired the lock during the run. */ public final Set starvedWriters = new HashSet<>(); - /** The long write waits. */ + /** Writers that waited longer than the reporting threshold. */ public final Set longWriteWaits = new HashSet<>(); - /** The current write holders. */ + /** Threads currently holding the write lock. */ public final Set currentWriteHolders = new HashSet<>(); - /** The current read holders. */ + /** Threads currently holding the read lock. */ public final Set currentReadHolders = new HashSet<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ReentrantLockDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ReentrantLockDetector.java index 35f88caa..f495ffdb 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ReentrantLockDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ReentrantLockDetector.java @@ -26,8 +26,8 @@ public class ReentrantLockDetector { /** * Register a ReentrantLock for monitoring. * - * @param lock the lock - * @param name the name + * @param lock the lock being recorded, tracked by identity rather than equality + * @param name a label identifying the lock in the report */ public void registerLock(ReentrantLock lock, String name) { lockRegistry.put(lock, new LockInfo(name)); @@ -36,8 +36,8 @@ public void registerLock(ReentrantLock lock, String name) { /** * Record a successful lock acquisition. * - * @param lock the lock - * @param threadName the thread name + * @param lock the lock being recorded, tracked by identity rather than equality + * @param threadName a label identifying the thread in the report */ public void recordLockAcquired(ReentrantLock lock, String threadName) { LockInfo info = lockRegistry.get(lock); @@ -49,8 +49,8 @@ public void recordLockAcquired(ReentrantLock lock, String threadName) { /** * Record a lock release. * - * @param lock the lock - * @param threadName the thread name + * @param lock the lock being recorded, tracked by identity rather than equality + * @param threadName a label identifying the thread in the report */ public void recordLockReleased(ReentrantLock lock, String threadName) { LockInfo info = lockRegistry.get(lock); @@ -62,7 +62,7 @@ public void recordLockReleased(ReentrantLock lock, String threadName) { /** * Record a tryLock() that timed out. * - * @param lock the lock + * @param lock the lock being recorded, tracked by identity rather than equality */ public void recordLockTimeout(ReentrantLock lock) { timeoutLocks.add(lock); @@ -71,7 +71,7 @@ public void recordLockTimeout(ReentrantLock lock) { /** * Record potential lock starvation (wait time exceeds threshold). * - * @param threadName the thread name + * @param threadName a label identifying the thread in the report * @param waitTimeMs the wait time in milliseconds */ public void recordStarvation(String threadName, long waitTimeMs) { @@ -81,7 +81,7 @@ public void recordStarvation(String threadName, long waitTimeMs) { /** * Analyze lock usage and return report. * - * @return the analyze + * @return the findings this detector collected during the run */ public ReentrantLockReport analyze() { return new ReentrantLockReport( @@ -98,7 +98,13 @@ public static class ReentrantLockReport { private final Map lockRegistry; private final Set timeoutLocks; private final Set starvationThreads; - + /** + * Creates a ReentrantLockReport. + * + * @param lockRegistry every registered lock and what was observed on it + * @param timeoutLocks the locks whose timed acquisition failed + * @param starvationThreads the threads that waited long enough to count as starved + */ public ReentrantLockReport( Map lockRegistry, Set timeoutLocks, diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ResourceLeakDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ResourceLeakDetector.java index 061c9d95..456c31f4 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ResourceLeakDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ResourceLeakDetector.java @@ -79,7 +79,7 @@ public void registerResource(Object resource, String name, String resourceType) /** * Record that a resource was opened/acquired. * - * @param resource the resource + * @param resource the resource being recorded, tracked by identity * @param name the resource name (should match registration) */ public void recordResourceOpened(Object resource, String name) { @@ -98,7 +98,7 @@ public void recordResourceOpened(Object resource, String name) { /** * Record that a resource was closed/released. * - * @param resource the resource + * @param resource the resource being recorded, tracked by identity * @param name the resource name (should match registration) */ public void recordResourceClosed(Object resource, String name) { @@ -169,7 +169,7 @@ public static class ResourceLeakReport { /** * Check if any issues were detected. * - * @return the has issues + * @return {@code true} when this detector recorded something worth reporting */ public boolean hasIssues() { return !resourceLeaks.isEmpty() || !openResources.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ScheduledExecutorDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ScheduledExecutorDetector.java index d787b1c4..d0b5830d 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ScheduledExecutorDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ScheduledExecutorDetector.java @@ -25,9 +25,9 @@ public class ScheduledExecutorDetector { /** * Register a ScheduledExecutorService for monitoring. * - * @param executor the executor - * @param name the name - * @param corePoolSize the core pool size + * @param executor the executor being recorded, tracked by identity + * @param name a label identifying the executor in the report + * @param corePoolSize the configured core pool size */ public void registerExecutor(ScheduledExecutorService executor, String name, int corePoolSize) { executorRegistry.put(executor, new ExecutorInfo(name, corePoolSize)); @@ -36,9 +36,9 @@ public void registerExecutor(ScheduledExecutorService executor, String name, int /** * Record a task being scheduled. * - * @param executor the executor - * @param executorName the executor name - * @param taskName the task name + * @param executor the executor being recorded, tracked by identity + * @param executorName a label identifying the executor in the report + * @param taskName a label identifying the task in the report */ public void recordSchedule(ScheduledExecutorService executor, String executorName, String taskName) { ExecutorInfo info = executorRegistry.get(executor); @@ -50,9 +50,9 @@ public void recordSchedule(ScheduledExecutorService executor, String executorNam /** * Record a task starting execution. * - * @param executor the executor - * @param executorName the executor name - * @param taskName the task name + * @param executor the executor being recorded, tracked by identity + * @param executorName a label identifying the executor in the report + * @param taskName a label identifying the task in the report */ public void recordTaskStart(ScheduledExecutorService executor, String executorName, String taskName) { ExecutorInfo info = executorRegistry.get(executor); @@ -64,9 +64,9 @@ public void recordTaskStart(ScheduledExecutorService executor, String executorNa /** * Record a task completing execution. * - * @param executor the executor - * @param executorName the executor name - * @param taskName the task name + * @param executor the executor being recorded, tracked by identity + * @param executorName a label identifying the executor in the report + * @param taskName a label identifying the task in the report * @param durationMs the duration in milliseconds */ public void recordTaskComplete(ScheduledExecutorService executor, String executorName, String taskName, long durationMs) { @@ -82,8 +82,8 @@ public void recordTaskComplete(ScheduledExecutorService executor, String executo /** * Record an exception in a scheduled task. * - * @param executor the executor - * @param executorName the executor name + * @param executor the executor being recorded, tracked by identity + * @param executorName a label identifying the executor in the report */ public void recordException(ScheduledExecutorService executor, String executorName) { exceptionInTasks++; @@ -92,7 +92,7 @@ public void recordException(ScheduledExecutorService executor, String executorNa /** * Record executor shutdown. * - * @param executor the executor + * @param executor the executor being recorded, tracked by identity */ public void recordShutdown(ScheduledExecutorService executor) { ExecutorInfo info = executorRegistry.get(executor); @@ -115,7 +115,7 @@ public void checkShutdown() { /** * Analyze ScheduledExecutorService usage and return report. * - * @return the analyze + * @return the findings this detector collected during the run */ public ScheduledExecutorReport analyze() { checkShutdown(); @@ -135,7 +135,14 @@ public static class ScheduledExecutorReport { private final Set notShutdownExecutors; private final Set longRunningTasks; private final int exceptionInTasks; - + /** + * Creates a ScheduledExecutorReport. + * + * @param executorRegistry every registered executor and what was observed on it + * @param notShutdownExecutors the executors never shut down + * @param longRunningTasks the tasks that ran past the reporting threshold + * @param exceptionInTasks the exceptions thrown inside scheduled tasks + */ public ScheduledExecutorReport( Map executorRegistry, Set notShutdownExecutors, diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SemaphoreMisuseDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SemaphoreMisuseDetector.java index ba2a4c86..6a969d17 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SemaphoreMisuseDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SemaphoreMisuseDetector.java @@ -73,7 +73,7 @@ public void registerSemaphore(Semaphore semaphore, String name, int initialPermi /** * Record a permit acquisition. * - * @param semaphore the semaphore + * @param semaphore the semaphore being recorded, tracked by identity * @param name the semaphore name (should match registration) */ public void recordAcquire(Semaphore semaphore, String name) { @@ -96,7 +96,7 @@ public void recordAcquire(Semaphore semaphore, String name) { /** * Record a permit release. * - * @param semaphore the semaphore + * @param semaphore the semaphore being recorded, tracked by identity * @param name the semaphore name (should match registration) */ public void recordRelease(Semaphore semaphore, String name) { @@ -173,7 +173,7 @@ public static class SemaphoreMisuseReport { /** * Check if any issues were detected. * - * @return the has issues + * @return {@code true} when this detector recorded something worth reporting */ public boolean hasIssues() { return !permitLeaks.isEmpty() || !overReleases.isEmpty() || !unreleasedPermits.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedByteBufferDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedByteBufferDetector.java index 4b5845d1..004fc48b 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedByteBufferDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedByteBufferDetector.java @@ -124,9 +124,8 @@ private State resolve(Object buffer) { /** * Analyses what has been recorded about the observation and builds the report for it. * - * @return the analyze + * @return the findings this detector collected during the run */ - public Report analyze() { Report r = new Report(); for (State s : instances.values()) { @@ -168,9 +167,9 @@ public Report analyze() { } public static final class Report { - /** The violations. */ + /** Findings as human-readable lines, for the text report. */ public final List violations = new ArrayList<>(); - /** The structured violations. */ + /** The same findings as {@link se.deversity.asynctest.report.Violation} objects, for machine-readable reports. */ public final List structuredViolations = new ArrayList<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedCharsetCoderDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedCharsetCoderDetector.java index b20a77ff..1a7813c9 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedCharsetCoderDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedCharsetCoderDetector.java @@ -107,9 +107,8 @@ private void record(int id, String operation, String kind, Thread thread) { /** * Analyses what has been recorded about the observation and builds the report for it. * - * @return the analyze + * @return the findings this detector collected during the run */ - public Report analyze() { Report r = new Report(); for (State s : instances.values()) { @@ -140,9 +139,9 @@ public Report analyze() { } public static final class Report { - /** The violations. */ + /** Findings as human-readable lines, for the text report. */ public final List violations = new ArrayList<>(); - /** The structured violations. */ + /** The same findings as {@link se.deversity.asynctest.report.Violation} objects, for machine-readable reports. */ public final List structuredViolations = new ArrayList<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedChecksumDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedChecksumDetector.java index d2feb1f8..97741c9e 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedChecksumDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedChecksumDetector.java @@ -84,9 +84,8 @@ public void recordAccess(Checksum checksum, String operation, Thread thread) { /** * Analyses what has been recorded about the observation and builds the report for it. * - * @return the analyze + * @return the findings this detector collected during the run */ - public Report analyze() { Report r = new Report(); for (State s : instances.values()) { @@ -115,9 +114,9 @@ public Report analyze() { } public static final class Report { - /** The violations. */ + /** Findings as human-readable lines, for the text report. */ public final List violations = new ArrayList<>(); - /** The structured violations. */ + /** The same findings as {@link se.deversity.asynctest.report.Violation} objects, for machine-readable reports. */ public final List structuredViolations = new ArrayList<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedCollectionDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedCollectionDetector.java index c8da1b41..01abbb0f 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedCollectionDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedCollectionDetector.java @@ -123,7 +123,7 @@ private CollectionState resolveState(Object collection, String name) { /** * Analyse collection usage and return a report. * - * @return the analyze + * @return the findings this detector collected during the run */ public SharedCollectionReport analyze() { SharedCollectionReport report = new SharedCollectionReport(); @@ -170,7 +170,7 @@ public static class SharedCollectionReport { /** * Returns {@code true} when any concurrent-access violations were detected. * - * @return the has issues + * @return {@code true} when this detector recorded something worth reporting */ public boolean hasIssues() { return !concurrentWriteViolations.isEmpty() || !mixedAccessViolations.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedDeflaterDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedDeflaterDetector.java index 09a36fa1..47bdb28c 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedDeflaterDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedDeflaterDetector.java @@ -101,9 +101,8 @@ private void record(int id, String name, String kind, Thread thread) { /** * Analyses what has been recorded about the observation and builds the report for it. * - * @return the analyze + * @return the findings this detector collected during the run */ - public Report analyze() { Report r = new Report(); for (State s : instances.values()) { @@ -133,9 +132,9 @@ public Report analyze() { } public static final class Report { - /** The violations. */ + /** Findings as human-readable lines, for the text report. */ public final List violations = new ArrayList<>(); - /** The structured violations. */ + /** The same findings as {@link se.deversity.asynctest.report.Violation} objects, for machine-readable reports. */ public final List structuredViolations = new ArrayList<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedIteratorDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedIteratorDetector.java index fd420f14..0985c526 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedIteratorDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedIteratorDetector.java @@ -115,9 +115,8 @@ private static String kindOf(Object iterator) { /** * Analyses what has been recorded about the observation and builds the report for it. * - * @return the analyze + * @return the findings this detector collected during the run */ - public Report analyze() { Report r = new Report(); for (State s : instances.values()) { @@ -149,9 +148,9 @@ public Report analyze() { } public static final class Report { - /** The violations. */ + /** Findings as human-readable lines, for the text report. */ public final List violations = new ArrayList<>(); - /** The structured violations. */ + /** The same findings as {@link se.deversity.asynctest.report.Violation} objects, for machine-readable reports. */ public final List structuredViolations = new ArrayList<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedJsonMapperReconfigDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedJsonMapperReconfigDetector.java index 9636a7a3..1f3687ca 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedJsonMapperReconfigDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedJsonMapperReconfigDetector.java @@ -137,9 +137,8 @@ private State stateFor(Object mapper) { /** * Analyses what has been recorded about the observation and builds the report for it. * - * @return the analyze + * @return the findings this detector collected during the run */ - public Report analyze() { Report r = new Report(); for (State s : instances.values()) { @@ -180,9 +179,9 @@ public Report analyze() { } public static final class Report { - /** The violations. */ + /** Findings as human-readable lines, for the text report. */ public final List violations = new ArrayList<>(); - /** The structured violations. */ + /** The same findings as {@link se.deversity.asynctest.report.Violation} objects, for machine-readable reports. */ public final List structuredViolations = new ArrayList<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedKdfDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedKdfDetector.java index 4212848e..7d31ff98 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedKdfDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedKdfDetector.java @@ -91,9 +91,8 @@ public void recordAccess(Object kdf, String algorithm, String operation, Thread /** * Analyses what has been recorded about the observation and builds the report for it. * - * @return the analyze + * @return the findings this detector collected during the run */ - public Report analyze() { Report r = new Report(); for (State s : instances.values()) { @@ -125,9 +124,9 @@ public Report analyze() { } public static final class Report { - /** The violations. */ + /** Findings as human-readable lines, for the text report. */ public final List violations = new ArrayList<>(); - /** The structured violations. */ + /** The same findings as {@link se.deversity.asynctest.report.Violation} objects, for machine-readable reports. */ public final List structuredViolations = new ArrayList<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedMessageDigestDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedMessageDigestDetector.java index 2d3de64b..b536c927 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedMessageDigestDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedMessageDigestDetector.java @@ -162,9 +162,9 @@ public SharedMessageDigestReport analyze() { /** Report produced by {@link #analyze()}. */ public static class SharedMessageDigestReport { - /** The violations. */ + /** Findings as human-readable lines, for the text report. */ public final List violations = new ArrayList<>(); - /** The violated types. */ + /** Algorithm names whose shared instance was used from more than one thread. */ public final Set violatedTypes = new LinkedHashSet<>(); /** Structured mirror of {@link #violations} for {@link se.deversity.asynctest.report.Formatter}s. */ public final List structuredViolations = new ArrayList<>(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedRandomDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedRandomDetector.java index 37722be0..13c886d6 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedRandomDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedRandomDetector.java @@ -157,7 +157,7 @@ public static class SharedRandomReport { /** * Check if any issues were detected. * - * @return the has issues + * @return {@code true} when this detector recorded something worth reporting */ public boolean hasIssues() { return !sharedRandoms.isEmpty() || !highContention.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedSecureRandomDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedSecureRandomDetector.java index ee6ba942..4675e0bb 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedSecureRandomDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedSecureRandomDetector.java @@ -101,9 +101,8 @@ public void recordAccess(SecureRandom random, String name, Thread thread) { /** * Analyses what has been recorded about the observation and builds the report for it. * - * @return the analyze + * @return the findings this detector collected during the run */ - public Report analyze() { Report r = new Report(); for (State s : instances.values()) { @@ -138,9 +137,9 @@ private static String safeString(java.util.concurrent.Callable c) { } public static final class Report { - /** The violations. */ + /** Findings as human-readable lines, for the text report. */ public final List violations = new ArrayList<>(); - /** The structured violations. */ + /** The same findings as {@link se.deversity.asynctest.report.Violation} objects, for machine-readable reports. */ public final List structuredViolations = new ArrayList<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedStatefulCryptoDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedStatefulCryptoDetector.java index 5b987799..cccba9d4 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedStatefulCryptoDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SharedStatefulCryptoDetector.java @@ -129,9 +129,8 @@ private void record(int id, String name, String kind, Class type, String algo /** * Analyses what has been recorded about the observation and builds the report for it. * - * @return the analyze + * @return the findings this detector collected during the run */ - public Report analyze() { Report r = new Report(); for (State s : instances.values()) { @@ -167,9 +166,9 @@ private static String safeString(java.util.concurrent.Callable c) { } public static final class Report { - /** The violations. */ + /** Findings as human-readable lines, for the text report. */ public final List violations = new ArrayList<>(); - /** The structured violations. */ + /** The same findings as {@link se.deversity.asynctest.report.Violation} objects, for machine-readable reports. */ public final List structuredViolations = new ArrayList<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SimpleDateFormatDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SimpleDateFormatDetector.java index ffdae614..534f1ffc 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SimpleDateFormatDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SimpleDateFormatDetector.java @@ -201,7 +201,7 @@ public static class SimpleDateFormatReport { /** * Check if any issues were detected. * - * @return the has issues + * @return {@code true} when this detector recorded something worth reporting */ public boolean hasIssues() { return !sharedFormatters.isEmpty() || !formattingErrors.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SiteCapture.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SiteCapture.java index 16b7a919..b7e43320 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SiteCapture.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SiteCapture.java @@ -68,7 +68,7 @@ private SiteCapture() {} * Returns the first stack frame outside the framework / JDK reflection / * JUnit, or {@link Optional#empty()} if none could be identified (rare). * - * @return the capture + * @return the first stack frame in user code, or empty when none could be attributed */ public static Optional capture() { return WALKER.walk(stream -> stream @@ -120,7 +120,7 @@ static Site of(StackFrame f) { /** * Human-readable {@code Class.method(File.java:42)} form. * - * @return the render + * @return the site rendered as {@code Class.method(File:line)} for the report */ public String render() { String shortClass = className.contains(".") diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SleepInLockDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SleepInLockDetector.java index 573ce819..6043ef33 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SleepInLockDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SleepInLockDetector.java @@ -191,7 +191,6 @@ public void clear() { /** * Disable. */ - public void disable() { this.enabled = false; } @@ -200,15 +199,15 @@ public void disable() { * Immutable snapshot of a sleep-in-lock event. */ public static class SleepInLockEventSnapshot { - /** The lock name. */ + /** Label identifying the lock that was held while sleeping. */ public final @Nullable String lockName; - /** The thread name. */ + /** Label identifying the sleeping thread in the report. */ public final String threadName; - /** The sleep duration. */ + /** How long the thread slept while holding the lock, in nanoseconds. */ public final long sleepDuration; - /** The stack trace. */ + /** Where the sleep happened. */ public final StackTraceElement[] stackTrace; - /** The lock type. */ + /** Whether the lock held was {@code synchronized} or a {@code ReentrantLock}. */ public final @Nullable String lockType; SleepInLockEventSnapshot(@Nullable String lockName, String threadName, diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SpuriousWakeupDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SpuriousWakeupDetector.java index ff9baaea..b0eb016c 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SpuriousWakeupDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SpuriousWakeupDetector.java @@ -37,10 +37,10 @@ private static final class State { /** * Record a wait/await operation. * - * @param monitor the monitor - * @param monitorName the monitor name + * @param monitor the object being used as a monitor, tracked by identity + * @param monitorName a label identifying the monitor in the report * @param insideLoop the {@code insideLoop} flag - * @param thread the thread + * @param thread the thread performing the operation */ public void recordWait(Object monitor, String monitorName, boolean insideLoop, Thread thread) { if (monitor == null || thread == null) return; @@ -55,9 +55,8 @@ public void recordWait(Object monitor, String monitorName, boolean insideLoop, T /** * Analyses what has been recorded about the observation and builds the report for it. * - * @return the analyze + * @return the findings this detector collected during the run */ - public Report analyze() { Report r = new Report(); for (State s : monitors.values()) { @@ -84,9 +83,9 @@ public Report analyze() { } public static final class Report { - /** The violations. */ + /** Findings as human-readable lines, for the text report. */ public final List violations = new ArrayList<>(); - /** The structured violations. */ + /** The same findings as {@link se.deversity.asynctest.report.Violation} objects, for machine-readable reports. */ public final List structuredViolations = new ArrayList<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StampedLockDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StampedLockDetector.java index a6b6de52..0e8e109a 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StampedLockDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StampedLockDetector.java @@ -23,8 +23,8 @@ public class StampedLockDetector { /** * Register a StampedLock for monitoring. * - * @param lock the lock - * @param name the name + * @param lock the lock being recorded, tracked by identity rather than equality + * @param name a label identifying the lock in the report */ public void registerLock(StampedLock lock, String name) { lockRegistry.put(lock, new LockInfo(name)); @@ -33,9 +33,9 @@ public void registerLock(StampedLock lock, String name) { /** * Record an optimistic read stamp acquisition. * - * @param lock the lock - * @param lockName the lock name - * @param stamp the stamp + * @param lock the lock being recorded, tracked by identity rather than equality + * @param lockName a label identifying the lock in the report + * @param stamp the stamp returned by the {@code StampedLock} operation */ public void recordOptimisticRead(StampedLock lock, String lockName, long stamp) { LockInfo info = lockRegistry.get(lock); @@ -47,9 +47,9 @@ public void recordOptimisticRead(StampedLock lock, String lockName, long stamp) /** * Record validation of optimistic read. * - * @param lock the lock - * @param lockName the lock name - * @param stamp the stamp + * @param lock the lock being recorded, tracked by identity rather than equality + * @param lockName a label identifying the lock in the report + * @param stamp the stamp returned by the {@code StampedLock} operation * @param validated the {@code validated} flag */ public void recordOptimisticValidation(StampedLock lock, String lockName, long stamp, boolean validated) { @@ -61,9 +61,9 @@ public void recordOptimisticValidation(StampedLock lock, String lockName, long s /** * Record a read lock acquisition. * - * @param lock the lock - * @param lockName the lock name - * @param stamp the stamp + * @param lock the lock being recorded, tracked by identity rather than equality + * @param lockName a label identifying the lock in the report + * @param stamp the stamp returned by the {@code StampedLock} operation */ public void recordReadLock(StampedLock lock, String lockName, long stamp) { LockInfo info = lockRegistry.get(lock); @@ -75,9 +75,9 @@ public void recordReadLock(StampedLock lock, String lockName, long stamp) { /** * Record a write lock acquisition. * - * @param lock the lock - * @param lockName the lock name - * @param stamp the stamp + * @param lock the lock being recorded, tracked by identity rather than equality + * @param lockName a label identifying the lock in the report + * @param stamp the stamp returned by the {@code StampedLock} operation */ public void recordWriteLock(StampedLock lock, String lockName, long stamp) { LockInfo info = lockRegistry.get(lock); @@ -89,9 +89,9 @@ public void recordWriteLock(StampedLock lock, String lockName, long stamp) { /** * Record a lock release. * - * @param lock the lock - * @param lockName the lock name - * @param stamp the stamp + * @param lock the lock being recorded, tracked by identity rather than equality + * @param lockName a label identifying the lock in the report + * @param stamp the stamp returned by the {@code StampedLock} operation */ public void recordUnlock(StampedLock lock, String lockName, long stamp) { LockInfo info = lockRegistry.get(lock); @@ -103,8 +103,8 @@ public void recordUnlock(StampedLock lock, String lockName, long stamp) { /** * Record a stamp that was not released. * - * @param lockName the lock name - * @param stamp the stamp + * @param lockName a label identifying the lock in the report + * @param stamp the stamp returned by the {@code StampedLock} operation */ public void recordStampNotReleased(String lockName, long stamp) { stampNotReleased.add(lockName + " (stamp: " + stamp + ")"); @@ -113,7 +113,7 @@ public void recordStampNotReleased(String lockName, long stamp) { /** * Analyze StampedLock usage and return report. * - * @return the analyze + * @return the findings this detector collected during the run */ public StampedLockReport analyze() { return new StampedLockReport( @@ -128,7 +128,12 @@ public StampedLockReport analyze() { public static class StampedLockReport { private final Set unvalidatedOptimisticReads; private final Set stampNotReleased; - + /** + * Creates a StampedLockReport. + * + * @param unvalidatedOptimisticReads the optimistic reads whose stamp was never validated + * @param stampNotReleased the stamps acquired but never released + */ public StampedLockReport( Set unvalidatedOptimisticReads, Set stampNotReleased diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StreamClosingDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StreamClosingDetector.java index 7068943c..88573654 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StreamClosingDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StreamClosingDetector.java @@ -182,7 +182,7 @@ public static class StreamClosingReport { /** * Check if any issues were detected. * - * @return the has issues + * @return {@code true} when this detector recorded something worth reporting */ public boolean hasIssues() { return !unclosedStreams.isEmpty() || diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StringBuilderDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StringBuilderDetector.java index 56b56904..380d2629 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StringBuilderDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StringBuilderDetector.java @@ -162,7 +162,7 @@ private BuilderState resolve(StringBuilder builder, String name) { /** * Analyse StringBuilder usage and return a report. * - * @return the analyze + * @return the findings this detector collected during the run */ public StringBuilderReport analyze() { StringBuilderReport report = new StringBuilderReport(); @@ -215,7 +215,7 @@ public static class StringBuilderReport { /** * Returns {@code true} when shared-mutation or errors were detected. * - * @return the has issues + * @return {@code true} when this detector recorded something worth reporting */ public boolean hasIssues() { return !sharedBuilderViolations.isEmpty() || !builderErrors.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StructuredTaskScopeMisuseDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StructuredTaskScopeMisuseDetector.java index 5429f9a9..aaa8bef8 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StructuredTaskScopeMisuseDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/StructuredTaskScopeMisuseDetector.java @@ -109,8 +109,8 @@ private static final class ScopeState { * Record that a {@code StructuredTaskScope.open(...)} returned a new scope, * confined to the opening (owner) thread. * - * @param scopeId the scope id - * @param owner the owner + * @param scopeId correlates the calls belonging to one scope across its lifecycle + * @param owner the thread currently holding it */ public void recordScopeOpened(String scopeId, Thread owner) { if (scopeId == null || owner == null) return; @@ -122,9 +122,9 @@ public void recordScopeOpened(String scopeId, Thread owner) { * Record a {@code scope.fork(task)} call. Flags fork-after-join and * owner-confinement violations. * - * @param scopeId the scope id - * @param subtaskId the subtask id - * @param thread the thread + * @param scopeId correlates the calls belonging to one scope across its lifecycle + * @param subtaskId correlates the calls belonging to one subtask + * @param thread the thread performing the operation */ public void recordFork(String scopeId, String subtaskId, Thread thread) { if (scopeId == null || subtaskId == null || thread == null) return; @@ -155,8 +155,8 @@ public void recordFork(String scopeId, String subtaskId, Thread thread) { * Record a {@code scope.join()} call. Flags owner-confinement violations and * marks the scope as joined. * - * @param scopeId the scope id - * @param thread the thread + * @param scopeId correlates the calls belonging to one scope across its lifecycle + * @param thread the thread performing the operation */ public void recordJoin(String scopeId, Thread thread) { if (scopeId == null || thread == null) return; @@ -179,9 +179,9 @@ public void recordJoin(String scopeId, Thread thread) { * scope has been joined, and reads that happen after {@code join()} timed out * (the subtask state is not {@code SUCCESS}, so {@code get()} throws). * - * @param scopeId the scope id - * @param subtaskId the subtask id - * @param thread the thread + * @param scopeId correlates the calls belonging to one scope across its lifecycle + * @param subtaskId correlates the calls belonging to one subtask + * @param thread the thread performing the operation */ public void recordResultRead(String scopeId, String subtaskId, Thread thread) { if (scopeId == null || subtaskId == null || thread == null) return; @@ -214,8 +214,8 @@ public void recordResultRead(String scopeId, String subtaskId, Thread thread) { * * @since 1.7.0 * - * @param scopeId the scope id - * @param thread the thread + * @param scopeId correlates the calls belonging to one scope across its lifecycle + * @param thread the thread performing the operation */ public void recordJoinTimeout(String scopeId, Thread thread) { if (scopeId == null || thread == null) return; @@ -242,8 +242,8 @@ public void recordJoinTimeout(String scopeId, Thread thread) { * * @since 1.7.0 * - * @param scopeId the scope id - * @param thread the thread + * @param scopeId correlates the calls belonging to one scope across its lifecycle + * @param thread the thread performing the operation */ public void recordTimeoutSwallowed(String scopeId, Thread thread) { if (scopeId == null || thread == null) return; @@ -268,8 +268,8 @@ public void recordTimeoutSwallowed(String scopeId, Thread thread) { * Record that the scope was closed (the try-with-resources block ended). * Flags a scope that forked subtasks but was never joined. * - * @param scopeId the scope id - * @param thread the thread + * @param scopeId correlates the calls belonging to one scope across its lifecycle + * @param thread the thread performing the operation */ public void recordScopeClosed(String scopeId, Thread thread) { if (scopeId == null || thread == null) return; @@ -364,15 +364,19 @@ public boolean hasIssues() { */ public List getMissingJoinIssues() { return Collections.unmodifiableList(missingJoinIssues); } /** + * Get result after timeout issues. + * * @since 1.7.0 * - * @return the get result after timeout issues + * @return the recorded cases where a subtask result was read after the scope timed out */ public List getResultAfterTimeoutIssues() { return Collections.unmodifiableList(resultAfterTimeoutIssues); } /** + * Get timeout swallowed warnings. + * * @since 1.7.0 * - * @return the get timeout swallowed warnings + * @return the recorded cases where a scope timeout was caught and discarded */ public List getTimeoutSwallowedWarnings() { return Collections.unmodifiableList(timeoutSwallowedWarnings); } /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizedCollectionIterationDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizedCollectionIterationDetector.java index b0dd2988..5356b03d 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizedCollectionIterationDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizedCollectionIterationDetector.java @@ -48,8 +48,8 @@ private static class WrapperInfo { /** * Register a synchronized wrapper created by {@code Collections.synchronized*(collection)}. * - * @param wrapper the wrapper - * @param name the name + * @param wrapper the wrapper object being recorded, tracked by identity + * @param name a label identifying the wrapper in the report */ public void recordWrapperCreated(Object wrapper, String name) { if (wrapper == null) return; diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizedNonFinalDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizedNonFinalDetector.java index c24fe144..a042b107 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizedNonFinalDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizedNonFinalDetector.java @@ -78,7 +78,7 @@ public void recordLockObject(Object lockObject, String fieldId, Class ownerCl * Analyses recorded lock objects and returns a report of slots where the * monitor reference changed across invocations. * - * @return the analyze + * @return the findings this detector collected during the run */ public SynchronizedNonFinalReport analyze() { SynchronizedNonFinalReport report = new SynchronizedNonFinalReport(); @@ -106,7 +106,7 @@ public static class SynchronizedNonFinalReport { /** * Returns {@code true} when any reassignable-lock violation was detected. * - * @return the has issues + * @return {@code true} when this detector recorded something worth reporting */ public boolean hasIssues() { return !violations.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizerMonitor.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizerMonitor.java index 252db0a2..f142d056 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizerMonitor.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/SynchronizerMonitor.java @@ -39,8 +39,8 @@ private static class BarrierState { /** * Register a synchronizer for monitoring. * - * @param synchronizer the synchronizer - * @param expectedParties the expected parties + * @param synchronizer the synchronizer being recorded, tracked by identity + * @param expectedParties the number of parties expected to arrive */ public void registerSynchronizer(Object synchronizer, int expectedParties) { if (!enabled) return; @@ -55,7 +55,7 @@ public void registerSynchronizer(Object synchronizer, int expectedParties) { /** * Record thread arriving at barrier. * - * @param synchronizer the synchronizer + * @param synchronizer the synchronizer being recorded, tracked by identity */ public void recordBarrierArrival(Object synchronizer) { if (!enabled) return; @@ -74,7 +74,7 @@ public void recordBarrierArrival(Object synchronizer) { /** * Record thread advancing past barrier. * - * @param synchronizer the synchronizer + * @param synchronizer the synchronizer being recorded, tracked by identity */ public void recordBarrierAdvance(Object synchronizer) { if (!enabled) return; @@ -90,7 +90,7 @@ public void recordBarrierAdvance(Object synchronizer) { /** * Record barrier reset. * - * @param synchronizer the synchronizer + * @param synchronizer the synchronizer being recorded, tracked by identity */ public void recordBarrierReset(Object synchronizer) { if (!enabled) return; @@ -107,7 +107,7 @@ public void recordBarrierReset(Object synchronizer) { /** * Analyze synchronizer behavior. * - * @return the analyze synchronizers + * @return the findings this detector collected during the run */ public SynchronizerReport analyzeSynchronizers() { SynchronizerReport report = new SynchronizerReport(); @@ -138,7 +138,7 @@ public SynchronizerReport analyzeSynchronizers() { /** * Standardized alias for {@link #analyzeSynchronizers()}. * - * @return the analyze + * @return the findings this detector collected during the run */ public SynchronizerReport analyze() { return analyzeSynchronizers(); @@ -146,29 +146,26 @@ public SynchronizerReport analyze() { /** * Clears recorded the observation so this instance can be reused for the next run. */ - public void reset() { synchronizers.clear(); } /** * Disable. */ - public void disable() { enabled = false; } /** * Enable. */ - public void enable() { enabled = true; } public static class SynchronizerReport { - /** The incomplete barriers. */ + /** Barriers that never had all their parties arrive. */ public final Set incompleteBarriers = new HashSet<>(); - /** The duplicate arrivals. */ + /** Parties that arrived at a synchronizer more than once in a cycle. */ public final Set duplicateArrivals = new HashSet<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThisEscapeDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThisEscapeDetector.java index 5dcd1184..97ff8647 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThisEscapeDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThisEscapeDetector.java @@ -125,9 +125,8 @@ public void recordConstructionComplete(Object instance) { /** * Analyses what has been recorded about the observation and builds the report for it. * - * @return the analyze + * @return the findings this detector collected during the run */ - public Report analyze() { Report r = new Report(); for (State s : instances.values()) { @@ -160,9 +159,9 @@ public Report analyze() { } public static final class Report { - /** The violations. */ + /** Findings as human-readable lines, for the text report. */ public final List violations = new ArrayList<>(); - /** The structured violations. */ + /** The same findings as {@link se.deversity.asynctest.report.Violation} objects, for machine-readable reports. */ public final List structuredViolations = new ArrayList<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadFactoryDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadFactoryDetector.java index 55638050..38a03820 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadFactoryDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadFactoryDetector.java @@ -24,8 +24,8 @@ public class ThreadFactoryDetector { /** * Register a ThreadFactory for monitoring. * - * @param factory the factory - * @param name the name + * @param factory the thread factory being recorded, tracked by identity + * @param name a label identifying the factory in the report */ public void registerFactory(ThreadFactory factory, String name) { factoryRegistry.put(factory, new FactoryInfo(name)); @@ -34,9 +34,9 @@ public void registerFactory(ThreadFactory factory, String name) { /** * Record a thread created by factory. * - * @param factory the factory - * @param factoryName the factory name - * @param thread the thread + * @param factory the thread factory being recorded, tracked by identity + * @param factoryName a label identifying the thread factory in the report + * @param thread the thread performing the operation */ public void recordThreadCreated(ThreadFactory factory, String factoryName, Thread thread) { FactoryInfo info = factoryRegistry.get(factory); @@ -63,7 +63,7 @@ public void recordThreadCreated(ThreadFactory factory, String factoryName, Threa /** * Analyze ThreadFactory usage and return report. * - * @return the analyze + * @return the findings this detector collected during the run */ public ThreadFactoryReport analyze() { return new ThreadFactoryReport( @@ -80,7 +80,13 @@ public static class ThreadFactoryReport { private final Set missingExceptionHandler; private final Set nonDaemonThreads; private final Set unnamedThreads; - + /** + * Creates a ThreadFactoryReport. + * + * @param missingExceptionHandler the threads created without an uncaught-exception handler + * @param nonDaemonThreads the non-daemon threads created, which can keep the JVM alive + * @param unnamedThreads the threads created without a name, which are hard to attribute in a dump + */ public ThreadFactoryReport( Set missingExceptionHandler, Set nonDaemonThreads, diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLeakDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLeakDetector.java index a43c6c57..305e2689 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLeakDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLeakDetector.java @@ -195,7 +195,6 @@ public void clear() { /** * Disable. */ - public void disable() { this.enabled = false; } @@ -204,14 +203,14 @@ public void disable() { * A thread leak event. */ public static class ThreadLeakEvent { - /** The thread name. */ + /** Label identifying the sleeping thread in the report. */ public final String threadName; - /** The thread. */ + /** The thread being tracked; cleared once it terminates. */ public final @Nullable Thread thread; - /** The start time. */ + /** When the thread was started, in nanoseconds. */ public final long startTime; public final StackTraceElement @Nullable [] creationStack; - /** The reason. */ + /** Why the thread was flagged, shown in the report. */ public final String reason; ThreadLeakEvent(String threadName, @Nullable Thread thread, long startTime, diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLocalContaminationDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLocalContaminationDetector.java index cd22696b..ae08b28a 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLocalContaminationDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLocalContaminationDetector.java @@ -42,8 +42,8 @@ private static final class ThreadState { /** * Call at the start of each task submitted to a thread pool. * - * @param thread the thread - * @param taskName the task name + * @param thread the thread performing the operation + * @param taskName a label identifying the task in the report */ public void recordNewTask(Thread thread, String taskName) { if (thread == null) return; @@ -55,9 +55,9 @@ public void recordNewTask(Thread thread, String taskName) { /** * Call after each {@code ThreadLocal.set()} inside a task. * - * @param thread the thread - * @param tl the tl - * @param name the name + * @param thread the thread performing the operation + * @param tl the thread-local being recorded, tracked by identity + * @param name a label identifying the tl in the report */ public void recordSet(Thread thread, Object tl, String name) { if (thread == null || tl == null) return; @@ -73,9 +73,9 @@ public void recordSet(Thread thread, Object tl, String name) { * * @param hasValue {@code true} if the get returned a non-null value * - * @param thread the thread - * @param tl the tl - * @param name the name + * @param thread the thread performing the operation + * @param tl the thread-local being recorded, tracked by identity + * @param name a label identifying the tl in the report */ public void recordGet(Thread thread, Object tl, String name, boolean hasValue) { if (thread == null || tl == null || !hasValue) return; diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLocalMonitor.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLocalMonitor.java index 86f10947..bbb38a81 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLocalMonitor.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLocalMonitor.java @@ -29,10 +29,9 @@ private static class ThreadLocalState { /** * Records thread local init so it can be analysed at the end of the run. * - * @param threadLocal the thread local - * @param name the name + * @param threadLocal the thread-local being recorded, tracked by identity + * @param name a label identifying the thread local in the report */ - public void recordThreadLocalInit(ThreadLocal threadLocal, String name) { if (!enabled || threadLocal == null) { return; @@ -47,9 +46,8 @@ public void recordThreadLocalInit(ThreadLocal threadLocal, String name) { /** * Records thread local access so it can be analysed at the end of the run. * - * @param threadLocal the thread local + * @param threadLocal the thread-local being recorded, tracked by identity */ - public void recordThreadLocalAccess(ThreadLocal threadLocal) { if (!enabled || threadLocal == null) { return; @@ -62,9 +60,8 @@ public void recordThreadLocalAccess(ThreadLocal threadLocal) { /** * Records thread local cleanup so it can be analysed at the end of the run. * - * @param threadLocal the thread local + * @param threadLocal the thread-local being recorded, tracked by identity */ - public void recordThreadLocalCleanup(ThreadLocal threadLocal) { if (!enabled || threadLocal == null) { return; @@ -84,9 +81,8 @@ private void recordThreadUsage(ThreadLocalState state, long threadId) { /** * Analyses what has been recorded about thread local leaks and builds the report for it. * - * @return the analyze thread local leaks + * @return the findings this detector collected during the run */ - public ThreadLocalReport analyzeThreadLocalLeaks() { ThreadLocalReport report = new ThreadLocalReport(); @@ -123,7 +119,7 @@ public ThreadLocalReport analyzeThreadLocalLeaks() { /** * Standardized alias for {@link #analyzeThreadLocalLeaks()}. * - * @return the analyze + * @return the findings this detector collected during the run */ public ThreadLocalReport analyze() { return analyzeThreadLocalLeaks(); @@ -131,7 +127,6 @@ public ThreadLocalReport analyze() { /** * Clears recorded the observation so this instance can be reused for the next run. */ - public void reset() { threadLocals.clear(); threadLocalsByThread.clear(); @@ -139,24 +134,22 @@ public void reset() { /** * Disable. */ - public void disable() { enabled = false; } /** * Enable. */ - public void enable() { enabled = true; } public static class ThreadLocalReport { - /** The uncleaned thread locals. */ + /** Thread-locals never removed before the thread was returned to its pool. */ public final Set uncleanedThreadLocals = new HashSet<>(); - /** The likely leaks. */ + /** Thread-locals still set on pooled threads after the task finished. */ public final Set likelyLeaks = new HashSet<>(); - /** The thread local accumulation. */ + /** Thread-locals whose stored value grew across reused threads. */ public final Set threadLocalAccumulation = new HashSet<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLocalRandomMisuseDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLocalRandomMisuseDetector.java index 99b005cd..6b216c85 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLocalRandomMisuseDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadLocalRandomMisuseDetector.java @@ -105,9 +105,8 @@ public void recordUse(ThreadLocalRandom rng, Thread thread) { /** * Analyses what has been recorded about the observation and builds the report for it. * - * @return the analyze + * @return the findings this detector collected during the run */ - public Report analyze() { Report r = new Report(); for (State s : instances.values()) { @@ -136,9 +135,9 @@ public Report analyze() { } public static final class Report { - /** The violations. */ + /** Findings as human-readable lines, for the text report. */ public final List violations = new ArrayList<>(); - /** The structured violations. */ + /** The same findings as {@link se.deversity.asynctest.report.Violation} objects, for machine-readable reports. */ public final List structuredViolations = new ArrayList<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadPoolMonitor.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadPoolMonitor.java index 67bd483f..9383aea8 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadPoolMonitor.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadPoolMonitor.java @@ -46,11 +46,11 @@ private static class PoolState { /** * Register a thread pool for monitoring. * - * @param executor the executor - * @param name the name - * @param coreSize the core size - * @param maxSize the max size - * @param queueCapacity the queue capacity + * @param executor the executor being recorded, tracked by identity + * @param name a label identifying the executor in the report + * @param coreSize the configured core pool size + * @param maxSize the configured maximum pool size + * @param queueCapacity the configured work-queue capacity */ public void registerPool(Object executor, String name, int coreSize, int maxSize, int queueCapacity) { if (!enabled) return; @@ -62,7 +62,7 @@ public void registerPool(Object executor, String name, int coreSize, int maxSize /** * Record task submission. * - * @param executor the executor + * @param executor the executor being recorded, tracked by identity */ public void recordTaskSubmitted(Object executor) { if (!enabled) return; @@ -78,7 +78,7 @@ public void recordTaskSubmitted(Object executor) { /** * Record task execution start. * - * @param executor the executor + * @param executor the executor being recorded, tracked by identity */ public void recordTaskStarted(Object executor) { if (!enabled) return; @@ -94,7 +94,7 @@ public void recordTaskStarted(Object executor) { /** * Record task completion. * - * @param executor the executor + * @param executor the executor being recorded, tracked by identity * @param durationMs the duration in milliseconds */ public void recordTaskCompleted(Object executor, long durationMs) { @@ -112,8 +112,8 @@ public void recordTaskCompleted(Object executor, long durationMs) { /** * Record task rejection. * - * @param executor the executor - * @param reason the reason + * @param executor the executor being recorded, tracked by identity + * @param reason why the event was recorded, shown in the report */ public void recordTaskRejected(Object executor, String reason) { if (!enabled) return; @@ -130,7 +130,7 @@ public void recordTaskRejected(Object executor, String reason) { /** * Analyze pool health. * - * @return the analyze pool health + * @return the findings this detector collected during the run */ public ThreadPoolReport analyzePoolHealth() { ThreadPoolReport report = new ThreadPoolReport(); @@ -171,7 +171,7 @@ public ThreadPoolReport analyzePoolHealth() { /** * Standardized alias for {@link #analyzePoolHealth()}. * - * @return the analyze + * @return the findings this detector collected during the run */ public ThreadPoolReport analyze() { return analyzePoolHealth(); @@ -179,33 +179,30 @@ public ThreadPoolReport analyze() { /** * Clears recorded the observation so this instance can be reused for the next run. */ - public void reset() { pools.clear(); } /** * Disable. */ - public void disable() { enabled = false; } /** * Enable. */ - public void enable() { enabled = true; } public static class ThreadPoolReport { - /** The pools with rejections. */ + /** Pools that rejected at least one submission. */ public final Set poolsWithRejections = new HashSet<>(); - /** The saturated queues. */ + /** Work queues observed at their capacity. */ public final Set saturatedQueues = new HashSet<>(); - /** The long running tasks. */ + /** Tasks that ran past the reporting threshold. */ public final Set longRunningTasks = new HashSet<>(); - /** The thread starvation. */ + /** Pools where work waited because no worker was free. */ public final Set threadStarvation = new HashSet<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadStarvationDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadStarvationDetector.java index 4b6b55f4..a24f8ae2 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadStarvationDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/ThreadStarvationDetector.java @@ -85,7 +85,7 @@ private static class TaskEvent { /** * Register an executor for starvation monitoring. * - * @param executor the executor + * @param executor the executor being recorded, tracked by identity * @param name a descriptive name * @param poolSize the number of threads in the pool */ @@ -99,7 +99,7 @@ public void registerExecutor(ExecutorService executor, String name, int poolSize /** * Record a task being submitted to an executor. * - * @param executor the executor + * @param executor the executor being recorded, tracked by identity * @return the submission timestamp in nanoseconds */ public long recordTaskSubmission(ExecutorService executor) { @@ -119,7 +119,7 @@ public long recordTaskSubmission(ExecutorService executor) { /** * Record a task starting execution. * - * @param executorName the executor name + * @param executorName a label identifying the executor in the report * @param submitTimeNanos the submission time (from recordTaskSubmission) */ public void recordTaskStart(String executorName, long submitTimeNanos) { @@ -162,7 +162,7 @@ public void recordTaskStart(String executorName, long submitTimeNanos) { /** * Record a task completing execution. * - * @param executorName the executor name + * @param executorName a label identifying the executor in the report */ public void recordTaskEnd(String executorName) { if (!enabled) return; @@ -227,7 +227,6 @@ public void clear() { /** * Disable. */ - public void disable() { this.enabled = false; } @@ -245,13 +244,13 @@ public void setStarvationThresholdMs(long thresholdMs) { * Immutable snapshot of a starvation event. */ public static class StarvationEventSnapshot { - /** The executor name. */ + /** Label identifying the executor in the report. */ public final String executorName; /** The wait time in milliseconds. */ public final long waitTimeMs; /** The execution time in milliseconds. */ public final long executionTimeMs; - /** The thread name. */ + /** Label identifying the sleeping thread in the report. */ public final String threadName; StarvationEventSnapshot(String executorName, long waitTimeMs, diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/TimerDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/TimerDetector.java index e6d0d801..7ea0162d 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/TimerDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/TimerDetector.java @@ -162,7 +162,7 @@ private TimerState resolve(java.util.Timer timer, String name) { /** * Analyse Timer usage and return a report. * - * @return the analyze + * @return the findings this detector collected during the run */ public TimerReport analyze() { TimerReport report = new TimerReport(); @@ -221,7 +221,7 @@ public static class TimerReport { /** * Returns {@code true} when timer thread failures or long-running tasks were detected. * - * @return the has issues + * @return {@code true} when this detector recorded something worth reporting */ public boolean hasIssues() { return !timerThreadFailures.isEmpty() || !longRunningTaskWarnings.isEmpty(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/TryLockMisuseDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/TryLockMisuseDetector.java index b6bc565d..6f13dd0d 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/TryLockMisuseDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/TryLockMisuseDetector.java @@ -38,10 +38,10 @@ private static final class State { /** * Record the result of a tryLock() call. * - * @param lock the lock - * @param lockName the lock name + * @param lock the lock being recorded, tracked by identity rather than equality + * @param lockName a label identifying the lock in the report * @param acquired the {@code acquired} flag - * @param thread the thread + * @param thread the thread performing the operation */ public void recordTryLockResult(Object lock, String lockName, boolean acquired, Thread thread) { if (lock == null || thread == null) return; @@ -52,9 +52,9 @@ public void recordTryLockResult(Object lock, String lockName, boolean acquired, /** * Record an unlock() call. * - * @param lock the lock - * @param lockName the lock name - * @param thread the thread + * @param lock the lock being recorded, tracked by identity rather than equality + * @param lockName a label identifying the lock in the report + * @param thread the thread performing the operation */ public void recordUnlock(Object lock, String lockName, Thread thread) { if (lock == null || thread == null) return; @@ -74,9 +74,8 @@ public void recordUnlock(Object lock, String lockName, Thread thread) { /** * Analyses what has been recorded about the observation and builds the report for it. * - * @return the analyze + * @return the findings this detector collected during the run */ - public Report analyze() { Report r = new Report(); for (State s : violations.values()) { @@ -101,9 +100,9 @@ public Report analyze() { } public static final class Report { - /** The violations. */ + /** Findings as human-readable lines, for the text report. */ public final List violations = new ArrayList<>(); - /** The structured violations. */ + /** The same findings as {@link se.deversity.asynctest.report.Violation} objects, for machine-readable reports. */ public final List structuredViolations = new ArrayList<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/UnboundedQueueDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/UnboundedQueueDetector.java index 388ec533..680cde24 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/UnboundedQueueDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/UnboundedQueueDetector.java @@ -92,7 +92,7 @@ public void recordQueueCreation(BlockingQueue queue, String name, int capacit /** * Record an enqueue operation. * - * @param queue the queue + * @param queue the queue being recorded, tracked by identity */ public void recordEnqueue(BlockingQueue queue) { if (!enabled || queue == null) return; @@ -131,7 +131,7 @@ public void recordEnqueue(BlockingQueue queue) { /** * Record a dequeue operation. * - * @param queue the queue + * @param queue the queue being recorded, tracked by identity */ public void recordDequeue(BlockingQueue queue) { if (!enabled || queue == null) return; @@ -198,7 +198,6 @@ public void clear() { /** * Disable. */ - public void disable() { this.enabled = false; } @@ -206,7 +205,7 @@ public void disable() { /** * Set the warning threshold for queue size. * - * @param threshold the threshold + * @param threshold the value above which this detector reports */ public void setWarningThreshold(int threshold) { this.warningThreshold = threshold; @@ -216,14 +215,14 @@ public void setWarningThreshold(int threshold) { * An unbounded queue event. */ public static class UnboundedQueueEvent { - /** The queue name. */ + /** Label identifying the queue in the report. */ public final String queueName; - /** The description. */ + /** What was observed, in the wording used by the report. */ public final String description; - /** The capacity. */ + /** Declared capacity of the queue, or unbounded when none was given. */ public final int capacity; public final StackTraceElement @Nullable [] creationStack; - /** The fix suggestion. */ + /** The change suggested to the reader of the report. */ public final String fixSuggestion; UnboundedQueueEvent(String queueName, String description, int capacity, diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/UncommittedChangesDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/UncommittedChangesDetector.java index adf87d22..f5c80ed4 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/UncommittedChangesDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/UncommittedChangesDetector.java @@ -155,7 +155,7 @@ private UncommittedChangesReport(GitStatus status) { /** * Check if any untracked or uncommitted changes were detected. * - * @return the has issues + * @return {@code true} when this detector recorded something worth reporting */ public boolean hasIssues() { return !uncommittedFiles.isEmpty() || !untrackedFiles.isEmpty() || error != null; diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadCarrierExhaustionDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadCarrierExhaustionDetector.java index 267c8842..b7d24df3 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadCarrierExhaustionDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadCarrierExhaustionDetector.java @@ -55,7 +55,9 @@ public class VirtualThreadCarrierExhaustionDetector { private final AtomicInteger exhaustionEvents = new AtomicInteger(0); private final List exhaustionDetails = Collections.synchronizedList(new ArrayList<>()); private final Map activeBlocksByThread = new ConcurrentHashMap<>(); - + /** + * Creates a VirtualThreadCarrierExhaustionDetector. + */ public VirtualThreadCarrierExhaustionDetector() { this(availableCarriers()); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadCpuBoundTaskDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadCpuBoundTaskDetector.java index 906f7580..79f986c7 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadCpuBoundTaskDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadCpuBoundTaskDetector.java @@ -84,11 +84,17 @@ long maxSegmentMs() { private final AtomicInteger totalTasks = new AtomicInteger(0); private final AtomicLong totalDurationMs = new AtomicLong(0); private final AtomicLong maxObservedMs = new AtomicLong(0); - + /** + * Creates a VirtualThreadCpuBoundTaskDetector. + */ public VirtualThreadCpuBoundTaskDetector() { this(DEFAULT_CPU_THRESHOLD_MS); } - + /** + * Creates a VirtualThreadCpuBoundTaskDetector. + * + * @param cpuThresholdMs the cpu threshold in milliseconds + */ public VirtualThreadCpuBoundTaskDetector(long cpuThresholdMs) { this.cpuThresholdMs = cpuThresholdMs; } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadPinningDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadPinningDetector.java index 20e7361d..70c8f6af 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadPinningDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadPinningDetector.java @@ -81,8 +81,8 @@ public enum PinningCause { * * @since 1.7.0 * - * @param blockingOperation the blocking operation - * @return the classify operation + * @param blockingOperation the blocking operation that was called, as it should appear in the report + * @return the pinning cause that operation falls under */ public static PinningCause classifyOperation(String blockingOperation) { if (blockingOperation == null) return PinningCause.OTHER; @@ -108,9 +108,9 @@ public static PinningCause classifyOperation(String blockingOperation) { * * @since 1.7.0 * - * @param cause the cause - * @param jdkFeatureVersion the jdk feature version - * @return the still pins on + * @param cause what pinned the virtual thread to its carrier + * @param jdkFeatureVersion the JDK feature version to evaluate the finding against + * @return {@code true} when that cause still pins a virtual thread on the given JDK */ public static boolean stillPinsOn(PinningCause cause, int jdkFeatureVersion) { return switch (cause) { @@ -363,7 +363,7 @@ public boolean hasEffectivePinningIssues() { * * @since 1.7.0 * - * @return the get obsolete event count + * @return the number of recorded events that no longer pin on the running JDK */ public long getObsoleteEventCount() { return events.stream().filter(PinningEventSnapshot::isObsoleteOnCurrentJdk).count(); @@ -457,7 +457,7 @@ public static class PinningEventSnapshot { * * @since 1.7.0 * - * @return the get cause + * @return what pinned the virtual thread to its carrier */ public PinningCause getCause() { return cause; @@ -470,7 +470,7 @@ public PinningCause getCause() { * * @since 1.7.0 * - * @return the is obsolete on current jdk + * @return {@code true} when this event no longer pins on the running JDK */ public boolean isObsoleteOnCurrentJdk() { return obsoleteOnCurrentJdk; diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadStressConfig.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadStressConfig.java index 9e6aeb14..c3d460a9 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadStressConfig.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VirtualThreadStressConfig.java @@ -25,7 +25,7 @@ public enum StressLevel { /** 100,000+ virtual threads - extreme stress (may require -Xmx settings) */ EXTREME(100000); - /** The thread count. */ + /** How many virtual threads the stress run starts. */ public final int threadCount; StressLevel(int threadCount) { @@ -37,7 +37,14 @@ public enum StressLevel { private final boolean detectThreadPinning; private final boolean enableVirtualThreadEvents; private final long timeoutMs; - + /** + * Creates a VirtualThreadStressConfig. + * + * @param stressLevel the stress level to apply + * @param detectThreadPinning the {@code detectThreadPinning} flag + * @param enableVirtualThreadEvents the {@code enableVirtualThreadEvents} flag + * @param timeoutMs the timeout in milliseconds + */ public VirtualThreadStressConfig(StressLevel stressLevel, boolean detectThreadPinning, boolean enableVirtualThreadEvents, @@ -50,7 +57,6 @@ public VirtualThreadStressConfig(StressLevel stressLevel, /** * {@return the builder} */ - public static Builder builder() { return new Builder(); } @@ -98,10 +104,9 @@ public static class Builder { /** * Stress level. * - * @param level the level - * @return the stress level + * @param level the stress level to apply + * @return this builder */ - public Builder stressLevel(StressLevel level) { this.stressLevel = level; return this; @@ -109,10 +114,9 @@ public Builder stressLevel(StressLevel level) { /** * Detect thread pinning. * - * @param detect the detect - * @return the detect thread pinning + * @param detect {@code true} to enable this detector for the run + * @return this builder */ - public Builder detectThreadPinning(boolean detect) { this.detectThreadPinning = detect; return this; @@ -120,10 +124,9 @@ public Builder detectThreadPinning(boolean detect) { /** * Enable virtual thread events. * - * @param enable the enable - * @return the enable virtual thread events + * @param enable {@code true} to enable this detector for the run + * @return this builder */ - public Builder enableVirtualThreadEvents(boolean enable) { this.enableVirtualThreadEvents = enable; return this; @@ -131,10 +134,9 @@ public Builder enableVirtualThreadEvents(boolean enable) { /** * Timeout in milliseconds. * - * @param timeout the timeout + * @param timeout the timeout supplied by the caller * @return the timeout in milliseconds */ - public Builder timeoutMs(long timeout) { this.timeoutMs = timeout; return this; @@ -142,7 +144,6 @@ public Builder timeoutMs(long timeout) { /** * {@return the build} */ - public VirtualThreadStressConfig build() { return new VirtualThreadStressConfig(stressLevel, detectThreadPinning, enableVirtualThreadEvents, timeoutMs); @@ -152,7 +153,7 @@ public VirtualThreadStressConfig build() { /** * Helper to check if this JVM supports virtual threads (Java 21+). * - * @return the is virtual thread supported + * @return {@code true} when the running JDK provides virtual threads */ public static boolean isVirtualThreadSupported() { try { @@ -167,7 +168,7 @@ public static boolean isVirtualThreadSupported() { * Utility to create a virtual thread executor with potential pinning detection. * Returns executor class name if virtual threads are available. * - * @return the get virtual thread executor class + * @return the class name of the virtual-thread executor, for reporting */ public static String getVirtualThreadExecutorClass() { if (isVirtualThreadSupported()) { diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VisibilityMonitor.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VisibilityMonitor.java index 9b5cd86c..b3a70a09 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VisibilityMonitor.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VisibilityMonitor.java @@ -39,8 +39,8 @@ private static final class FieldSnapshot { * Record a field access. Call this from test code to track when a field is read/written. * Format: className.fieldName * - * @param fieldIdentifier the field identifier - * @param value the value + * @param fieldIdentifier the {@code Type.field} the access was on + * @param value the value read or written */ public void recordFieldAccess(String fieldIdentifier, Object value) { if (!enabled) return; @@ -68,7 +68,7 @@ public void markInvocationStart() { /** * Analyze visibility issues. Returns a report of suspected visibility issues. * - * @return the analyze visibility + * @return the findings this detector collected during the run */ public VisibilityReport analyzeVisibility() { VisibilityReport report = new VisibilityReport(); @@ -101,7 +101,7 @@ public VisibilityReport analyzeVisibility() { /** * Standardized alias for {@link #analyzeVisibility()}. * - * @return the analyze + * @return the findings this detector collected during the run */ public VisibilityReport analyze() { return analyzeVisibility(); @@ -109,7 +109,6 @@ public VisibilityReport analyze() { /** * Clears recorded the observation so this instance can be reused for the next run. */ - public void reset() { fieldSnapshots.clear(); seenValues.clear(); @@ -118,22 +117,20 @@ public void reset() { /** * Disable. */ - public void disable() { enabled = false; } /** * Enable. */ - public void enable() { enabled = true; } public static class VisibilityReport { - /** The suspected fields. */ + /** Fields where two threads observed different values at the same time. */ public final Set suspectedFields = new HashSet<>(); - /** The field value variations. */ + /** Values each thread observed per field, used to spot stale reads. */ public final Map>> fieldValueVariations = new HashMap<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VolatileArrayDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VolatileArrayDetector.java index 624b605f..36c30700 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VolatileArrayDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/VolatileArrayDetector.java @@ -29,9 +29,9 @@ public class VolatileArrayDetector { /** * Register a volatile array for monitoring. * - * @param array the array - * @param name the name - * @param componentType the component type + * @param array the array being recorded, tracked by identity + * @param name a label identifying the array in the report + * @param componentType the component type of the array */ public void registerArray(Object array, String name, Class componentType) { ArrayInfo info = new ArrayInfo(name, array, componentType); @@ -41,9 +41,9 @@ public void registerArray(Object array, String name, Class componentType) { /** * Record a write to an array element. * - * @param array the array - * @param index the index - * @param arrayName the array name + * @param array the array being recorded, tracked by identity + * @param index the array index being accessed + * @param arrayName a label identifying the array in the report */ public void recordElementWrite(Object array, int index, String arrayName) { ArrayInfo info = findArrayInfo(array, arrayName); @@ -70,9 +70,9 @@ public void recordElementWrite(Object array, int index, String arrayName) { /** * Record a read from an array element. * - * @param array the array - * @param index the index - * @param arrayName the array name + * @param array the array being recorded, tracked by identity + * @param index the array index being accessed + * @param arrayName a label identifying the array in the report */ public void recordElementRead(Object array, int index, String arrayName) { ArrayInfo info = findArrayInfo(array, arrayName); @@ -97,7 +97,7 @@ public void recordElementRead(Object array, int index, String arrayName) { /** * Analyze array access patterns and return report. * - * @return the analyze + * @return the findings this detector collected during the run */ public VolatileArrayReport analyze() { return new VolatileArrayReport( @@ -110,7 +110,11 @@ public VolatileArrayReport analyze() { */ public static class VolatileArrayReport { private final Set problematicArrays; - + /** + * Creates a VolatileArrayReport. + * + * @param problematicArrays the arrays whose elements were accessed without the ordering the code assumes + */ public VolatileArrayReport( Set problematicArrays ) { diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WaitTimeoutDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WaitTimeoutDetector.java index 754fb985..90c42e39 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WaitTimeoutDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WaitTimeoutDetector.java @@ -35,9 +35,9 @@ public class WaitTimeoutDetector { /** * Register a wait() call without timeout. * - * @param monitor the monitor - * @param monitorName the monitor name - * @param threadName the thread name + * @param monitor the object being used as a monitor, tracked by identity + * @param monitorName a label identifying the monitor in the report + * @param threadName a label identifying the thread in the report */ public void recordInfiniteWait(Object monitor, String monitorName, String threadName) { WaitInfo info = new WaitInfo(monitor, monitorName); @@ -49,9 +49,9 @@ public void recordInfiniteWait(Object monitor, String monitorName, String thread /** * Register a wait() call with timeout. * - * @param monitor the monitor - * @param monitorName the monitor name - * @param threadName the thread name + * @param monitor the object being used as a monitor, tracked by identity + * @param monitorName a label identifying the monitor in the report + * @param threadName a label identifying the thread in the report * @param timeoutMs the timeout in milliseconds */ public void recordTimedWait(Object monitor, String monitorName, String threadName, long timeoutMs) { @@ -63,8 +63,8 @@ public void recordTimedWait(Object monitor, String monitorName, String threadNam /** * Record a notify() call. * - * @param monitor the monitor - * @param monitorName the monitor name + * @param monitor the object being used as a monitor, tracked by identity + * @param monitorName a label identifying the monitor in the report */ public void recordNotify(Object monitor, String monitorName) { WaitInfo info = new WaitInfo(monitor, monitorName); @@ -75,8 +75,8 @@ public void recordNotify(Object monitor, String monitorName) { /** * Record a notifyAll() call. * - * @param monitor the monitor - * @param monitorName the monitor name + * @param monitor the object being used as a monitor, tracked by identity + * @param monitorName a label identifying the monitor in the report */ public void recordNotifyAll(Object monitor, String monitorName) { WaitInfo info = new WaitInfo(monitor, monitorName); @@ -87,7 +87,7 @@ public void recordNotifyAll(Object monitor, String monitorName) { /** * Analyze wait patterns and return report. * - * @return the analyze + * @return the findings this detector collected during the run */ public WaitTimeoutReport analyze() { return new WaitTimeoutReport(waitEvents, infiniteWaits); @@ -99,7 +99,12 @@ public WaitTimeoutReport analyze() { public static class WaitTimeoutReport { private final Map> waitEvents; private final Set infiniteWaits; - + /** + * Creates a WaitTimeoutReport. + * + * @param waitEvents every recorded wait and what was observed on it + * @param infiniteWaits the waits entered without a timeout + */ public WaitTimeoutReport( Map> waitEvents, Set infiniteWaits diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WakeupDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WakeupDetector.java index 00eefecb..334ea426 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WakeupDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WakeupDetector.java @@ -39,7 +39,7 @@ private static class MonitorState { /** * Record that a thread is about to wait on a monitor. * - * @param monitor the monitor + * @param monitor the object being used as a monitor, tracked by identity */ public void recordWaitEnter(Object monitor) { if (!enabled) return; @@ -59,7 +59,7 @@ public void recordWaitEnter(Object monitor) { /** * Record that a thread has exited wait (either notified or spurious). * - * @param monitor the monitor + * @param monitor the object being used as a monitor, tracked by identity * @param wasNotified the {@code wasNotified} flag */ public void recordWaitExit(Object monitor, boolean wasNotified) { @@ -86,7 +86,7 @@ public void recordWaitExit(Object monitor, boolean wasNotified) { /** * Record a notify call on a monitor. * - * @param monitor the monitor + * @param monitor the object being used as a monitor, tracked by identity * @param notifyAll the {@code notifyAll} flag */ public void recordNotify(Object monitor, boolean notifyAll) { @@ -115,7 +115,7 @@ public void recordNotify(Object monitor, boolean notifyAll) { /** * Analyze wakeup patterns for issues. * - * @return the analyze wakeups + * @return the findings this detector collected during the run */ public WakeupReport analyzeWakeups() { WakeupReport report = new WakeupReport(); @@ -150,7 +150,7 @@ public WakeupReport analyzeWakeups() { /** * Standardized alias for {@link #analyzeWakeups()}. * - * @return the analyze + * @return the findings this detector collected during the run */ public WakeupReport analyze() { return analyzeWakeups(); @@ -158,31 +158,28 @@ public WakeupReport analyze() { /** * Clears recorded the observation so this instance can be reused for the next run. */ - public void reset() { monitors.clear(); } /** * Disable. */ - public void disable() { enabled = false; } /** * Enable. */ - public void enable() { enabled = true; } public static class WakeupReport { - /** The monitors with spurious wakeups. */ + /** Monitors whose waiters woke without a matching notification. */ public final Set monitorsWithSpuriousWakeups = new HashSet<>(); - /** The monitors with lost notifications. */ + /** Monitors where a notification arrived before the waiter blocked. */ public final Set monitorsWithLostNotifications = new HashSet<>(); - /** The always notify without wait. */ + /** Monitors notified while nothing was waiting, so the signal was lost. */ public final Set alwaysNotifyWithoutWait = new HashSet<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WeakHashMapSharedDetector.java b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WeakHashMapSharedDetector.java index 3a9b4d1f..76a50c2a 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WeakHashMapSharedDetector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/diagnostics/WeakHashMapSharedDetector.java @@ -91,9 +91,8 @@ public void recordAccess(Map map, String name, Thread thread) { /** * Analyses what has been recorded about the observation and builds the report for it. * - * @return the analyze + * @return the findings this detector collected during the run */ - public Report analyze() { Report r = new Report(); for (State s : instances.values()) { @@ -128,9 +127,9 @@ public Report analyze() { } public static final class Report { - /** The violations. */ + /** Findings as human-readable lines, for the text report. */ public final List violations = new ArrayList<>(); - /** The structured violations. */ + /** The same findings as {@link se.deversity.asynctest.report.Violation} objects, for machine-readable reports. */ public final List structuredViolations = new ArrayList<>(); /** diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/extension/AsyncTestInvocationInterceptor.java b/async-test-lib/src/main/java/se/deversity/asynctest/extension/AsyncTestInvocationInterceptor.java index 1a221f0c..e02df7bf 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/extension/AsyncTestInvocationInterceptor.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/extension/AsyncTestInvocationInterceptor.java @@ -23,7 +23,11 @@ public class AsyncTestInvocationInterceptor implements InvocationInterceptor { private final AsyncTest asyncTest; private final int threadCount; - + /** + * Creates a AsyncTestInvocationInterceptor. + * + * @param asyncTest the annotation on the test method, supplying the run configuration + */ public AsyncTestInvocationInterceptor(AsyncTest asyncTest) { this(asyncTest, asyncTest.threads()); } @@ -34,6 +38,9 @@ public AsyncTestInvocationInterceptor(AsyncTest asyncTest) { * its own count. * * @since 1.6.0 + * + * @param asyncTest the annotation on the test method, supplying the run configuration + * @param threadCount thread count to use instead of the annotation value, for a parameterised template */ public AsyncTestInvocationInterceptor(AsyncTest asyncTest, int threadCount) { this.asyncTest = asyncTest; diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/report/Baseline.java b/async-test-lib/src/main/java/se/deversity/asynctest/report/Baseline.java index fbfde708..9b298283 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/report/Baseline.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/report/Baseline.java @@ -68,7 +68,7 @@ private Baseline(Set entries) { * baseline when the property is unset. A missing file is treated as empty in * update mode (it will be created) and logged once otherwise. * - * @return the from system properties + * @return the baseline named by the system properties, or an empty one when none is configured */ public static Baseline fromSystemProperties() { String prop = System.getProperty(PATH_PROPERTY); @@ -81,7 +81,7 @@ public static Baseline fromSystemProperties() { /** * Whether {@value #UPDATE_PROPERTY} is set, switching the gate to record mode. * - * @return the update mode + * @return {@code true} when findings are being recorded into the baseline rather than gated on */ public static boolean updateMode() { return Boolean.getBoolean(UPDATE_PROPERTY); @@ -90,8 +90,8 @@ public static boolean updateMode() { /** * Loads (with caching by last-modified time) the baseline at {@code path}. * - * @param path the path - * @return the load + * @param path the baseline file to read; a missing file yields an empty baseline rather than an error + * @return the baseline read from that file, or an empty baseline when the file does not exist */ public static Baseline load(Path path) { if (!Files.exists(path)) { @@ -125,9 +125,9 @@ public static Baseline load(Path path) { /** * Returns {@code true} when the (test, detector) finding is baselined. * - * @param testId the test id - * @param detectorName the detector name - * @return the contains + * @param testId the test the finding was raised against + * @param detectorName the detector that raised the finding + * @return {@code true} when that finding is already baselined and must not fail the build */ public boolean contains(String testId, String detectorName) { return entries.contains(key(testId, detectorName)); @@ -136,7 +136,7 @@ public boolean contains(String testId, String detectorName) { /** * Number of entries in this baseline. * - * @return the size + * @return the number of baselined findings */ public int size() { return entries.size(); @@ -149,8 +149,8 @@ public int size() { * * @return the number of entries actually added * - * @param testId the test id - * @param detectorNames the detector names + * @param testId the test the finding was raised against + * @param detectorNames the detectors to baseline for this test; entries already present are skipped */ public static int record(String testId, Collection detectorNames) { String prop = System.getProperty(PATH_PROPERTY); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/report/DetectorFinding.java b/async-test-lib/src/main/java/se/deversity/asynctest/report/DetectorFinding.java index 8f12031f..a91f0d61 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/report/DetectorFinding.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/report/DetectorFinding.java @@ -7,15 +7,22 @@ */ public final class DetectorFinding { - /** The detector name. */ + /** Detector that raised this finding. */ public final String detectorName; - /** The severity. */ + /** How the finding is weighed by the {@code failOn} gate. */ public final IssueSeverity severity; - /** The report. */ + /** Human-readable detail shown in the report. */ public final String report; /** The timestamp in milliseconds. */ public final long timestampMs; - + /** + * Creates a DetectorFinding. + * + * @param detectorName the detector that raised this finding + * @param severity how the finding is weighed by the {@code failOn} gate + * @param report the human-readable detail shown in the report + * @param timestampMs the timestamp in milliseconds + */ public DetectorFinding(String detectorName, IssueSeverity severity, String report, long timestampMs) { this.detectorName = detectorName; this.severity = severity != null ? severity : IssueSeverity.HIGH; diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/report/Formatter.java b/async-test-lib/src/main/java/se/deversity/asynctest/report/Formatter.java index 8e0a92f4..c8f9667f 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/report/Formatter.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/report/Formatter.java @@ -27,6 +27,9 @@ public interface Formatter { /** * Render the violations. Empty lists must produce a non-null result * (typically an empty string, or a "no violations" marker — formatter's choice). + * + * @param violations the findings to render, possibly empty but never {@code null} + * @return the rendered report, never {@code null} */ String format(List violations); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/report/JUnitXmlReportListener.java b/async-test-lib/src/main/java/se/deversity/asynctest/report/JUnitXmlReportListener.java index 62aa21e6..9bfb6ba3 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/report/JUnitXmlReportListener.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/report/JUnitXmlReportListener.java @@ -78,6 +78,8 @@ public JUnitXmlReportListener(String outputDir) { } /** + * Creates a JUnitXmlReportListener. + * * @param outputDir the directory to write the XML report into * @param registerShutdownHook whether to register a JVM shutdown hook for auto-flush */ @@ -120,7 +122,7 @@ public void onStructuredReport(String detectorName, IssueSeverity severity, /** * Returns the number of accumulated findings (useful for assertions in tests of this listener). * - * @return the get finding count + * @return the number of findings written so far */ public int getFindingCount() { return findings.size(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/report/JsonReportListener.java b/async-test-lib/src/main/java/se/deversity/asynctest/report/JsonReportListener.java index 71d50613..1d6da7db 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/report/JsonReportListener.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/report/JsonReportListener.java @@ -85,6 +85,8 @@ public JsonReportListener(String outputDir) { } /** + * Creates a JsonReportListener. + * * @param outputDir the directory to write the JSON report into * @param registerShutdownHook whether to register a JVM shutdown hook for auto-flush */ @@ -127,7 +129,7 @@ public void onStructuredReport(String detectorName, IssueSeverity severity, /** * Returns the number of accumulated findings. * - * @return the get finding count + * @return the number of findings written so far */ public int getFindingCount() { return findings.size(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/runner/ConcurrencyRunner.java b/async-test-lib/src/main/java/se/deversity/asynctest/runner/ConcurrencyRunner.java index e75a7e71..2a61aa8d 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/runner/ConcurrencyRunner.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/runner/ConcurrencyRunner.java @@ -86,8 +86,11 @@ public class ConcurrencyRunner { * await and the async-body {@code CompletionStage} wait. See * {@link #resolveTimeoutMultiplier()} for the CI-scaling mechanism itself. * - * @param invocationContext the invocation context - * @param config the config + * @param invocationContext the JUnit invocation context carrying the test instance and method to run + * @param config the resolved configuration deciding threads, rounds, timeout and detectors + * + * @throws Throwable the failure from the test body, unwrapped, or an {@link AssertionError} + * raised by the timeout path or the {@code failOn} gate */ @AILoadBearing( invariant = "The timeoutAlreadyReported flag, and the per-step guarded cleanup in the " diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/runner/LicenseGuard.java b/async-test-lib/src/main/java/se/deversity/asynctest/runner/LicenseGuard.java index c2da3218..5a932b7a 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/runner/LicenseGuard.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/runner/LicenseGuard.java @@ -48,7 +48,7 @@ private LicenseGuard() {} * Validates the license for the given config. Throws {@link SecurityException} * if denied. Subsequent calls with the same fingerprint return immediately. * - * @param config the config + * @param config the configuration whose fingerprint keys the cached decision */ @AIIdempotent(reason = "ConcurrentHashMap.computeIfAbsent guarantees the underlying gate.check fires at most once per Fingerprint; repeat calls return immediately. Denied results consistently throw SecurityException for the same fingerprint.") public static void check(AsyncTestConfig config) { diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/runner/SpinContentionBarrier.java b/async-test-lib/src/main/java/se/deversity/asynctest/runner/SpinContentionBarrier.java index a2c23432..41fbf21e 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/runner/SpinContentionBarrier.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/runner/SpinContentionBarrier.java @@ -66,6 +66,16 @@ public final class SpinContentionBarrier { @SuppressWarnings("unused") private long pad11; @SuppressWarnings("unused") private long pad12; + /** + * Creates a barrier that releases once {@code totalThreads} threads have arrived. + * + *

Unlike {@link java.util.concurrent.CyclicBarrier}, arriving threads spin rather than + * park, so they resume within nanoseconds of the last arrival instead of waiting to be + * scheduled. That is what makes the collision tight enough for a race to reproduce. + * + * @param totalThreads how many threads must arrive before any is released; must be at least 1 + * @throws IllegalArgumentException if {@code totalThreads} is less than 1 + */ public SpinContentionBarrier(int totalThreads) { if (totalThreads < 1) { throw new IllegalArgumentException("totalThreads must be >= 1, got: " + totalThreads); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/spi/Detector.java b/async-test-lib/src/main/java/se/deversity/asynctest/spi/Detector.java index cacda2f3..f4dd0738 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/spi/Detector.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/spi/Detector.java @@ -54,6 +54,8 @@ public interface Detector { * Identity of this detector. Must be a value from the {@link DetectorType} * enum so that {@code @AsyncTest(excludes = {...})} and * {@code Preset.enabled()} can address it. + * + * @return the constant identifying this detector */ DetectorType type(); diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/spi/DetectorFactory.java b/async-test-lib/src/main/java/se/deversity/asynctest/spi/DetectorFactory.java index 1f20adf3..eae846ce 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/spi/DetectorFactory.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/spi/DetectorFactory.java @@ -28,6 +28,8 @@ public interface DetectorFactory { /** * Identity of the detector this factory produces. Must match * {@link Detector#type()} of the instances returned by {@link #create(AsyncTestConfig)}. + * + * @return the constant identifying the detector this factory produces */ DetectorType type(); @@ -35,16 +37,22 @@ public interface DetectorFactory { * Whether this detector is active for the given test configuration. * *

{@code AsyncTestConfig} carries one boolean field per legacy detector - * (~85 fields) — each factory's adapter consults its own flag here. There is + * (132 fields) — each factory's adapter consults its own flag here. There is * no automatic mapping from {@link DetectorType} to a boolean field, so this * method must be implemented by every concrete factory. Returning {@code true} * unconditionally is acceptable for detectors that should always run. + * + * @param config the resolved configuration for the test about to run + * @return {@code true} to build and install this detector for that test */ boolean isEnabledFor(AsyncTestConfig config); /** * Construct a fresh detector instance for one {@code @AsyncTest} method's * invocation rounds. + * + * @param config the resolved configuration for the test about to run + * @return a new detector, not shared with any other test */ Detector create(AsyncTestConfig config); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/spi/DetectorRegistry.java b/async-test-lib/src/main/java/se/deversity/asynctest/spi/DetectorRegistry.java index f901525f..2b67d7cf 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/spi/DetectorRegistry.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/spi/DetectorRegistry.java @@ -71,8 +71,8 @@ private DetectorRegistry(Map detectors) { * through the SPI. It is not the path the runner takes: see * {@link #buildExternal(AsyncTestConfig)}. * - * @param config the config - * @return the build + * @param config the configuration deciding which factories report themselves as enabled + * @return a registry holding every enabled detector, built-in and third-party */ public static DetectorRegistry build(AsyncTestConfig config) { Map detectors = new EnumMap<>(DetectorType.class); @@ -103,8 +103,8 @@ public static DetectorRegistry build(AsyncTestConfig config) { * * @since 1.7.0 * - * @param config the config - * @return the build external + * @param config the configuration deciding which factories report themselves as enabled + * @return a registry holding only the enabled third-party detectors */ public static DetectorRegistry buildExternal(AsyncTestConfig config) { Map detectors = new EnumMap<>(DetectorType.class); @@ -194,6 +194,10 @@ public boolean isEmpty() { *

Calls into user code should treat null as "feature off" rather than an * error — matches the behavior of the legacy {@code AsyncTestContext.require} * accessors but without the exception. + * + * @param the detector type being looked up + * @param detectorClass the class to match against the active detectors + * @return the active detector of that class, or {@code null} when it is not enabled */ @SuppressWarnings("unchecked") public @Nullable T get(Class detectorClass) { @@ -206,8 +210,8 @@ public boolean isEmpty() { /** * Type-keyed lookup. * - * @param type the type - * @return the get + * @param type the detector to look up + * @return the active detector for that type, or {@code null} when it is not enabled */ public @Nullable Detector get(DetectorType type) { return byType.get(type); @@ -216,7 +220,7 @@ public boolean isEmpty() { /** * All active detectors (snapshot). * - * @return the all + * @return the active detectors, in {@link DetectorType} order */ public List all() { return new ArrayList<>(byType.values()); @@ -225,7 +229,7 @@ public List all() { /** * Aggregated violations from every active detector for the current round. * - * @return the analyze all + * @return the violations reported by every active detector */ @AIIdempotent(reason = "Each Detector.analyze() must return the same violations for the same observed state (the SPI contract). Calling analyzeAll() N times on a quiescent registry yields N identical lists; do not introduce stateful side-effects in analyze().") public List analyzeAll() { @@ -248,14 +252,12 @@ public List analyzeAll() { /** * Fire on test start. */ - public void fireOnTestStart() { for (Detector d : byType.values()) d.onTestStart(); } /** * Fire on test end. */ - public void fireOnTestEnd() { for (Detector d : byType.values()) d.onTestEnd(); } diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/spi/adapters/LegacyDetectorAdapter.java b/async-test-lib/src/main/java/se/deversity/asynctest/spi/adapters/LegacyDetectorAdapter.java index bcedde7a..e24e6cdd 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/spi/adapters/LegacyDetectorAdapter.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/spi/adapters/LegacyDetectorAdapter.java @@ -47,7 +47,13 @@ public final class LegacyDetectorAdapter implements Detector { private final @Nullable Method analyzeMethod; private final @Nullable NoSuchMethodException analyzeMethodLookupFailure; private final @Nullable Method hasIssuesMethod; - + /** + * Creates a LegacyDetectorAdapter. + * + * @param delegate the legacy detector to expose through the SPI; findings are read from this instance + * @param type the constant this detector answers to + * @param detectorName the name this detector reports under + */ public LegacyDetectorAdapter(D delegate, DetectorType type, String detectorName) { this.delegate = delegate; this.type = type; @@ -103,7 +109,7 @@ public List analyze() { /** * Exposed for callers that need direct access to the wrapped legacy detector. * - * @return the delegate + * @return the wrapped legacy detector */ public D delegate() { return delegate; diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/spi/adapters/SharedMessageDigestDetectorFactory.java b/async-test-lib/src/main/java/se/deversity/asynctest/spi/adapters/SharedMessageDigestDetectorFactory.java index 60e6a446..dc626e49 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/spi/adapters/SharedMessageDigestDetectorFactory.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/spi/adapters/SharedMessageDigestDetectorFactory.java @@ -44,7 +44,11 @@ public Detector create(AsyncTestConfig config) { */ public static final class Adapter implements Detector { private final SharedMessageDigestDetector delegate; - + /** + * Creates a Adapter. + * + * @param delegate the legacy detector whose findings this adapter republishes + */ public Adapter(SharedMessageDigestDetector delegate) { this.delegate = delegate; } @@ -62,7 +66,7 @@ public List analyze() { /** * Exposed for legacy users that need direct access to the wrapped detector. * - * @return the delegate + * @return the wrapped legacy detector */ public SharedMessageDigestDetector delegate() { return delegate; diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/telemetry/TelemetryEventBuffer.java b/async-test-lib/src/main/java/se/deversity/asynctest/telemetry/TelemetryEventBuffer.java index 4db2786c..1b157183 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/telemetry/TelemetryEventBuffer.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/telemetry/TelemetryEventBuffer.java @@ -76,6 +76,8 @@ static final class AccessEvent { private volatile long consumerCursor = -1; /** + * Creates a TelemetryEventBuffer. + * * @param capacityPowerOfTwo ring-buffer capacity; must be a power of two (e.g. 1024, 4096) */ public TelemetryEventBuffer(int capacityPowerOfTwo) { @@ -157,7 +159,7 @@ public int drain(DrainCallback callback) { /** * Returns the number of events published so far (monotonically increasing). * - * @return the published count + * @return the number of events published since this buffer was created */ public long publishedCount() { return producerCursor.get() + 1; diff --git a/async-test-lib/src/main/java/se/deversity/asynctest/telemetry/TelemetryRegistry.java b/async-test-lib/src/main/java/se/deversity/asynctest/telemetry/TelemetryRegistry.java index 059fa032..f6fda5ea 100644 --- a/async-test-lib/src/main/java/se/deversity/asynctest/telemetry/TelemetryRegistry.java +++ b/async-test-lib/src/main/java/se/deversity/asynctest/telemetry/TelemetryRegistry.java @@ -229,7 +229,7 @@ public static void stop() { /** * Exposes the shared buffer for testing and advanced consumers. * - * @return the buffer + * @return the buffer producers publish into */ public static TelemetryEventBuffer buffer() { return BUFFER; diff --git a/async-test-lib/src/test/java/se/deversity/asynctest/architecture/JavadocDescribesRatherThanRestatesTest.java b/async-test-lib/src/test/java/se/deversity/asynctest/architecture/JavadocDescribesRatherThanRestatesTest.java new file mode 100644 index 00000000..0bffbe68 --- /dev/null +++ b/async-test-lib/src/test/java/se/deversity/asynctest/architecture/JavadocDescribesRatherThanRestatesTest.java @@ -0,0 +1,221 @@ +package se.deversity.asynctest.architecture; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Pins that published javadoc says something the signature does not already say. + * + *

Doclint answers one question: is the tag present. It cannot tell {@code @param timeout the + * timeout} from a description, so a mechanical pass that adds a tag per parameter turns a build + * with hundreds of warnings into a green one without a reader learning anything. That is exactly + * what happened here: closing the warnings left 432 {@code @param} lines, 166 {@code @return} + * lines and 135 one-line summaries that restated the identifier, all on public members, all + * published to whoever depends on this library. One of them read {@code /** The totcou races. *}{@code /} + * over a field recording time-of-check-to-time-of-use races. + * + *

These tests ask the question doclint cannot. A description fails when, ignoring a leading + * article, it is nothing but the identifier it is attached to respelled: {@code @param lockName + * the lock name}, {@code @return the size} on {@code size()}. Anything that adds a unit, a null + * rule, a range, an identity-versus-equality note, or any other fact a caller could not read off + * the signature passes. + * + *

They deliberately do not measure length or wording. A short description can be complete + * ({@code @return this builder}), and no rule about prose style survives contact with 127 + * detectors. The only thing pinned is that the text is not pure restatement. + * + * @see BuildMetadataSyncTest + */ +class JavadocDescribesRatherThanRestatesTest { + + /** Matches a {@code @param} tag, capturing the name and its description. */ + private static final Pattern PARAM = + Pattern.compile("^\\s*\\*\\s*@param\\s+?\\s+(\\S.*?)\\s*$"); + + /** Matches a {@code @return} tag, capturing its description. */ + private static final Pattern RETURN = + Pattern.compile("^\\s*\\*\\s*@return\\s+(\\S.*?)\\s*$"); + + /** Matches a whole javadoc block written on one line, capturing its text. */ + private static final Pattern SUMMARY = + Pattern.compile("^\\s*/\\*\\*\\s*(.+?)\\s*\\*/\\s*$"); + + /** Matches the name of the member a javadoc block sits on: a method, constructor or field. */ + private static final Pattern DECLARED_MEMBER = + Pattern.compile("(\\w+)\\s*(?:[;=]|\\()"); + + /** Splits an identifier into its words, so {@code lockName} and "lock name" compare equal. */ + private static final Pattern IDENTIFIER_WORDS = Pattern.compile("(? offenders = new ArrayList<>(); + for (Path file : mainSources()) { + offenders.addAll(restatementsIn(file)); + } + assertTrue(offenders.isEmpty(), + "These javadoc tags only respell the identifier they document, so they reach " + + "consumers without saying anything the signature does not. Give the " + + "unit, the null rule, the range, or what the value is used for:\n " + + String.join("\n ", offenders)); + } + + @Test + @DisplayName("no one-line javadoc summary in main sources merely restates the identifier") + void summariesDescribeRatherThanRestate() { + List offenders = new ArrayList<>(); + for (Path file : mainSources()) { + List lines = readLines(file); + for (int i = 0; i < lines.size(); i++) { + Matcher m = SUMMARY.matcher(lines.get(i)); + if (!m.matches() || m.group(1).startsWith("{@return")) { + continue; + } + String subject = declaredNameAfter(lines, i); + if (subject != null && restates(subject, m.group(1))) { + offenders.add(report(file, i, lines.get(i))); + } + } + } + assertTrue(offenders.isEmpty(), + "These javadoc summaries only respell the member they describe. A public field " + + "surfaced in a report has to say what its entries mean, not repeat its " + + "own name:\n " + String.join("\n ", offenders)); + } + + /** {@return every restating {@code @param} or {@code @return} tag in {@code file}} */ + private static List restatementsIn(Path file) { + List lines = readLines(file); + List offenders = new ArrayList<>(); + for (int i = 0; i < lines.size(); i++) { + String line = lines.get(i); + + Matcher param = PARAM.matcher(line); + if (param.matches() && restates(param.group(1), param.group(2))) { + offenders.add(report(file, i, line)); + continue; + } + + Matcher ret = RETURN.matcher(line); + if (ret.matches()) { + String subject = declaredNameAfter(lines, i); + if (subject != null && restates(subject, ret.group(1))) { + offenders.add(report(file, i, line)); + } + } + } + return offenders; + } + + /** + * Whether {@code description} is nothing but {@code identifier} respelled. + * + * @param identifier the member or parameter name the text is attached to + * @param description the text as written, without its tag + * @return {@code true} when the description adds nothing to the identifier + */ + private static boolean restates(String identifier, String description) { + String stripped = LEADING_ARTICLE + .matcher(description.toLowerCase(Locale.ROOT).trim()) + .replaceFirst("") + .replaceAll("\\.$", "") + .trim(); + String words = IDENTIFIER_WORDS.matcher(identifier).replaceAll(" ").toLowerCase(Locale.ROOT); + return stripped.equals(words) + || stripped.replace(" ", "").equals(words.replace(" ", "")); + } + + /** + * The member declared after the javadoc block containing line {@code i}, skipping blank + * lines, comments and annotations. + * + * @param lines the file's lines + * @param i the index of the tag or summary being judged + * @return the declared name, or {@code null} when nothing recognisable follows the block + */ + private static String declaredNameAfter(List lines, int i) { + int end = i; + while (end < lines.size() && !lines.get(end).contains("*/")) { + end++; + } + int decl = end + 1; + while (decl < lines.size()) { + String candidate = lines.get(decl).trim(); + if (candidate.isEmpty() || candidate.startsWith("//") || candidate.startsWith("@")) { + decl++; + continue; + } + Matcher m = DECLARED_MEMBER.matcher(candidate); + return m.find() ? m.group(1) : null; + } + return null; + } + + /** {@return a {@code path:line} report for the javadoc on line {@code i}} */ + private static String report(Path file, int i, String line) { + return repoRoot().relativize(file) + ":" + (i + 1) + " " + line.trim(); + } + + /** {@return every {@code .java} file under any published module's {@code src/main/java}} */ + private static List mainSources() { + Path root = repoRoot(); + List files = new ArrayList<>(); + for (String module : List.of("async-test-lib", "async-test-agent", "async-test-analysis")) { + Path src = root.resolve(module).resolve("src/main/java"); + if (!Files.isDirectory(src)) { + continue; + } + try (Stream walk = Files.walk(src)) { + walk.filter(p -> p.toString().endsWith(".java")).forEach(files::add); + } catch (IOException e) { + throw new UncheckedIOException("Could not walk " + src, e); + } + } + assertTrue(files.size() > 100, + "Expected to scan the published sources but found only " + files.size() + + " files under " + root + ". The test is looking in the wrong place, " + + "which would let it pass by scanning nothing."); + return files; + } + + private static List readLines(Path file) { + try { + return Files.readAllLines(file, StandardCharsets.UTF_8); + } catch (IOException e) { + throw new UncheckedIOException("Could not read " + file, e); + } + } + + /** {@return the reactor root, found by walking up to the directory holding the parent pom} */ + private static Path repoRoot() { + Path dir = Path.of("").toAbsolutePath(); + while (dir != null) { + if (Files.exists(dir.resolve("settings.gradle.kts")) + && Files.exists(dir.resolve("pom.xml"))) { + return dir; + } + dir = dir.getParent(); + } + throw new IllegalStateException( + "Could not find the reactor root (a directory holding both pom.xml and " + + "settings.gradle.kts) above " + Path.of("").toAbsolutePath()); + } +} diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index db1351d0..26529ba3 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -7,6 +7,50 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed — 733 published javadoc descriptions said nothing, and a test now says so + +The previous entries in this release closed every doclint warning. Doclint answers exactly one +question, is the tag present, so closing its warnings does not mean a reader learns anything. It +cannot tell `@param timeout the timeout` from a description. Counting what the mechanical pass had +actually produced: + +| Restatement | Count | All on public members | +|---|---|---| +| `@param` tags | 432 | yes | +| `@return` tags | 166 | yes | +| one-line summaries | 135 | yes | + +Every one restated the identifier it documented and nothing else. `@param lockName the lock name`. +`@return the size` on `size()`. The worst read `/** The totcou races. */` over a public field +recording time-of-check-to-time-of-use races, where the generated prose had respelled a misspelled +acronym into a non-word. All 733 now describe something a caller cannot read off the signature: the +unit on `sleepDuration` (nanoseconds), the null rule on `SiteCapture.capture()`, that detectors +track subjects by identity rather than equality, that `distanceInBytes` under a cache line is what +makes two fields share one. + +Two defects surfaced while doing it, both invisible to doclint because it checks nothing below +`protected` by default: + +- `SpinContentionBarrier`'s constructor javadoc had been placed between two cache-line padding + fields, documenting `pad7`. The public constructor had no javadoc at all, and the build was + silent about it. Reattached, and it now states the contract that matters, that arriving threads + spin rather than park so the collision stays tight enough to reproduce a race. +- 183 stray blank lines sat between a javadoc block and the member it documents. + +`totcouRaces` keeps its misspelled name. It is a public field and renaming it would break binary +compatibility against the 1.6.0 baseline; the javadoc now spells out what it records and notes the +name is kept deliberately. + +`JavadocDescribesRatherThanRestatesTest` pins all of this. It asks the question doclint cannot: +whether a description, ignoring a leading article, is anything more than its identifier respelled. +It does not measure length or style, because a short description can be complete (`@return this +builder`) and no prose rule survives contact with 127 detectors. It was verified in both +directions: it failed on the real tree before the fix, naming +`ABAProblemDetector.java:197 * @return the analyze ABA` and one other that the initial sweep's +acronym handling had missed, and putting a single placeholder back afterwards turned it red again +with the exact file and line. It also asserts it scanned more than 100 files, so it cannot pass by +looking in the wrong directory and finding nothing. + ### Added — DetectorType's 127 constants documented, and its lock text corrected The enum a user types into `@AsyncTest(excludes = ...)` had no documentation on any of its 127