Some content is hidden

Large Commits have some content hidden by default. Use the searcx below for content that may be hidden.

45 files changed

+310
-50
lines changed
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ inputs:
2525
runs:
2626
using: composite
2727
steps:
28+
- name: Setup shell
29+
if: runner.os == 'macOS'
30+
shell: bash
31+
run: |
32+
brew install bash
33+
echo "/opt/homebrew/bin/bash" >> $_PATH || \
34+
echo "/usr/local/bin/bash" >> $_PATH
35+
brew install grep
36+
echo "/opt/homebrew/opt/grep/libexec/gnubin" >> $_PATH || \
37+
echo "/usr/local/opt/grep/libexec/gnubin" >> $_PATH
2838
- name: Read Gradle JDK toolchain version
2939
id: gradle_toolchain
3040
shell: bash
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ javadocs
130130
JBridge
131131
jcache
132132
JCP
133+
jcstress
133134
JDKs
134135
jemalloc
135136
jetbrains
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ set -eux
33

44
./gradlew \
55
forbiddenApis -DforbiddenApis \
6-
ecjJavaPoet ecjMain ecjCodeGen ecjJmh ecjTest \
7-
pmdJavaPoet pmdMain pmdCodeGen pmdJmh pmdTest -Dpmd \
8-
spotbugsJavaPoet spotbugsMain spotbugsCodeGen spotbugsJmh spotbugsTest -Dspotbugs \
6+
ecjJavaPoet ecjMain ecjCodeGen ecjJmh ecjTest ecjJcstress \
7+
pmdJavaPoet pmdMain pmdCodeGen pmdJmh pmdTest pmdJcstress -Dpmd \
8+
spotbugsJavaPoet spotbugsMain spotbugsCodeGen spotbugsJmh spotbugsTest spotbugsJcstress -Dspotbugs \
99
"$@"
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ jobs:
5656
GH_TOKEN: ${{ secrets._TOKEN }}
5757
run: uvx zizmor --pedantic --format sarif . > results.sarif
5858
- name: Upload SARIF file for Advanced Security Dasard
59-
uses: /codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
59+
uses: /codeql-action/upload-sarif@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19
6060
with:
6161
sarif_file: results.sarif
6262
category: zizmor
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ jobs:
4343
with:
4444
java: ${{ env.JAVA_VERSION }}
4545
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
46-
arguments: ecjJavaPoet ecjMain ecjCodeGen ecjJmh ecjTest
46+
arguments: ecjJavaPoet ecjMain ecjCodeGen ecjJmh ecjTest ecjJcstress
4747

