diff --git a/.github/workflows/asciidoc.yml b/.github/workflows/asciidoc.yml index 57b376de5..4573d5aad 100644 --- a/.github/workflows/asciidoc.yml +++ b/.github/workflows/asciidoc.yml @@ -4,6 +4,9 @@ on: push: branches: [ master ] +permissions: + contents: write + jobs: checkout-and-deploy: runs-on: ubuntu-latest @@ -21,6 +24,6 @@ jobs: - name: Build ASCIIDoc with Gradle run: ./gradlew clean asciidoctor - name: Simple deploy with git - uses: rdarida/simple-github-pages-deploy-action@v1 + uses: JamesIves/github-pages-deploy-action@v4 with: - git-base-folder: build/docs/asciidoc/en/ + folder: build/docs/asciidoc/en/ diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 5e3db07a8..e19c8801c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,21 +1,9 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# name: "CodeQL" on: push: branches: [ master ] pull_request: - # The branches below must be a subset of the branches above branches: [ master ] schedule: - cron: '33 3 * * 3' @@ -24,50 +12,27 @@ jobs: analyze: name: Analyze runs-on: ubuntu-latest - + permissions: + actions: read + contents: read + security-events: write strategy: fail-fast: false matrix: language: [ 'java' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] - # Learn more: - # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed - steps: - name: Checkout repository - uses: actions/checkout@v2 - - # Initializes the CodeQL tools for scanning. + uses: actions/checkout@v3 - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - - name: Set up JDK 11 uses: actions/setup-java@v2 with: java-version: 11 distribution: 'zulu' - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v1 - - # ℹī¸ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release - + uses: github/codeql-action/autobuild@v2 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 diff --git a/build.gradle b/build.gradle index a5a9e8d7d..888079726 100644 --- a/build.gradle +++ b/build.gradle @@ -19,11 +19,11 @@ plugins { id 'signing' id 'checkstyle' id 'idea' - id "io.github.reyerizo.gradle.jcstress" version "0.8.11" + id "io.github.reyerizo.gradle.jcstress" version "0.8.14" id 'org.asciidoctor.jvm.convert' version '3.3.2' id 'org.asciidoctor.jvm.gems' version '3.3.2' - id "me.champeau.jmh" version "0.6.5" - id "biz.aQute.bnd.builder" version "5.3.0" + id "me.champeau.jmh" version "0.6.8" + id "biz.aQute.bnd.builder" version "6.3.1" } group = 'com.lmax' @@ -46,7 +46,7 @@ apply from: 'gradle/jmh.gradle' apply from: 'gradle/asciidoc.gradle' apply from: 'gradle/jcstress.gradle' -wrapper.gradleVersion = '7.2' +wrapper.gradleVersion = '7.6' repositories { mavenCentral() @@ -56,8 +56,8 @@ repositories { sourceCompatibility = targetCompatibility = JavaVersion.VERSION_11 dependencies { - checkstyle 'com.puppycrawl.tools:checkstyle:9.0.1' - testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1' + checkstyle 'com.puppycrawl.tools:checkstyle:10.4' + testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' testImplementation 'org.hamcrest:hamcrest:2.2' } diff --git a/gradle/jmh.gradle b/gradle/jmh.gradle index c6aa0bde5..8226ec75f 100644 --- a/gradle/jmh.gradle +++ b/gradle/jmh.gradle @@ -9,13 +9,13 @@ sourceSets { } } -def jmhLibVersion = '1.32' +def jmhLibVersion = '1.35' dependencies { jmhImplementation "org.openjdk.jmh:jmh-core:${jmhLibVersion}" jmhAnnotationProcessor "org.openjdk.jmh:jmh-generator-annprocess:${jmhLibVersion}" - jmhImplementation 'net.openhft:affinity:3.20.0' + jmhImplementation 'net.openhft:affinity:3.23.2' } jmh { diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180f2..943f0cbfa 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ffed3a254..f398c33c4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c78733..65dcd68d6 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,10 +80,10 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' @@ -143,12 +143,16 @@ fi if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -205,6 +209,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index ac1b06f93..6689b85be 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/src/main/java/com/lmax/disruptor/EventHandlerBase.java b/src/main/java/com/lmax/disruptor/EventHandlerBase.java index 2ed0db9f6..006125a50 100644 --- a/src/main/java/com/lmax/disruptor/EventHandlerBase.java +++ b/src/main/java/com/lmax/disruptor/EventHandlerBase.java @@ -37,8 +37,9 @@ interface EventHandlerBase * Invoked by {@link BatchEventProcessor} prior to processing a batch of events * * @param batchSize the size of the batch that is starting + * @param queueDepth the total number of queued up events including the batch about to be processed */ - default void onBatchStart(long batchSize) + default void onBatchStart(long batchSize, long queueDepth) { } diff --git a/src/main/java/com/lmax/disruptor/util/Util.java b/src/main/java/com/lmax/disruptor/util/Util.java index 44f1aa887..e573709a2 100644 --- a/src/main/java/com/lmax/disruptor/util/Util.java +++ b/src/main/java/com/lmax/disruptor/util/Util.java @@ -91,18 +91,16 @@ public static Sequence[] getSequencesFor(final EventProcessor... processors) * Calculate the log base 2 of the supplied integer, essentially reports the location * of the highest bit. * - * @param i Value to calculate log2 for. + * @param value Positive value to calculate log2 for. * @return The log2 value */ - public static int log2(final int i) + public static int log2(final int value) { - long value = i; - int r = 0; - while ((value >>= 1) != 0) + if (value < 1) { - ++r; + throw new IllegalArgumentException("value must be a positive number"); } - return r; + return Integer.SIZE - Integer.numberOfLeadingZeros(value) - 1; } /** diff --git a/src/perftest/java/com/lmax/disruptor/offheap/OneToOneOffHeapThroughputTest.java b/src/perftest/java/com/lmax/disruptor/offheap/OneToOneOffHeapThroughputTest.java index 7707dab35..45a89f793 100644 --- a/src/perftest/java/com/lmax/disruptor/offheap/OneToOneOffHeapThroughputTest.java +++ b/src/perftest/java/com/lmax/disruptor/offheap/OneToOneOffHeapThroughputTest.java @@ -137,7 +137,7 @@ public void reset(final CountDownLatch latch, final long expectedCount) } @Override - public void onBatchStart(final long batchSize) + public void onBatchStart(final long batchSize, final long queueDepth) { batchesProcessed.increment(); } diff --git a/src/perftest/java/com/lmax/disruptor/offheap/OneToOneOnHeapThroughputTest.java b/src/perftest/java/com/lmax/disruptor/offheap/OneToOneOnHeapThroughputTest.java index a976cadcd..10df70d2d 100644 --- a/src/perftest/java/com/lmax/disruptor/offheap/OneToOneOnHeapThroughputTest.java +++ b/src/perftest/java/com/lmax/disruptor/offheap/OneToOneOnHeapThroughputTest.java @@ -141,7 +141,7 @@ public void reset(final CountDownLatch latch, final long expectedCount) } @Override - public void onBatchStart(final long batchSize) + public void onBatchStart(final long batchSize, final long queueDepth) { batchesProcessed.increment(); } diff --git a/src/perftest/java/com/lmax/disruptor/support/LongArrayEventHandler.java b/src/perftest/java/com/lmax/disruptor/support/LongArrayEventHandler.java index 4f1b5eae5..9b787a00f 100644 --- a/src/perftest/java/com/lmax/disruptor/support/LongArrayEventHandler.java +++ b/src/perftest/java/com/lmax/disruptor/support/LongArrayEventHandler.java @@ -60,7 +60,7 @@ public void onEvent(final long[] event, final long sequence, final boolean endOf } @Override - public void onBatchStart(final long batchSize) + public void onBatchStart(final long batchSize, final long queueDepth) { batchesProcessed.increment(); } diff --git a/src/perftest/java/com/lmax/disruptor/support/ValueAdditionEventHandler.java b/src/perftest/java/com/lmax/disruptor/support/ValueAdditionEventHandler.java index f1582cbe5..1953902b0 100644 --- a/src/perftest/java/com/lmax/disruptor/support/ValueAdditionEventHandler.java +++ b/src/perftest/java/com/lmax/disruptor/support/ValueAdditionEventHandler.java @@ -57,7 +57,7 @@ public void onEvent(final ValueEvent event, final long sequence, final boolean e } @Override - public void onBatchStart(final long batchSize) + public void onBatchStart(final long batchSize, final long queueDepth) { batchesProcessed.increment(); } diff --git a/src/perftest/java/com/lmax/disruptor/support/ValueMutationEventHandler.java b/src/perftest/java/com/lmax/disruptor/support/ValueMutationEventHandler.java index 03c70899c..b7e20981e 100644 --- a/src/perftest/java/com/lmax/disruptor/support/ValueMutationEventHandler.java +++ b/src/perftest/java/com/lmax/disruptor/support/ValueMutationEventHandler.java @@ -63,7 +63,7 @@ public void onEvent(final ValueEvent event, final long sequence, final boolean e } @Override - public void onBatchStart(final long batchSize) + public void onBatchStart(final long batchSize, final long queueDepth) { batchesProcessed.increment(); } diff --git a/src/test/java/com/lmax/disruptor/BatchEventProcessorTest.java b/src/test/java/com/lmax/disruptor/BatchEventProcessorTest.java index b63d5a45c..c6cd3ee9c 100644 --- a/src/test/java/com/lmax/disruptor/BatchEventProcessorTest.java +++ b/src/test/java/com/lmax/disruptor/BatchEventProcessorTest.java @@ -162,7 +162,7 @@ final class LoopbackEventHandler { @Override - public void onBatchStart(final long batchSize) + public void onBatchStart(final long batchSize, final long queueDepth) { batchSizes.add(batchSize); } @@ -367,7 +367,7 @@ public void onEvent(final StubEvent event, final long sequence, final boolean en } @Override - public void onBatchStart(final long batchSize) + public void onBatchStart(final long batchSize, final long queueDepth) { final Integer currentCount = batchSizeToCountMap.get(batchSize); final int nextCount = null == currentCount ? 1 : currentCount + 1; diff --git a/src/test/java/com/lmax/disruptor/MaxBatchSizeEventProcessorTest.java b/src/test/java/com/lmax/disruptor/MaxBatchSizeEventProcessorTest.java index a96d40ffa..90cff322d 100644 --- a/src/test/java/com/lmax/disruptor/MaxBatchSizeEventProcessorTest.java +++ b/src/test/java/com/lmax/disruptor/MaxBatchSizeEventProcessorTest.java @@ -58,33 +58,18 @@ void setUp() @Test public void shouldLimitTheBatchToConfiguredMaxBatchSize() throws Exception { - long sequence = 0; - for (int i = 0; i < PUBLISH_COUNT; i++) - { - sequence = ringBuffer.next(); - } - ringBuffer.publish(sequence); - - //Wait for consumer to process all events - countDownLatch.await(); + publishEvents(); assertEquals(eventHandler.batchedSequences, Arrays.asList(Arrays.asList(0L, 1L, 2L), Arrays.asList(3L, 4L))); } @Test - public void shouldAnnounceBatchSizeAtTheStartOfBatch() throws Exception + public void shouldAnnounceBatchSizeAndQueueDepthAtTheStartOfBatch() throws Exception { - long sequence = 0; - for (int i = 0; i < PUBLISH_COUNT; i++) - { - sequence = ringBuffer.next(); - } - ringBuffer.publish(sequence); - - //Wait for consumer to process all events - countDownLatch.await(); + publishEvents(); assertEquals(eventHandler.announcedBatchSizes, Arrays.asList(3L, 2L)); + assertEquals(eventHandler.announcedQueueDepths, Arrays.asList(5L, 2L)); } @AfterEach @@ -94,12 +79,26 @@ void tearDown() throws InterruptedException thread.join(); } + private void publishEvents() throws InterruptedException + { + long sequence = 0; + for (int i = 0; i < PUBLISH_COUNT; i++) + { + sequence = ringBuffer.next(); + } + ringBuffer.publish(sequence); + + //Wait for consumer to process all events + countDownLatch.await(); + } + private static class BatchLimitRecordingHandler implements EventHandler { public final List> batchedSequences = new ArrayList<>(); private List currentSequences; private final CountDownLatch countDownLatch; private final List announcedBatchSizes = new ArrayList<>(); + private final List announcedQueueDepths = new ArrayList<>(); BatchLimitRecordingHandler(final CountDownLatch countDownLatch) { @@ -120,10 +119,11 @@ public void onEvent(final StubEvent event, final long sequence, final boolean en } @Override - public void onBatchStart(final long batchSize) + public void onBatchStart(final long batchSize, final long queueDepth) { currentSequences = new ArrayList<>(); announcedBatchSizes.add(batchSize); + announcedQueueDepths.add(queueDepth); } } } diff --git a/src/test/java/com/lmax/disruptor/util/UtilTest.java b/src/test/java/com/lmax/disruptor/util/UtilTest.java index ca9e2b68a..17a05623c 100644 --- a/src/test/java/com/lmax/disruptor/util/UtilTest.java +++ b/src/test/java/com/lmax/disruptor/util/UtilTest.java @@ -19,6 +19,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; public final class UtilTest { @@ -52,4 +53,22 @@ public void shouldReturnLongMaxWhenNoEventProcessors() assertEquals(Long.MAX_VALUE, Util.getMinimumSequence(sequences)); } + + @Test + void shouldThrowErrorIfValuePassedToLog2FunctionIsNotPositive() + { + assertThrows(IllegalArgumentException.class, () -> Util.log2(0)); + assertThrows(IllegalArgumentException.class, () -> Util.log2(-1)); + assertThrows(IllegalArgumentException.class, () -> Util.log2(Integer.MIN_VALUE)); + } + + @Test + void shouldCalculateCorrectlyIntegerFlooredLog2() + { + assertEquals(0, Util.log2(1)); + assertEquals(1, Util.log2(2)); + assertEquals(1, Util.log2(3)); + assertEquals(10, Util.log2(1024)); + assertEquals(30, Util.log2(Integer.MAX_VALUE)); + } }