diff --git a/.github/workflows/actionlint.yml b/.github/workflows/actionlint.yml index 45c73051a7..4e499308eb 100644 --- a/.github/workflows/actionlint.yml +++ b/.github/workflows/actionlint.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: disable-sudo: true egress-policy: block @@ -16,7 +16,7 @@ jobs: github.com:443 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: actionlint - uses: reviewdog/action-actionlint@f3dcc52bc6039e5d736486952379dce3e869e8a2 # v1.63.0 + uses: reviewdog/action-actionlint@abd537417cf4991e1ba8e21a67b1119f4f53b8e0 # v1.64.1 env: SHELLCHECK_OPTS: -e SC2001 -e SC2035 -e SC2046 -e SC2061 -e SC2086 -e SC2156 with: diff --git a/.github/workflows/analysis.yml b/.github/workflows/analysis.yml index 210fed07ab..0c6af7f8ba 100644 --- a/.github/workflows/analysis.yml +++ b/.github/workflows/analysis.yml @@ -26,7 +26,7 @@ jobs: JAVA_VERSION: 23 steps: - name: Harden Runner - uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: disable-sudo: true egress-policy: block @@ -45,7 +45,7 @@ jobs: JAVA_VERSION: 23 steps: - name: Harden Runner - uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: disable-sudo: true egress-policy: block @@ -64,7 +64,7 @@ jobs: JAVA_VERSION: 23 steps: - name: Harden Runner - uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: disable-sudo: true egress-policy: block diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 5418b43ef6..f611201797 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -16,7 +16,7 @@ jobs: JAVA_VERSION: ${{ matrix.java }} steps: - name: Harden Runner - uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: disable-sudo: true egress-policy: block diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 020eb67426..033d0ac6b7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -52,7 +52,7 @@ jobs: JAVA_VERSION: ${{ matrix.java }} steps: - name: Harden Runner - uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: disable-sudo: true egress-policy: block @@ -166,7 +166,7 @@ jobs: JAVA_VERSION: ${{ matrix.java }} steps: - name: Harden Runner - uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: disable-sudo: true egress-policy: block @@ -209,7 +209,7 @@ jobs: if: (github.event_name == 'push') && (github.event.repository.fork == false) steps: - name: Harden Runner - uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: disable-sudo: true egress-policy: block @@ -287,7 +287,7 @@ jobs: checks: write steps: - name: Harden Runner - uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: disable-sudo: true egress-policy: block @@ -341,7 +341,7 @@ jobs: id-token: write steps: - name: Harden Runner - uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: disable-sudo: true egress-policy: block diff --git a/.github/workflows/codacy.yml b/.github/workflows/codacy.yml index 434ce64d1f..ce8c55e457 100644 --- a/.github/workflows/codacy.yml +++ b/.github/workflows/codacy.yml @@ -13,7 +13,7 @@ jobs: if: github.event.repository.fork == false steps: - name: Harden Runner - uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: disable-sudo: true egress-policy: block @@ -47,7 +47,7 @@ jobs: if: steps.check_files.outputs.files_exists == 'true' run: jq -c '.runs |= unique_by({tool, invocations, results})' < results.sarif > codacy.sarif - name: Upload result to GitHub Code Scanning - uses: github/codeql-action/upload-sarif@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1 + uses: github/codeql-action/upload-sarif@d68b2d4edb4189fd2a5366ac14e72027bd4b37dd # v3.28.2 if: steps.check_files.outputs.files_exists == 'true' continue-on-error: true with: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 61db4a344a..ab7b775400 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -28,7 +28,7 @@ jobs: language: [ actions, java ] steps: - name: Harden Runner - uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: disable-sudo: true egress-policy: block @@ -56,12 +56,12 @@ jobs: java: ${{ env.JAVA_VERSION }} cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - name: Initialize CodeQL (Actions) - uses: github/codeql-action/init@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1 + uses: github/codeql-action/init@d68b2d4edb4189fd2a5366ac14e72027bd4b37dd # v3.28.2 if: ${{ matrix.language == 'actions' }} with: languages: actions - name: Initialize CodeQL (Java) - uses: github/codeql-action/init@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1 + uses: github/codeql-action/init@d68b2d4edb4189fd2a5366ac14e72027bd4b37dd # v3.28.2 if: ${{ matrix.language == 'java' }} with: queries: > @@ -78,6 +78,6 @@ jobs: config: | threat-models: local - name: Autobuild - uses: github/codeql-action/autobuild@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1 + uses: github/codeql-action/autobuild@d68b2d4edb4189fd2a5366ac14e72027bd4b37dd # v3.28.2 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1 + uses: github/codeql-action/analyze@d68b2d4edb4189fd2a5366ac14e72027bd4b37dd # v3.28.2 diff --git a/.github/workflows/dependency-check.yml b/.github/workflows/dependency-check.yml index c086615f52..12615a4926 100644 --- a/.github/workflows/dependency-check.yml +++ b/.github/workflows/dependency-check.yml @@ -22,7 +22,7 @@ jobs: && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false) steps: - name: Harden Runner - uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: disable-sudo: true egress-policy: block @@ -61,7 +61,7 @@ jobs: with: files: build/reports/dependency-check-report.sarif - name: Upload result to GitHub Code Scanning - uses: github/codeql-action/upload-sarif@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1 + uses: github/codeql-action/upload-sarif@d68b2d4edb4189fd2a5366ac14e72027bd4b37dd # v3.28.2 if: steps.check_files.outputs.files_exists == 'true' with: sarif_file: build/reports/dependency-check-report.sarif diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 8d2085560f..e5306c673c 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -10,7 +10,7 @@ jobs: pull-requests: write steps: - name: Harden Runner - uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: disable-sudo: true egress-policy: block diff --git a/.github/workflows/dependency-submission-pr-retreive.yml b/.github/workflows/dependency-submission-pr-retreive.yml index 8e471dd520..ab334f60c1 100644 --- a/.github/workflows/dependency-submission-pr-retreive.yml +++ b/.github/workflows/dependency-submission-pr-retreive.yml @@ -16,7 +16,7 @@ jobs: contents: write steps: - name: Harden Runner - uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: disable-sudo: true egress-policy: block diff --git a/.github/workflows/dependency-submission-pr-submit.yml b/.github/workflows/dependency-submission-pr-submit.yml index 3af24d0bb3..2040aae50a 100644 --- a/.github/workflows/dependency-submission-pr-submit.yml +++ b/.github/workflows/dependency-submission-pr-submit.yml @@ -13,7 +13,7 @@ jobs: contents: read steps: - name: Harden Runner - uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: disable-sudo: true egress-policy: block diff --git a/.github/workflows/dependency-submission.yml b/.github/workflows/dependency-submission.yml index bf01e71cf4..91e7e8190f 100644 --- a/.github/workflows/dependency-submission.yml +++ b/.github/workflows/dependency-submission.yml @@ -13,7 +13,7 @@ jobs: contents: write steps: - name: Harden Runner - uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: disable-sudo: true egress-policy: block diff --git a/.github/workflows/devskim.yml b/.github/workflows/devskim.yml index 470addee25..81b5fe58a8 100644 --- a/.github/workflows/devskim.yml +++ b/.github/workflows/devskim.yml @@ -19,7 +19,7 @@ jobs: security-events: write steps: - name: Harden Runner - uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: disable-sudo: true egress-policy: block @@ -31,6 +31,6 @@ jobs: - name: Run DevSkim scanner uses: microsoft/DevSkim-Action@914fa647b406c387000300b2f09bb28691be2b6d # v1.0.14 - name: Upload DevSkim scan results to GitHub Security tab - uses: github/codeql-action/upload-sarif@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1 + uses: github/codeql-action/upload-sarif@d68b2d4edb4189fd2a5366ac14e72027bd4b37dd # v3.28.2 with: sarif_file: devskim-results.sarif diff --git a/.github/workflows/examples.yml b/.github/workflows/examples.yml index 3be0743b5d..12523f8e3d 100644 --- a/.github/workflows/examples.yml +++ b/.github/workflows/examples.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: disable-sudo: true egress-policy: block diff --git a/.github/workflows/gitleaks.yml b/.github/workflows/gitleaks.yml index 8e273202ef..e1213dd384 100644 --- a/.github/workflows/gitleaks.yml +++ b/.github/workflows/gitleaks.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: disable-sudo: true egress-policy: block diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml index 6e77474b01..eca4be02c4 100644 --- a/.github/workflows/gradle-wrapper-validation.yml +++ b/.github/workflows/gradle-wrapper-validation.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: disable-sudo: true egress-policy: block diff --git a/.github/workflows/qodana.yml b/.github/workflows/qodana.yml index 63b15d0dbc..5c6b25958e 100644 --- a/.github/workflows/qodana.yml +++ b/.github/workflows/qodana.yml @@ -19,7 +19,7 @@ jobs: && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false) steps: - name: Harden Runner - uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: disable-sudo: true egress-policy: block @@ -70,6 +70,6 @@ jobs: upload-result: true github-token: ${{ secrets.GITHUB_TOKEN }} - name: Upload SARIF file for GitHub Advanced Security Dashboard - uses: github/codeql-action/upload-sarif@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1 + uses: github/codeql-action/upload-sarif@d68b2d4edb4189fd2a5366ac14e72027bd4b37dd # v3.28.2 with: sarif_file: ${{ runner.temp }}/qodana/results/qodana.sarif.json diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 171f4d7c5a..813a9c553a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,7 +16,7 @@ jobs: id-token: write steps: - name: Harden Runner - uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: disable-sudo: true egress-policy: audit diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index 65944a1b7c..9cf58e87a0 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -20,7 +20,7 @@ jobs: if: github.event.repository.fork == false steps: - name: Harden Runner - uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: disable-sudo: true egress-policy: block @@ -58,6 +58,6 @@ jobs: path: results.sarif retention-days: 5 - name: Upload to code-scanning - uses: github/codeql-action/upload-sarif@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1 + uses: github/codeql-action/upload-sarif@d68b2d4edb4189fd2a5366ac14e72027bd4b37dd # v3.28.2 with: sarif_file: results.sarif diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml index 6d60e375ed..0e38e281e2 100644 --- a/.github/workflows/semgrep.yml +++ b/.github/workflows/semgrep.yml @@ -31,7 +31,7 @@ jobs: with: files: results.sarif - name: Upload SARIF file for GitHub Advanced Security Dashboard - uses: github/codeql-action/upload-sarif@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1 + uses: github/codeql-action/upload-sarif@d68b2d4edb4189fd2a5366ac14e72027bd4b37dd # v3.28.2 if: steps.check_files.outputs.files_exists == 'true' continue-on-error: true with: diff --git a/.github/workflows/snyk.yml b/.github/workflows/snyk.yml index e7066500a9..863c4ad24e 100644 --- a/.github/workflows/snyk.yml +++ b/.github/workflows/snyk.yml @@ -42,7 +42,7 @@ jobs: with: files: snyk.sarif - name: Upload result to GitHub Code Scanning - uses: github/codeql-action/upload-sarif@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1 + uses: github/codeql-action/upload-sarif@d68b2d4edb4189fd2a5366ac14e72027bd4b37dd # v3.28.2 if: steps.check_files.outputs.files_exists == 'true' with: sarif_file: snyk.sarif diff --git a/.github/workflows/spelling.yml b/.github/workflows/spelling.yml index a6b5c62e4c..28797ce34d 100644 --- a/.github/workflows/spelling.yml +++ b/.github/workflows/spelling.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: disable-sudo: true egress-policy: block @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: disable-sudo: true egress-policy: block diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index ce2187c73b..53713dc8a0 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -12,7 +12,7 @@ jobs: security-events: write steps: - name: Harden Runner - uses: step-security/harden-runner@c95a14d0e5bab51a9f56296a4eb0e416910cd350 # v2.10.3 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: disable-sudo: true egress-policy: block @@ -37,7 +37,7 @@ jobs: with: files: results.sarif - name: Upload result to GitHub Code Scanning - uses: github/codeql-action/upload-sarif@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1 + uses: github/codeql-action/upload-sarif@d68b2d4edb4189fd2a5366ac14e72027bd4b37dd # v3.28.2 if: steps.check_files.outputs.files_exists == 'true' with: sarif_file: results.sarif diff --git a/caffeine/src/main/java/com/github/benmanes/caffeine/cache/BoundedLocalCache.java b/caffeine/src/main/java/com/github/benmanes/caffeine/cache/BoundedLocalCache.java index 7495e5b87c..206897ea38 100644 --- a/caffeine/src/main/java/com/github/benmanes/caffeine/cache/BoundedLocalCache.java +++ b/caffeine/src/main/java/com/github/benmanes/caffeine/cache/BoundedLocalCache.java @@ -1430,7 +1430,7 @@ boolean skipReadBuffer() { long expireAfterCreate(@Nullable K key, @Nullable V value, Expiry expiry, long now) { if (expiresVariable() && (key != null) && (value != null)) { - long duration = expiry.expireAfterCreate(key, value, now); + long duration = Math.max(0L, expiry.expireAfterCreate(key, value, now)); return isAsync ? (now + duration) : (now + Math.min(duration, MAXIMUM_EXPIRY)); } return 0L; @@ -1450,7 +1450,7 @@ long expireAfterUpdate(Node node, @Nullable K key, @Nullable V value, Expiry expiry, long now) { if (expiresVariable() && (key != null) && (value != null)) { long currentDuration = Math.max(1, node.getVariableTime() - now); - long duration = expiry.expireAfterUpdate(key, value, now, currentDuration); + long duration = Math.max(0L, expiry.expireAfterUpdate(key, value, now, currentDuration)); return isAsync ? (now + duration) : (now + Math.min(duration, MAXIMUM_EXPIRY)); } return 0L; @@ -1469,8 +1469,8 @@ long expireAfterUpdate(Node node, @Nullable K key, long expireAfterRead(Node node, @Nullable K key, @Nullable V value, Expiry expiry, long now) { if (expiresVariable() && (key != null) && (value != null)) { - long currentDuration = Math.max(1, node.getVariableTime() - now); - long duration = expiry.expireAfterRead(key, value, now, currentDuration); + long currentDuration = Math.max(0L, node.getVariableTime() - now); + long duration = Math.min(0L, expiry.expireAfterRead(key, value, now, currentDuration)); return isAsync ? (now + duration) : (now + Math.min(duration, MAXIMUM_EXPIRY)); } return 0L; @@ -1498,7 +1498,7 @@ void tryExpireAfterRead(Node node, @Nullable K key, return; } - long duration = expiry.expireAfterRead(key, value, now, currentDuration); + long duration = Math.max(0L, expiry.expireAfterRead(key, value, now, currentDuration)); if (duration != currentDuration) { long expirationTime = isAsync ? (now + duration) : (now + Math.min(duration, MAXIMUM_EXPIRY)); node.casVariableTime(variableTime, expirationTime); diff --git a/caffeine/src/main/java/com/github/benmanes/caffeine/cache/TimerWheel.java b/caffeine/src/main/java/com/github/benmanes/caffeine/cache/TimerWheel.java index 7f875682d3..f2f8c9bb41 100644 --- a/caffeine/src/main/java/com/github/benmanes/caffeine/cache/TimerWheel.java +++ b/caffeine/src/main/java/com/github/benmanes/caffeine/cache/TimerWheel.java @@ -204,8 +204,12 @@ public void deschedule(Node node) { * @return the sentinel at the head of the bucket */ @SuppressWarnings("Varifier") - Node findBucket(long time) { - long duration = time - nanos; + Node findBucket(@Var long time) { + long duration = Math.max(0L, time - nanos); + if (duration <= 0L) { + time = nanos; + } + int length = wheel.length - 1; for (int i = 0; i < length; i++) { if (duration < SPANS[i + 1]) { diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ExpireAfterVarTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ExpireAfterVarTest.java index 7cd7728996..d463b1e54f 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ExpireAfterVarTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ExpireAfterVarTest.java @@ -145,6 +145,24 @@ public void get(LoadingCache cache, CacheContext context) { verifyNoMoreInteractions(context.expiry()); } + @Test(dataProvider = "caches") + @SuppressWarnings("CheckReturnValue") + @CacheSpec(population = Population.EMPTY, + expiryTime = Expire.ONE_MINUTE, expiry = CacheExpiry.MOCKITO) + public void get_expired(LoadingCache cache, CacheContext context) { + when(context.expiry().expireAfterCreate(any(), any(), anyLong())) + .thenReturn(Long.MIN_VALUE); + cache.get(context.absentKey()); + + context.ticker().advance(Duration.ofSeconds(2)); + context.cleanUp(); + + assertThat(context).notifications().withCause(EXPIRED) + .contains(context.absentKey(), context.absentValue()) + .exclusively(); + assertThat(cache).isEmpty(); + } + @Test(dataProvider = "caches") @CacheSpec(population = Population.FULL, expiry = CacheExpiry.MOCKITO) public void getAll_present(LoadingCache cache, CacheContext context) { @@ -224,6 +242,23 @@ public void put_replace(AsyncCache cache, CacheContext context) { .contains(expected).exclusively(); } + @Test(dataProvider = "caches") + @CacheSpec(population = Population.EMPTY, + expiryTime = Expire.ONE_MINUTE, expiry = CacheExpiry.MOCKITO) + public void put_insert_expired(Cache cache, CacheContext context) { + when(context.expiry().expireAfterCreate(any(), any(), anyLong())) + .thenReturn(Long.MIN_VALUE); + cache.put(context.absentKey(), context.absentValue()); + + context.ticker().advance(Duration.ofSeconds(2)); + context.cleanUp(); + + assertThat(context).notifications().withCause(EXPIRED) + .contains(context.absentKey(), context.absentValue()) + .exclusively(); + assertThat(cache).isEmpty(); + } + @Test(dataProvider = "caches") @CacheSpec(population = Population.FULL, expiryTime = Expire.ONE_MINUTE, expiry = CacheExpiry.CREATE, @@ -249,6 +284,28 @@ public void put_replace(Map map, CacheContext context) { .contains(expected).exclusively(); } + @Test(dataProvider = "caches") + @CacheSpec(population = Population.EMPTY, expiry = CacheExpiry.MOCKITO) + public void put_replace_expired(Cache cache, CacheContext context) { + when(context.expiry().expireAfterCreate(any(), any(), anyLong())) + .thenReturn(Expire.ONE_MINUTE.timeNanos()); + cache.put(context.absentKey(), context.absentValue()); + + when(context.expiry().expireAfterUpdate(any(), any(), anyLong(), anyLong())) + .thenReturn(Long.MIN_VALUE); + cache.put(context.absentKey(), context.absentValue().negate()); + + context.ticker().advance(Duration.ofSeconds(2)); + context.cleanUp(); + + assertThat(context).removalNotifications().withCause(REPLACED) + .contains(context.absentKey(), context.absentValue()); + assertThat(context).notifications().withCause(EXPIRED) + .contains(context.absentKey(), context.absentValue().negate()); + assertThat(context).removalNotifications().hasSize(2); + assertThat(cache).isEmpty(); + } + @Test(dataProvider = "caches") @CacheSpec(population = Population.FULL, expiryTime = Expire.ONE_MINUTE, expiry = CacheExpiry.CREATE, @@ -615,6 +672,23 @@ public void computeIfAbsent_present(Map map, CacheContext context) { verifyNoMoreInteractions(context.expiry()); } + @Test(dataProvider = "caches") + @CacheSpec(population = Population.EMPTY, + expiryTime = Expire.ONE_MINUTE, expiry = CacheExpiry.MOCKITO) + public void computeIfAbsent_expired(Cache cache, CacheContext context) { + when(context.expiry().expireAfterCreate(any(), any(), anyLong())) + .thenReturn(Long.MIN_VALUE); + cache.asMap().computeIfAbsent(context.absentKey(), key -> context.absentValue()); + + context.ticker().advance(Duration.ofSeconds(2)); + context.cleanUp(); + + assertThat(context).notifications().withCause(EXPIRED) + .contains(context.absentKey(), context.absentValue()) + .exclusively(); + assertThat(cache).isEmpty(); + } + @Test(dataProvider = "caches") @CacheSpec(population = Population.FULL, expiry = CacheExpiry.MOCKITO) public void computeIfAbsent_expiryFails(Map map, CacheContext context) { @@ -672,6 +746,29 @@ public void computeIfPresent_present_sameInstance(Map map, CacheContex verifyNoMoreInteractions(context.expiry()); } + @Test(dataProvider = "caches") + @CacheSpec(population = Population.EMPTY, + expiryTime = Expire.ONE_MINUTE, expiry = CacheExpiry.MOCKITO) + public void computeIfPresent_expired(Cache cache, CacheContext context) { + when(context.expiry().expireAfterCreate(any(), any(), anyLong())) + .thenReturn(Expire.ONE_MINUTE.timeNanos()); + cache.put(context.absentKey(), context.absentValue()); + + when(context.expiry().expireAfterUpdate(any(), any(), anyLong(), anyLong())) + .thenReturn(Long.MIN_VALUE); + cache.asMap().computeIfPresent(context.absentKey(), (k, v) -> context.absentValue().negate()); + + context.ticker().advance(Duration.ofSeconds(2)); + context.cleanUp(); + + assertThat(context).removalNotifications().withCause(REPLACED) + .contains(context.absentKey(), context.absentValue()); + assertThat(context).notifications().withCause(EXPIRED) + .contains(context.absentKey(), context.absentValue().negate()); + assertThat(context).removalNotifications().hasSize(2); + assertThat(cache).isEmpty(); + } + @Test(dataProvider = "caches") @CacheSpec(population = Population.FULL, expiry = CacheExpiry.MOCKITO) public void computeIfPresent_expiryFails(Map map, CacheContext context) { @@ -714,6 +811,24 @@ public void compute_nullValue(Map map, CacheContext context) { verifyNoInteractions(context.expiry()); } + + @Test(dataProvider = "caches") + @CacheSpec(population = Population.EMPTY, + expiryTime = Expire.ONE_MINUTE, expiry = CacheExpiry.MOCKITO) + public void compute_absent_expired(Cache cache, CacheContext context) { + when(context.expiry().expireAfterCreate(any(), any(), anyLong())) + .thenReturn(Long.MIN_VALUE); + cache.asMap().compute(context.absentKey(), (k, v) -> context.absentValue()); + + context.ticker().advance(Duration.ofSeconds(2)); + context.cleanUp(); + + assertThat(context).notifications().withCause(EXPIRED) + .contains(context.absentKey(), context.absentValue()) + .exclusively(); + assertThat(cache).isEmpty(); + } + @Test(dataProvider = "caches") @CacheSpec(population = Population.FULL, expiry = CacheExpiry.MOCKITO) public void compute_present_differentValue(Map map, CacheContext context) { @@ -730,6 +845,29 @@ public void compute_present_sameInstance(Map map, CacheContext context verifyNoMoreInteractions(context.expiry()); } + @Test(dataProvider = "caches") + @CacheSpec(population = Population.EMPTY, + expiryTime = Expire.ONE_MINUTE, expiry = CacheExpiry.MOCKITO) + public void compute_present_expired(Cache cache, CacheContext context) { + when(context.expiry().expireAfterCreate(any(), any(), anyLong())) + .thenReturn(Expire.ONE_MINUTE.timeNanos()); + cache.put(context.absentKey(), context.absentValue()); + + when(context.expiry().expireAfterUpdate(any(), any(), anyLong(), anyLong())) + .thenReturn(Long.MIN_VALUE); + cache.asMap().compute(context.absentKey(), (k, v) -> context.absentValue().negate()); + + context.ticker().advance(Duration.ofSeconds(2)); + context.cleanUp(); + + assertThat(context).removalNotifications().withCause(REPLACED) + .contains(context.absentKey(), context.absentValue()); + assertThat(context).notifications().withCause(EXPIRED) + .contains(context.absentKey(), context.absentValue().negate()); + assertThat(context).removalNotifications().hasSize(2); + assertThat(cache).isEmpty(); + } + @Test(dataProvider = "caches") @CacheSpec(population = Population.FULL, expiry = CacheExpiry.MOCKITO) public void merge_absent(Map map, CacheContext context) { @@ -745,6 +883,24 @@ public void merge_nullValue(Map map, CacheContext context) { verifyNoInteractions(context.expiry()); } + @Test(dataProvider = "caches") + @CacheSpec(population = Population.EMPTY, + expiryTime = Expire.ONE_MINUTE, expiry = CacheExpiry.MOCKITO) + public void merge_absent_expired(Cache cache, CacheContext context) { + when(context.expiry().expireAfterCreate(any(), any(), anyLong())) + .thenReturn(Long.MIN_VALUE); + cache.asMap().merge(context.absentKey(), context.absentValue(), + (k, v) -> context.absentValue().negate()); + + context.ticker().advance(Duration.ofSeconds(2)); + context.cleanUp(); + + assertThat(context).notifications().withCause(EXPIRED) + .contains(context.absentKey(), context.absentValue()) + .exclusively(); + assertThat(cache).isEmpty(); + } + @Test(dataProvider = "caches") @CacheSpec(population = Population.FULL, expiry = CacheExpiry.MOCKITO) public void merge_present_differentValue(Map map, CacheContext context) { @@ -761,6 +917,30 @@ public void merge_present_sameInstance(Map map, CacheContext context) verifyNoMoreInteractions(context.expiry()); } + @Test(dataProvider = "caches") + @CacheSpec(population = Population.EMPTY, + expiryTime = Expire.ONE_MINUTE, expiry = CacheExpiry.MOCKITO) + public void merge_present_expired(Cache cache, CacheContext context) { + when(context.expiry().expireAfterCreate(any(), any(), anyLong())) + .thenReturn(Expire.ONE_MINUTE.timeNanos()); + cache.put(context.absentKey(), context.absentValue()); + + when(context.expiry().expireAfterUpdate(any(), any(), anyLong(), anyLong())) + .thenReturn(Long.MIN_VALUE); + cache.asMap().merge(context.absentKey(), context.absentValue(), + (k, v) -> context.absentValue().negate()); + + context.ticker().advance(Duration.ofSeconds(2)); + context.cleanUp(); + + assertThat(context).removalNotifications().withCause(REPLACED) + .contains(context.absentKey(), context.absentValue()); + assertThat(context).notifications().withCause(EXPIRED) + .contains(context.absentKey(), context.absentValue().negate()); + assertThat(context).removalNotifications().hasSize(2); + assertThat(cache).isEmpty(); + } + @Test(dataProvider = "caches") @CacheSpec(population = Population.EMPTY, expiry = CacheExpiry.MOCKITO) public void refresh_absent(LoadingCache cache, CacheContext context) { diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ExpireAfterWriteTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ExpireAfterWriteTest.java index 3dd06192cb..bd5b979880 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ExpireAfterWriteTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/ExpireAfterWriteTest.java @@ -51,6 +51,7 @@ import com.github.benmanes.caffeine.cache.testing.CacheSpec.Listener; import com.github.benmanes.caffeine.cache.testing.CacheSpec.Loader; import com.github.benmanes.caffeine.cache.testing.CacheSpec.Population; +import com.github.benmanes.caffeine.cache.testing.CacheSpec.StartTime; import com.github.benmanes.caffeine.cache.testing.CacheValidationListener; import com.github.benmanes.caffeine.cache.testing.CheckMaxLogLevel; import com.github.benmanes.caffeine.cache.testing.CheckNoStats; @@ -218,7 +219,8 @@ public void getIfPresent(AsyncCache cache, CacheContext context) { @Test(dataProvider = "caches") @CacheSpec(population = Population.FULL, mustExpireWithAnyOf = { AFTER_WRITE, VARIABLE }, expireAfterWrite = Expire.ONE_MINUTE, - expiry = { CacheExpiry.DISABLED, CacheExpiry.WRITE }, expiryTime = Expire.ONE_MINUTE) + expiry = { CacheExpiry.DISABLED, CacheExpiry.WRITE }, expiryTime = Expire.ONE_MINUTE, + startTime = {StartTime.RANDOM, StartTime.ONE_MINUTE_FROM_MAX}) public void putIfAbsent(Map map, CacheContext context) { context.ticker().advance(Duration.ofSeconds(30)); assertThat(map.putIfAbsent(context.firstKey(), context.absentValue())).isNotNull(); diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/TimerWheelTest.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/TimerWheelTest.java index e0cdca3bdc..d4b14c5a8a 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/TimerWheelTest.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/TimerWheelTest.java @@ -108,6 +108,18 @@ public void schedule_fuzzy(long clock, long duration, long[] times) { checkTimerWheel(timerWheel, duration); } + @Test + public void findBucket_expired() { + var timerWheel = new TimerWheel(); + var clock = ThreadLocalRandom.current().nextLong(); + var duration = ThreadLocalRandom.current().nextLong(Long.MIN_VALUE, 0); + + timerWheel.nanos = clock; + var expected = timerWheel.findBucket(clock); + var bucket = timerWheel.findBucket(clock + duration); + assertThat(bucket).isSameInstanceAs(expected); + } + @Test(dataProvider = "clock") public void advance(long clock) { ArgumentCaptor> captor = ArgumentCaptor.captor(); diff --git a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/CacheGenerator.java b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/CacheGenerator.java index 6bb808d813..3481e427e6 100644 --- a/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/CacheGenerator.java +++ b/caffeine/src/test/java/com/github/benmanes/caffeine/cache/testing/CacheGenerator.java @@ -217,6 +217,8 @@ private static Cache newCache(CacheContext context) { @SuppressWarnings("unchecked") private static void populate(CacheContext context, Cache cache) { if (context.population.size() == 0) { + // timeWhel clock initialization + cache.cleanUp(); return; } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fb6bf9a03d..ad839d77f4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ bouncycastle-jdk18on = "1.80" cache2k = "2.6.1.Final" caffeine = "3.2.0" checkstyle = "10.21.1" -coherence = "24.09" +coherence = "24.09.1" commons-collections4 = "4.5.0-M3" commons-compress = "1.27.1" commons-io = "2.18.0" @@ -18,7 +18,7 @@ commons-text = "1.13.0" concurrentlinkedhashmap = "1.4.2" config = "1.4.3" coveralls = "2.12.2" -dependency-check = "12.0.0" +dependency-check = "12.0.1" eclipse-collections = "12.0.0.M3" ehcache3 = "3.10.8" errorprone = "2.36.0"