4848
forbidden-apis:
4949
runs-on: ubuntu-latest
@@ -91,7 +91,7 @@ jobs:
9191
with:
9292
java: ${{ env.JAVA_VERSION }}
9393
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
94-
arguments: pmdJavaPoet pmdMain pmdCodeGen pmdJmh pmdTest -Dpmd
94+
arguments: pmdJavaPoet pmdMain pmdCodeGen pmdJmh pmdTest pmdJcstress -Dpmd
9595
- name: Upload Reports
9696
if: always()
9797
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
@@ -123,7 +123,8 @@ jobs:
123123
java: ${{ env.JAVA_VERSION }}
124124
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
125125
arguments: >
126-
spotbugsJavaPoet spotbugsMain spotbugsCodeGen spotbugsJmh spotbugsTest -Dspotbugs
126+
spotbugsJavaPoet spotbugsMain spotbugsCodeGen
127+
spotbugsJmh spotbugsTest spotbugsJcstress -Dspotbugs
127128
- name: Upload Reports
128129
if: always()
129130
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ jobs:
4949
if: steps.check_files.outputs.files_exists == 'true'
5050
run: jq -c '.runs |= unique_by({tool, invocations, results})' < results.sarif > codacy.sarif
5151
- name: Upload result to Code Scanning
52-
uses: /codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
52+
uses: /codeql-action/upload-sarif@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19
5353
if: steps.check_files.outputs.files_exists == 'true'
5454
continue-on-error: true
5555
with:
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,13 @@ jobs:
5959
java: ${{ env.JAVA_VERSION }}
6060
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
6161
- name: Initialize CodeQL (Actions)
62-
uses: /codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
62+
uses: /codeql-action/init@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19
6363
if: ${{ matrix.language == 'actions' }}
6464
with:
6565
languages: actions
6666
dependency-caching: true
6767
- name: Initialize CodeQL (Java)
68-
uses: /codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
68+
uses: /codeql-action/init@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19
6969
if: ${{ matrix.language == 'java' }}
7070
with:
7171
queries: >
@@ -83,6 +83,6 @@ jobs:
8383
config: |
8484
threat-models: local
8585
- name: Autobuild
86-
uses: /codeql-action/autobuild@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
86+
uses: /codeql-action/autobuild@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19
8787
- name: Perform CodeQL Analysis
88-
uses: /codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
88+
uses: /codeql-action/analyze@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ jobs:
6666
with:
6767
files: build/reports/dependency-check-report.sarif
6868
- name: Upload result to Code Scanning
69-
uses: /codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
69+
uses: /codeql-action/upload-sarif@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19
7070
if: steps.check_files.outputs.files_exists == 'true'
7171
with:
7272
sarif_file: build/reports/dependency-check-report.sarif
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,6 @@ jobs:
3333
- name: Run DevSkim scanner
3434
uses: microsoft/DevSkim-Action@4b5047945a44163b94642a1cecc0d93a3f428cc6 # v1.0.16
3535
- name: Upload DevSkim scan results to Security tab
36-
uses: /codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
36+
uses: /codeql-action/upload-sarif@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19
3737
with:
3838
sarif_file: devskim-results.sarif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
name: JCStress
2+
permissions: {}
3+
on:
4+
schedule:
5+
- cron: '0 0 * * 1'
6+
7+
env:
8+
DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
9+
ALLOWED_ENDPOINTS: >
10+
api.adoptium.net:443
11+
api.foojay.io:443
12+
api..com:443
13+
caffeine.gradle-enterprise.cloud:443
14+
downloads.gradle.org:443
15+
downloads.gradle-dn.com:443
16+
.com:443
17+
jcenter.bintray.com:443
18+
objects.usercontent.com:443
19+
plugins.gradle.org:443
20+
plugins-artifacts.gradle.org:443
21+
repo.maven.apache.org:443
22+
repo1.maven.org:443
23+
services.gradle.org:443
24+
25+
jobs:
26+
jcstress:
27+
runs-on: ${{ matrix.os }}
28+
permissions:
29+
contents: read
30+
strategy:
31+
matrix:
32+
java: [ 11, 24, 25-ea ]
33+
os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest]
34+
env:
35+
JAVA_VERSION: ${{ matrix.java }}
36+
steps:
37+
- name: Harden Runner
38+
uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
39+
with:
40+
allowed-endpoints: ${{ env.ALLOWED_ENDPOINTS }}
41+
disable-sudo-and-containers: true
42+
egress-policy: block
43+
- name: Checkout
44+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
45+
with:
46+
persist-credentials: false
47+
- name: JCStress
48+
uses: ././actions/run-gradle
49+
with:
50+
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
51+
java: ${{ env.JAVA_VERSION }}
52+
arguments: jcstress
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,6 @@ jobs:
7272
upload-result: true
7373
-token: ${{ secrets._TOKEN }}
7474
- name: Upload SARIF file for Advanced Security Dasard
75-
uses: /codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
75+
uses: /codeql-action/upload-sarif@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19
7676
with:
7777
sarif_file: ${{ runner.temp }}/qodana/results/qodana.sarif.json
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,6 @@ jobs:
5757
path: results.sarif
5858
retention-days: 5
5959
- name: Upload to code-scanning
60-
uses: /codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
60+
uses: /codeql-action/upload-sarif@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19
6161
with:
6262
sarif_file: results.sarif
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
with:
3535
files: results.sarif
3636
- name: Upload SARIF file for Advanced Security Dasard
37-
uses: /codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
37+
uses: /codeql-action/upload-sarif@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19
3838
if: steps.check_files.outputs.files_exists == 'true'
3939
continue-on-error: true
4040
with:
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ jobs:
4444
with:
4545
files: snyk.sarif
4646
- name: Upload result to Code Scanning
47-
uses: /codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
47+
uses: /codeql-action/upload-sarif@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19
4848
if: steps.check_files.outputs.files_exists == 'true'
4949
with:
5050
sarif_file: snyk.sarif
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,4 @@ jobs:
103103
with:
104104
persist-credentials: false
105105
- name: Typos
106-
uses: crate-ci/typos@0f0ccba9ed1df83948f0c15026e4f5ccfce46109 # v1.32.0
106+
uses: crate-ci/typos@b1ae8d918b6e85bd611117d3d9a3be4f903ee5e4 # v1.33.1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
with:
2929
persist-credentials: false
3030
- name: Run Trivy vulnerability scanner
31-
uses: aquasecurity/trivy-action@6c175e9c4083a92bbca2f9724c8a5e33bc2d97a5 # v0.30.0
31+
uses: aquasecurity/trivy-action@76071ef0d7ec797419534a183b498b4d6366cf37 # v0.31.0
3232
continue-on-error: true
3333
with:
3434
scan-type: fs
@@ -40,7 +40,7 @@ jobs:
4040
with:
4141
files: results.sarif
4242
- name: Upload result to Code Scanning
43-
uses: /codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
43+
uses: /codeql-action/upload-sarif@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19
4444
if: steps.check_files.outputs.files_exists == 'true'
4545
with:
4646
sarif_file: results.sarif
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import org.gradle.plugins.ide.eclipse.model.Library
99

