Skip to content

Commit ba08c27

Browse files
authored
Use byte padding to avoid fields re-ordering on JDK 15 and above (#3168)
On modern JDK (15+) good old trick using `long` fields as padding is no longer reliable: fields can be reordered and packed into the unused space of superclass fields. To prevent false-sharing this commit switches to `byte` padding, for which you cannot have "unused" space, as the smallest byte takes as much space as the largest. See https://shipilev.net/jvm/objects-inside-out/#_superhierarchy_gaps_in_java_15. Fixes #2057.
1 parent 4c0b791 commit ba08c27

4 files changed

Lines changed: 198 additions & 71 deletions

File tree

reactor-core/src/main/java/reactor/core/publisher/QueueDrainSubscriber.java

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018-2021 VMware Inc. or its affiliates, All Rights Reserved.
2+
* Copyright (c) 2018-2022 VMware Inc. or its affiliates, All Rights Reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -284,9 +284,24 @@ static <Q, S> boolean checkTerminated(boolean d, boolean empty,
284284
//-------------------------------------------------------------------
285285

286286
/** Pads the header away from other fields. */
287+
@SuppressWarnings("unused")
287288
class QueueDrainSubscriberPad0 {
288-
volatile long p1, p2, p3, p4, p5, p6, p7;
289-
volatile long p8, p9, p10, p11, p12, p13, p14, p15;
289+
byte pad000,pad001,pad002,pad003,pad004,pad005,pad006,pad007;// 8b
290+
byte pad010,pad011,pad012,pad013,pad014,pad015,pad016,pad017;// 16b
291+
byte pad020,pad021,pad022,pad023,pad024,pad025,pad026,pad027;// 24b
292+
byte pad030,pad031,pad032,pad033,pad034,pad035,pad036,pad037;// 32b
293+
byte pad040,pad041,pad042,pad043,pad044,pad045,pad046,pad047;// 40b
294+
byte pad050,pad051,pad052,pad053,pad054,pad055,pad056,pad057;// 48b
295+
byte pad060,pad061,pad062,pad063,pad064,pad065,pad066,pad067;// 56b
296+
byte pad070,pad071,pad072,pad073,pad074,pad075,pad076,pad077;// 64b
297+
byte pad100,pad101,pad102,pad103,pad104,pad105,pad106,pad107;// 72b
298+
byte pad110,pad111,pad112,pad113,pad114,pad115,pad116,pad117;// 80b
299+
byte pad120,pad121,pad122,pad123,pad124,pad125,pad126,pad127;// 88b
300+
byte pad130,pad131,pad132,pad133,pad134,pad135,pad136,pad137;// 96b
301+
byte pad140,pad141,pad142,pad143,pad144,pad145,pad146,pad147;//104b
302+
byte pad150,pad151,pad152,pad153,pad154,pad155,pad156,pad157;//112b
303+
byte pad160,pad161,pad162,pad163,pad164,pad165,pad166,pad167;//120b
304+
byte pad170,pad171,pad172,pad173,pad174,pad175,pad176,pad177;//128b
290305
}
291306

292307
/** The WIP counter. */
@@ -295,9 +310,24 @@ class QueueDrainSubscriberWip extends QueueDrainSubscriberPad0 {
295310
}
296311

297312
/** Pads away the wip from the other fields. */
313+
@SuppressWarnings("unused")
298314
class QueueDrainSubscriberPad2 extends QueueDrainSubscriberWip {
299-
volatile long p1a, p2a, p3a, p4a, p5a, p6a, p7a;
300-
volatile long p8a, p9a, p10a, p11a, p12a, p13a, p14a, p15a;
315+
byte pad000,pad001,pad002,pad003,pad004,pad005,pad006,pad007;// 8b
316+
byte pad010,pad011,pad012,pad013,pad014,pad015,pad016,pad017;// 16b
317+
byte pad020,pad021,pad022,pad023,pad024,pad025,pad026,pad027;// 24b
318+
byte pad030,pad031,pad032,pad033,pad034,pad035,pad036,pad037;// 32b
319+
byte pad040,pad041,pad042,pad043,pad044,pad045,pad046,pad047;// 40b
320+
byte pad050,pad051,pad052,pad053,pad054,pad055,pad056,pad057;// 48b
321+
byte pad060,pad061,pad062,pad063,pad064,pad065,pad066,pad067;// 56b
322+
byte pad070,pad071,pad072,pad073,pad074,pad075,pad076,pad077;// 64b
323+
byte pad100,pad101,pad102,pad103,pad104,pad105,pad106,pad107;// 72b
324+
byte pad110,pad111,pad112,pad113,pad114,pad115,pad116,pad117;// 80b
325+
byte pad120,pad121,pad122,pad123,pad124,pad125,pad126,pad127;// 88b
326+
byte pad130,pad131,pad132,pad133,pad134,pad135,pad136,pad137;// 96b
327+
byte pad140,pad141,pad142,pad143,pad144,pad145,pad146,pad147;//104b
328+
byte pad150,pad151,pad152,pad153,pad154,pad155,pad156,pad157;//112b
329+
byte pad160,pad161,pad162,pad163,pad164,pad165,pad166,pad167;//120b
330+
byte pad170,pad171,pad172,pad173,pad174,pad175,pad176,pad177;//128b
301331
}
302332

303333
/** Contains the requested field. */
@@ -310,7 +340,22 @@ class QueueDrainSubscriberPad3 extends QueueDrainSubscriberPad2 {
310340
}
311341

312342
/** Pads away the requested from the other fields. */
343+
@SuppressWarnings("unused")
313344
class QueueDrainSubscriberPad4 extends QueueDrainSubscriberPad3 {
314-
volatile long q1, q2, q3, q4, q5, q6, q7;
315-
volatile long q8, q9, q10, q11, q12, q13, q14, q15;
345+
byte pad000,pad001,pad002,pad003,pad004,pad005,pad006,pad007;// 8b
346+
byte pad010,pad011,pad012,pad013,pad014,pad015,pad016,pad017;// 16b
347+
byte pad020,pad021,pad022,pad023,pad024,pad025,pad026,pad027;// 24b
348+
byte pad030,pad031,pad032,pad033,pad034,pad035,pad036,pad037;// 32b
349+
byte pad040,pad041,pad042,pad043,pad044,pad045,pad046,pad047;// 40b
350+
byte pad050,pad051,pad052,pad053,pad054,pad055,pad056,pad057;// 48b
351+
byte pad060,pad061,pad062,pad063,pad064,pad065,pad066,pad067;// 56b
352+
byte pad070,pad071,pad072,pad073,pad074,pad075,pad076,pad077;// 64b
353+
byte pad100,pad101,pad102,pad103,pad104,pad105,pad106,pad107;// 72b
354+
byte pad110,pad111,pad112,pad113,pad114,pad115,pad116,pad117;// 80b
355+
byte pad120,pad121,pad122,pad123,pad124,pad125,pad126,pad127;// 88b
356+
byte pad130,pad131,pad132,pad133,pad134,pad135,pad136,pad137;// 96b
357+
byte pad140,pad141,pad142,pad143,pad144,pad145,pad146,pad147;//104b
358+
byte pad150,pad151,pad152,pad153,pad154,pad155,pad156,pad157;//112b
359+
byte pad160,pad161,pad162,pad163,pad164,pad165,pad166,pad167;//120b
360+
byte pad170,pad171,pad172,pad173,pad174,pad175,pad176,pad177;//128b
316361
}

reactor-core/src/main/java/reactor/util/concurrent/SpscArrayQueue.java

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2016-2021 VMware Inc. or its affiliates, All Rights Reserved.
2+
* Copyright (c) 2016-2022 VMware Inc. or its affiliates, All Rights Reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -175,13 +175,28 @@ public SpscArrayQueueCold(int length) {
175175
mask = length - 1;
176176
}
177177
}
178+
@SuppressWarnings("unused")
178179
class SpscArrayQueueP1<T> extends SpscArrayQueueCold<T> {
179180
/** */
180181
private static final long serialVersionUID = -4461305682174876914L;
181-
182-
long p00, p01, p02, p03, p04, p05, p06, p07;
183-
long p08, p09, p0A, p0B, p0C, p0D, p0E;
184182

183+
byte pad000,pad001,pad002,pad003,pad004,pad005,pad006,pad007;// 8b
184+
byte pad010,pad011,pad012,pad013,pad014,pad015,pad016,pad017;// 16b
185+
byte pad020,pad021,pad022,pad023,pad024,pad025,pad026,pad027;// 24b
186+
byte pad030,pad031,pad032,pad033,pad034,pad035,pad036,pad037;// 32b
187+
byte pad040,pad041,pad042,pad043,pad044,pad045,pad046,pad047;// 40b
188+
byte pad050,pad051,pad052,pad053,pad054,pad055,pad056,pad057;// 48b
189+
byte pad060,pad061,pad062,pad063,pad064,pad065,pad066,pad067;// 56b
190+
byte pad070,pad071,pad072,pad073,pad074,pad075,pad076,pad077;// 64b
191+
byte pad100,pad101,pad102,pad103,pad104,pad105,pad106,pad107;// 72b
192+
byte pad110,pad111,pad112,pad113,pad114,pad115,pad116,pad117;// 80b
193+
byte pad120,pad121,pad122,pad123,pad124,pad125,pad126,pad127;// 88b
194+
byte pad130,pad131,pad132,pad133,pad134,pad135,pad136,pad137;// 96b
195+
byte pad140,pad141,pad142,pad143,pad144,pad145,pad146,pad147;//104b
196+
byte pad150,pad151,pad152,pad153,pad154,pad155,pad156,pad157;//112b
197+
byte pad160,pad161,pad162,pad163,pad164,pad165,pad166,pad167;//120b
198+
byte pad170,pad171,pad172,pad173,pad174,pad175,pad176,pad177;//128b
199+
byte pad200,pad201,pad202,pad203; //132b
185200
SpscArrayQueueP1(int length) {
186201
super(length);
187202
}
@@ -203,12 +218,27 @@ class SpscArrayQueueProducer<T> extends SpscArrayQueueP1<T> {
203218

204219
}
205220

221+
@SuppressWarnings("unused")
206222
class SpscArrayQueueP2<T> extends SpscArrayQueueProducer<T> {
207223
/** */
208224
private static final long serialVersionUID = -5400235061461013116L;
209-
210-
long p00, p01, p02, p03, p04, p05, p06, p07;
211-
long p08, p09, p0A, p0B, p0C, p0D, p0E;
225+
226+
byte pad000,pad001,pad002,pad003,pad004,pad005,pad006,pad007;// 8b
227+
byte pad010,pad011,pad012,pad013,pad014,pad015,pad016,pad017;// 16b
228+
byte pad020,pad021,pad022,pad023,pad024,pad025,pad026,pad027;// 24b
229+
byte pad030,pad031,pad032,pad033,pad034,pad035,pad036,pad037;// 32b
230+
byte pad040,pad041,pad042,pad043,pad044,pad045,pad046,pad047;// 40b
231+
byte pad050,pad051,pad052,pad053,pad054,pad055,pad056,pad057;// 48b
232+
byte pad060,pad061,pad062,pad063,pad064,pad065,pad066,pad067;// 56b
233+
byte pad070,pad071,pad072,pad073,pad074,pad075,pad076,pad077;// 64b
234+
byte pad100,pad101,pad102,pad103,pad104,pad105,pad106,pad107;// 72b
235+
byte pad110,pad111,pad112,pad113,pad114,pad115,pad116,pad117;// 80b
236+
byte pad120,pad121,pad122,pad123,pad124,pad125,pad126,pad127;// 88b
237+
byte pad130,pad131,pad132,pad133,pad134,pad135,pad136,pad137;// 96b
238+
byte pad140,pad141,pad142,pad143,pad144,pad145,pad146,pad147;//104b
239+
byte pad150,pad151,pad152,pad153,pad154,pad155,pad156,pad157;//112b
240+
byte pad160,pad161,pad162,pad163,pad164,pad165,pad166,pad167;//120b
241+
byte pad170,pad171,pad172,pad173,pad174,pad175,pad176,pad177;//128b
212242

213243
SpscArrayQueueP2(int length) {
214244
super(length);
@@ -231,12 +261,27 @@ class SpscArrayQueueConsumer<T> extends SpscArrayQueueP2<T> {
231261

232262
}
233263

264+
@SuppressWarnings("unused")
234265
class SpscArrayQueueP3<T> extends SpscArrayQueueConsumer<T> {
235266
/** */
236267
private static final long serialVersionUID = -2684922090021364171L;
237-
238-
long p00, p01, p02, p03, p04, p05, p06, p07;
239-
long p08, p09, p0A, p0B, p0C, p0D, p0E;
268+
269+
byte pad000,pad001,pad002,pad003,pad004,pad005,pad006,pad007;// 8b
270+
byte pad010,pad011,pad012,pad013,pad014,pad015,pad016,pad017;// 16b
271+
byte pad020,pad021,pad022,pad023,pad024,pad025,pad026,pad027;// 24b
272+
byte pad030,pad031,pad032,pad033,pad034,pad035,pad036,pad037;// 32b
273+
byte pad040,pad041,pad042,pad043,pad044,pad045,pad046,pad047;// 40b
274+
byte pad050,pad051,pad052,pad053,pad054,pad055,pad056,pad057;// 48b
275+
byte pad060,pad061,pad062,pad063,pad064,pad065,pad066,pad067;// 56b
276+
byte pad070,pad071,pad072,pad073,pad074,pad075,pad076,pad077;// 64b
277+
byte pad100,pad101,pad102,pad103,pad104,pad105,pad106,pad107;// 72b
278+
byte pad110,pad111,pad112,pad113,pad114,pad115,pad116,pad117;// 80b
279+
byte pad120,pad121,pad122,pad123,pad124,pad125,pad126,pad127;// 88b
280+
byte pad130,pad131,pad132,pad133,pad134,pad135,pad136,pad137;// 96b
281+
byte pad140,pad141,pad142,pad143,pad144,pad145,pad146,pad147;//104b
282+
byte pad150,pad151,pad152,pad153,pad154,pad155,pad156,pad157;//112b
283+
byte pad160,pad161,pad162,pad163,pad164,pad165,pad166,pad167;//120b
284+
byte pad170,pad171,pad172,pad173,pad174,pad175,pad176,pad177;//128b
240285

241286
SpscArrayQueueP3(int length) {
242287
super(length);
Lines changed: 35 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018-2021 VMware Inc. or its affiliates, All Rights Reserved.
2+
* Copyright (c) 2018-2022 VMware Inc. or its affiliates, All Rights Reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,64 +16,56 @@
1616

1717
package reactor.core.publisher;
1818

19-
import java.util.concurrent.atomic.AtomicReference;
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
import java.util.concurrent.atomic.AtomicLong;
2022

2123
import org.junit.jupiter.api.Tag;
2224
import org.junit.jupiter.api.Test;
2325
import org.openjdk.jol.info.ClassLayout;
24-
import org.openjdk.jol.info.FieldLayout;
2526

2627
import static org.assertj.core.api.Assertions.assertThat;
2728

2829
public class QueueDrainSubscriberTest {
2930

3031
@Test
3132
@Tag("slow")
32-
public void objectPadding() {
33+
void objectPadding() {
3334
ClassLayout layout = ClassLayout.parseClass(QueueDrainSubscriber.class);
34-
AtomicReference<FieldLayout> wip = new AtomicReference<>();
35-
AtomicReference<FieldLayout> requested = new AtomicReference<>();
35+
36+
AtomicLong currentPaddingSize = new AtomicLong();
37+
List<String> fields = new ArrayList<>();
38+
List<Long> paddingSizes = new ArrayList<>();
3639

3740
layout.fields().forEach(f -> {
38-
if ("wip".equals(f.name())) wip.set(f);
39-
else if ("requested".equals(f.name())) requested.set(f);
41+
if (f.name().startsWith("pad")) {
42+
currentPaddingSize.addAndGet(f.size());
43+
} else {
44+
if (currentPaddingSize.get() > 0) {
45+
fields.add("[padding]");
46+
paddingSizes.add(currentPaddingSize.getAndSet(0));
47+
}
48+
fields.add(f.name());
49+
}
4050
});
51+
if (currentPaddingSize.get() > 0) {
52+
fields.add("[padding]");
53+
paddingSizes.add(currentPaddingSize.getAndSet(0));
54+
}
4155

42-
final FieldLayout fieldAfterRequested = layout.fields()
43-
.tailSet(requested.get())
44-
.stream()
45-
.skip(1)
46-
.filter(fl -> fl.name().length() >= 4)
47-
.findFirst()
48-
.get();
49-
50-
assertThat(layout.fields().headSet(wip.get()))
51-
.as("wip pre-padding")
52-
.hasSize(15)
53-
.allSatisfy(fl -> assertThat(fl.name()).startsWith("p"));
54-
55-
assertThat(layout.fields().subSet(wip.get(), requested.get()).stream().skip(1))
56-
.as("wip-requested padding")
57-
.hasSize(15)
58-
.allSatisfy(fl -> assertThat(fl.name()).startsWith("p").endsWith("a"));
56+
assertThat(fields).containsExactly(
57+
"[padding]",
58+
"wip",
59+
"[padding]",
60+
"requested",
61+
"[padding]",
62+
"cancelled",
63+
"done",
64+
"actual",
65+
"queue",
66+
"error"
67+
);
5968

60-
assertThat(layout.fields().subSet(requested.get(), fieldAfterRequested)
61-
.stream()
62-
.skip(1))
63-
.as("requested post-padding")
64-
.hasSize(15)
65-
.allSatisfy(fl -> assertThat(fl.name()).startsWith("q").isNotEqualTo("queue"));
66-
67-
assertThat(wip.get().offset())
68-
.as("wip offset")
69-
.isEqualTo(136);
70-
assertThat(requested.get().offset())
71-
.as("requested offset")
72-
.isEqualTo(wip.get().offset() + 128);
73-
74-
System.out.println(wip.get());
75-
System.out.println(requested.get());
76-
System.out.println(fieldAfterRequested);
69+
assertThat(paddingSizes).containsExactly(128L, 128L, 128L);
7770
}
78-
7971
}

0 commit comments

Comments
 (0)