1010
plugins {
1111
id("jmh.caffeine")
12+
id("jcstress.caffeine")
1213
id("java-library.caffeine")
1314
}
1415

Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import static com..benmanes.caffeine.cache.Specifications.vTypeVar;
2020
import static com..benmanes.caffeine.cache.node.NodeContext.varHandleName;
2121

22+
import java.lang.invoke.VarHandle;
2223
import java.lang.ref.Reference;
2324
import java.util.Objects;
2425

@@ -87,6 +88,7 @@ private static MethodSpec makeGetValue(NodeContext context) {
8788
.beginControlFlow("if (referent != null)")
8889
.addStatement("return referent")
8990
.endControlFlow()
91+
.addStatement("$T.loadLoadFence()", VarHandle.class)
9092
.addStatement("$1T<V> current = ($1T<V>) $2L.getAcquire(this)", Reference.class, handle)
9193
.beginControlFlow("if (ref == current)")
9294
.addStatement("return null")
@@ -119,10 +121,10 @@ private static MethodSpec makeSetValue(NodeContext context) {
119121
} else {
120122
setter.addStatement("$1T<V> ref = ($1T<V>) $2L.getAcquire(this)",
121123
Reference.class, varHandleName("value"));
122-
setter.addComment("Can be setRelease in JDK 12+ (see JDK-8205523, JDK-8205523, JDK-8209697)");
123-
setter.addStatement("$L.setVolatile(this, new $T($L, $N, referenceQueue))",
124+
setter.addStatement("$L.setRelease(this, new $T($L, $N, referenceQueue))",
124125
varHandleName("value"), context.valueReferenceType(),
125126
"getKeyReference()", "value");
127+
setter.addStatement("$T.storeStoreFence()", VarHandle.class);
126128
setter.addStatement("ref.clear()");
127129
}
128130

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright 2025 Ben Manes. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com..benmanes.caffeine.cache;
17+
18+
import java.lang.invoke.MethodHandles;
19+
import java.lang.invoke.VarHandle;
20+
21+
import org.jspecify.annotations.Nullable;
22+
import org.openjdk.jcstress.annotations.Actor;
23+
import org.openjdk.jcstress.annotations.Expect;
24+
import org.openjdk.jcstress.annotations.JCStressTest;
25+
import org.openjdk.jcstress.annotations.Outcome;
26+
import org.openjdk.jcstress.annotations.State;
27+
import org.openjdk.jcstress.infra.results.L_Result;
28+
29+
import com.google.errorprone.annotations.Var;
30+
31+
/**
32+
* A stress test that simulates the behavior for {@link java.lang.ref.Reference} reads and writes in
33+
* weak or soft valued caches. The {@link Node#setValue} proactively clears the underlying referent
34+
* after updating the entry's value. This is done to assist cross-generational pollution which can
35+
* cause garbage collectors to unnecessarily promote young dead objects to an old collection and
36+
* increase pause times. The {@link Node#getValue} compensates by a re-check validation to determine
37+
* if the observed null referent is due to garbage collection or a stale read. Due to referent being
38+
* read and written with plain memory semantics, an additional memory barrier is required to ensure
39+
* the correct visibility ordering.
40+
* <p>
41+
* {@snippet lang="shell" :
42+
* // JAVA_VERSION=?? for an alternative jdk
43+
* ./gradlew caffeine:jcstress --rerun
44+
* }
45+
*
46+
* @author [email protected] (Ben Manes)
47+
*/
48+
@State
49+
@JCStressTest
50+
@Outcome(id = "ok", expect = Expect.ACCEPTABLE, desc = "Reference seen and valid")
51+
@Outcome(id = "null", expect = Expect.FORBIDDEN, desc = "Reference seen but field not visible")
52+
public class IntermittentNull {
53+
private static final VarHandle VALUE;
54+
55+
volatile Value value = new Value("ok");
56+
57+
@Actor
58+
public void writer() {
59+
var oldValue = (Value) VALUE.getAcquire(this);
60+
var newValue = new Value("ok");
61+
VALUE.setRelease(this, newValue);
62+
VarHandle.storeStoreFence();
63+
oldValue.data = null;
64+
}
65+
66+
@Actor
67+
public void reader(L_Result r) {
68+
@Var var value = (Value) VALUE.getAcquire(this);
69+
for (;;) {
70+
var data = value.data;
71+
if (data != null) {
72+
r.r1 = data;
73+
return;
74+
}
75+
VarHandle.loadLoadFence();
76+
var current = (Value) VALUE.getAcquire(this);
77+
if (value == current) {
78+
r.r1 = null;
79+
return;
80+
}
81+
value = current;
82+
}
83+
}
84+
85+
static {
86+
try {
87+
VALUE = MethodHandles.lookup().findVarHandle(IntermittentNull.class, "value", Value.class);
88+
} catch (ReflectiveOperationException e) {
89+
throw new ExceptionInInitializerError(e);
90+
}
91+
}
92+
93+
static final class Value {
94+
@Nullable String data;
95+
96+
Value(String data) {
97+
this.data = data;
98+
}
99+
}
100+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
@NullMarked
2+
@CheckReturnValue
3+
package com..benmanes.caffeine.cache;
4+
5+
import com.google.errorprone.annotations.CheckReturnValue;
6+
import org.jspecify.annotations.NullMarked;
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[versions]
22
caffeine = "3.2.0"
3-
junit = "5.13.0"
3+
junit = "5.13.1"
44
reactor = "3.8.0-M3"
55
truth = "1.4.4"
66
versions = "0.52.0"
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip
1+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
22
distributionBase=GRADLE_USER_HOME
33
distributionPath=wrapper/dists
44
zipStoreBase=GRADLE_USER_HOME

0 commit comments

Comments
 (0)