Skip to content

Commit 2b7d411

Browse files
authoredMay 5, 2021
CODETOOLS-7902923: jcstress: Eliminate dependency on Executors/Futures in forked VMs
1 parent 1621610 commit 2b7d411

File tree

7 files changed

+230
-88
lines changed

7 files changed

+230
-88
lines changed
 

‎jcstress-core/src/main/java/org/openjdk/jcstress/ForkedMain.java

+6-30
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,12 @@
2828
import org.openjdk.jcstress.infra.collectors.TestResult;
2929
import org.openjdk.jcstress.infra.runners.ForkedTestConfig;
3030
import org.openjdk.jcstress.infra.runners.Runner;
31+
import org.openjdk.jcstress.infra.runners.VoidThread;
3132
import org.openjdk.jcstress.link.BinaryLinkClient;
3233
import org.openjdk.jcstress.os.AffinitySupport;
3334
import org.openjdk.jcstress.util.StringUtils;
3435

3536
import java.lang.reflect.Constructor;
36-
import java.util.concurrent.ExecutorService;
37-
import java.util.concurrent.Executors;
38-
import java.util.concurrent.ThreadFactory;
39-
import java.util.concurrent.atomic.AtomicInteger;
4037

4138
/**
4239
* Entry point for the forked VM run.
@@ -50,13 +47,10 @@ public static void main(String[] args) throws Exception {
5047
throw new IllegalStateException("Expected three arguments");
5148
}
5249

53-
ExecutorService pool = Executors.newCachedThreadPool(new MyThreadFactory());
54-
5550
// Pre-initialize the affinity support and threads, so that workers
5651
// do not have to do this on critical paths during the execution.
5752
// This also runs when the rest of the infrastructure starts up.
58-
pool.submit(new WarmupAffinityTask());
59-
pool.submit(new EmptyTask());
53+
new WarmupAffinityTask().start();
6054

6155
String host = args[0];
6256
int port = Integer.parseInt(args[1]);
@@ -71,8 +65,8 @@ public static void main(String[] args) throws Exception {
7165

7266
try {
7367
Class<?> aClass = Class.forName(config.generatedRunnerName);
74-
Constructor<?> cnstr = aClass.getConstructor(ForkedTestConfig.class, ExecutorService.class);
75-
Runner<?> o = (Runner<?>) cnstr.newInstance(config, pool);
68+
Constructor<?> cnstr = aClass.getConstructor(ForkedTestConfig.class);
69+
Runner<?> o = (Runner<?>) cnstr.newInstance(config);
7670
result = o.run();
7771
forceExit = o.forceExit();
7872
} catch (ClassFormatError | NoClassDefFoundError | NoSuchMethodError | NoSuchFieldError e) {
@@ -94,21 +88,9 @@ public static void main(String[] args) throws Exception {
9488
}
9589
}
9690

97-
private static class MyThreadFactory implements ThreadFactory {
98-
private final AtomicInteger id = new AtomicInteger();
99-
91+
private static class WarmupAffinityTask extends VoidThread {
10092
@Override
101-
public Thread newThread(Runnable r) {
102-
Thread t = new Thread(r);
103-
t.setName("jcstress-worker-" + id.incrementAndGet());
104-
t.setDaemon(true);
105-
return t;
106-
}
107-
}
108-
109-
private static class WarmupAffinityTask implements Runnable {
110-
@Override
111-
public void run() {
93+
protected void internalRun() {
11294
try {
11395
AffinitySupport.tryBind();
11496
} catch (Exception e) {
@@ -117,10 +99,4 @@ public void run() {
11799
}
118100
}
119101

120-
private static class EmptyTask implements Runnable {
121-
@Override
122-
public void run() {
123-
// Do nothing
124-
}
125-
}
126102
}

‎jcstress-core/src/main/java/org/openjdk/jcstress/infra/processors/JCStressTestProcessor.java

+37-45
Original file line numberDiff line numberDiff line change
@@ -330,8 +330,8 @@ private void generateContinuous(TestInfo info) {
330330
pw.println(" " + r + "[] gr;");
331331
pw.println();
332332

333-
pw.println(" public " + className + "(ForkedTestConfig config, ExecutorService pool) {");
334-
pw.println(" super(config, pool);");
333+
pw.println(" public " + className + "(ForkedTestConfig config) {");
334+
pw.println(" super(config);");
335335
pw.println(" }");
336336
pw.println();
337337

@@ -351,33 +351,28 @@ private void generateContinuous(TestInfo info) {
351351

352352
for (int a = 0; a < actorsCount; a++) {
353353
ExecutableElement el = info.getActors().get(a);
354-
pw.println(" Future<?> a" + a + " = pool.submit(new Callable<Object>() { public Object call() {");
354+
pw.println(" VoidThread a" + a + " = new VoidThread() { protected void internalRun() {");
355355
pw.print(" ");
356356
emitMethod(pw, el, (isStateItself ? "s." : "t.") + el.getSimpleName(), "s", "r", false);
357357
pw.println(";");
358-
pw.println(" return null;");
359-
pw.println(" }});");
358+
pw.println(" }};");
359+
}
360+
361+
for (int a = 0; a < actorsCount; a++) {
362+
pw.println(" a" + a + ".start();");
360363
}
361364

362365
for (int a = 0; a < actorsCount; a++) {
363-
pw.println(" try {");
364-
pw.println(" a" + a + ".get();");
365-
pw.println(" } catch (ExecutionException e) {");
366-
pw.println(" throw e.getCause();");
366+
pw.println(" a" + a + ".join();");
367+
pw.println(" if (a" + a + ".throwable() != null) {");
368+
pw.println(" throw a" + a + ".throwable();");
367369
pw.println(" }");
368370
}
369371

370372
if (info.getArbiter() != null) {
371-
pw.println(" try {");
372-
pw.println(" pool.submit(new Callable<Object>() { public Object call() {");
373-
pw.print(" ");
373+
pw.print(" ");
374374
emitMethod(pw, info.getArbiter(), (isStateItself ? "s." : "t.") + info.getArbiter().getSimpleName(), "s", "r", false);
375375
pw.println(";");
376-
pw.println(" return null;");
377-
pw.println(" }}).get();");
378-
pw.println(" } catch (ExecutionException e) {");
379-
pw.println(" throw e.getCause();");
380-
pw.println(" }");
381376
}
382377
pw.println(" counter.record(r);");
383378
pw.println(" }");
@@ -404,7 +399,7 @@ private void generateContinuous(TestInfo info) {
404399

405400
for (int a = 0; a < actorsCount; a++) {
406401
ExecutableElement el = info.getActors().get(a);
407-
pw.println(" Future<Long> a" + a + " = pool.submit(new Callable<Long>() { public Long call() {");
402+
pw.println(" LongThread a" + a + " = new LongThread() { public long internalRun() {");
408403
pw.println(" long a1 = AllocProfileSupport.getAllocatedBytes();");
409404
pw.println(" for (int c = 0; c < size; c++) {");
410405
pw.print(" ");
@@ -413,32 +408,26 @@ private void generateContinuous(TestInfo info) {
413408
pw.println(" }");
414409
pw.println(" long a2 = AllocProfileSupport.getAllocatedBytes();");
415410
pw.println(" return a2 - a1;");
416-
pw.println(" }});");
411+
pw.println(" }};");
412+
}
413+
414+
for (int a = 0; a < actorsCount; a++) {
415+
pw.println(" a" + a + ".start();");
417416
}
418417

419418
for (int a = 0; a < actorsCount; a++) {
420419
pw.println(" try {");
421-
pw.println(" cnts[0] += a" + a + ".get();");
422-
pw.println(" } catch (Throwable e) {");
423-
pw.println(" // Should not happen, checked in API check");
420+
pw.println(" a" + a + ".join();");
421+
pw.println(" cnts[0] += a" + a + ".result();");
422+
pw.println(" } catch (InterruptedException e) {");
424423
pw.println(" }");
425424
}
426425

427426
if (info.getArbiter() != null) {
428-
pw.println(" try {");
429-
pw.println(" long a = pool.submit(new Callable<Long>() { public Long call() {");
430-
pw.println(" long a1 = AllocProfileSupport.getAllocatedBytes();");
431-
pw.println(" for (int c = 0; c < size; c++) {");
432-
pw.print(" ");
427+
pw.println(" for (int c = 0; c < size; c++) {");
428+
pw.print(" ");
433429
emitMethod(pw, info.getArbiter(), (isStateItself ? "ls[c]." : "t.") + info.getArbiter().getSimpleName(), "ls[c]", "lr[c]", false);
434430
pw.println(";");
435-
pw.println(" }");
436-
pw.println(" long a2 = AllocProfileSupport.getAllocatedBytes();");
437-
pw.println(" return a2 - a1;");
438-
pw.println(" }}).get();");
439-
pw.println(" cnts[0] += a;");
440-
pw.println(" } catch (Throwable e) {");
441-
pw.println(" // Should not happen, checked in API check");
442431
pw.println(" }");
443432
}
444433

@@ -454,7 +443,7 @@ private void generateContinuous(TestInfo info) {
454443
pw.println();
455444

456445
pw.println(" @Override");
457-
pw.println(" public ArrayList<Future<Counter<" + r + ">>> internalRun() {");
446+
pw.println(" public ArrayList<CounterThread<" + r + ">> internalRun() {");
458447
if (!isStateItself) {
459448
pw.println(" test = new " + t + "();");
460449
}
@@ -468,13 +457,17 @@ private void generateContinuous(TestInfo info) {
468457
pw.println();
469458
pw.println(" control.isStopped = false;");
470459
pw.println();
471-
pw.println(" ArrayList<Future<Counter<" + r + ">>> results = new ArrayList<>(" + actorsCount + ");");
460+
pw.println(" ArrayList<CounterThread<" + r + ">> threads = new ArrayList<>(" + actorsCount + ");");
472461
for (ExecutableElement a : info.getActors()) {
473-
pw.println(" results.add(pool.submit(new Callable<Counter<" + r + ">>() { public Counter<" + r + "> call() {");
462+
pw.println(" threads.add(new CounterThread<" + r + ">() { public Counter<" + r + "> internalRun() {");
474463
pw.println(" return " + TASK_LOOP_PREFIX + a.getSimpleName() + "();");
475-
pw.println(" }}));");
464+
pw.println(" }});");
476465
}
477466
pw.println();
467+
pw.println(" for (CounterThread t : threads) {");
468+
pw.println(" t.start();");
469+
pw.println(" }");
470+
pw.println();
478471
pw.println(" if (config.time > 0) {");
479472
pw.println(" try {");
480473
pw.println(" TimeUnit.MILLISECONDS.sleep(config.time);");
@@ -484,7 +477,7 @@ private void generateContinuous(TestInfo info) {
484477
pw.println();
485478
pw.println(" control.isStopped = true;");
486479
pw.println();
487-
pw.println(" return results;");
480+
pw.println(" return threads;");
488481
pw.println(" }");
489482
pw.println();
490483

@@ -771,8 +764,8 @@ private void generateTermination(TestInfo info) {
771764
pw.println("public class " + generatedName + " extends Runner<" + generatedName + ".Outcome> {");
772765
pw.println();
773766

774-
pw.println(" public " + generatedName + "(ForkedTestConfig config, ExecutorService pool) {");
775-
pw.println(" super(config, pool);");
767+
pw.println(" public " + generatedName + "(ForkedTestConfig config) {");
768+
pw.println(" super(config);");
776769
pw.println(" }");
777770
pw.println();
778771

@@ -798,7 +791,7 @@ private void generateTermination(TestInfo info) {
798791
pw.println(" }");
799792
pw.println();
800793
pw.println(" @Override");
801-
pw.println(" public ArrayList<Future<Counter<Outcome>>> internalRun() {");
794+
pw.println(" public ArrayList<CounterThread<Outcome>> internalRun() {");
802795
pw.println(" throw new UnsupportedOperationException();");
803796
pw.println(" }");
804797
pw.println();
@@ -949,13 +942,12 @@ private void emitMethodTermination(PrintWriter pw, ExecutableElement el, String
949942
private void printImports(PrintWriter pw, TestInfo info) {
950943
Class<?>[] imports = new Class<?>[] {
951944
ArrayList.class, Arrays.class,
952-
ExecutorService.class, Future.class, TimeUnit.class,
945+
TimeUnit.class,
953946
ForkedTestConfig.class, TestResult.class,
954947
Runner.class, WorkerSync.class, Counter.class,
955-
ExecutionException.class,
956-
Callable.class,
957948
AffinitySupport.class, AllocProfileSupport.class,
958949
FootprintEstimator.class,
950+
VoidThread.class, LongThread.class, CounterThread.class
959951
};
960952

961953
for (Class<?> c : imports) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright (c) 2021, Red Hat, Inc. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package org.openjdk.jcstress.infra.runners;
26+
27+
import java.util.concurrent.atomic.AtomicInteger;
28+
29+
public class AbstractThread extends Thread {
30+
private static final AtomicInteger ID = new AtomicInteger();
31+
32+
protected volatile Throwable throwable;
33+
34+
public AbstractThread() {
35+
setDaemon(true);
36+
setName("jcstress-worker-" + ID.incrementAndGet());
37+
}
38+
39+
public Throwable throwable() { return throwable; }
40+
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (c) 2021, Red Hat, Inc. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package org.openjdk.jcstress.infra.runners;
26+
27+
import org.openjdk.jcstress.util.Counter;
28+
29+
public abstract class CounterThread<R> extends AbstractThread {
30+
private Counter<R> result;
31+
public Counter<R> result() {
32+
return result;
33+
}
34+
35+
@Override
36+
public void run() {
37+
try {
38+
result = internalRun();
39+
} catch (Throwable e) {
40+
throwable = e;
41+
}
42+
}
43+
44+
protected abstract Counter<R> internalRun();
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright (c) 2021, Red Hat, Inc. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package org.openjdk.jcstress.infra.runners;
26+
27+
public abstract class LongThread extends AbstractThread {
28+
private long result;
29+
public long result() {
30+
return result;
31+
}
32+
33+
@Override
34+
public void run() {
35+
try {
36+
result = internalRun();
37+
} catch (Throwable e) {
38+
throwable = e;
39+
}
40+
}
41+
42+
protected abstract long internalRun();
43+
44+
}

‎jcstress-core/src/main/java/org/openjdk/jcstress/infra/runners/Runner.java

+19-13
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,10 @@ public abstract class Runner<R> {
4545
protected static final int MIN_TIMEOUT_MS = 30*1000;
4646

4747
protected final Control control;
48-
protected final ExecutorService pool;
4948
protected final ForkedTestConfig config;
5049
protected volatile boolean forceExit;
5150

52-
public Runner(ForkedTestConfig config, ExecutorService pool) {
53-
this.pool = pool;
51+
public Runner(ForkedTestConfig config) {
5452
this.control = new Control();
5553
this.config = config;
5654
}
@@ -71,17 +69,25 @@ public TestResult run() {
7169
}
7270

7371
for (int c = 0; c < config.iters; c++) {
74-
ArrayList<Future<Counter<R>>> futures = internalRun();
72+
ArrayList<CounterThread<R>> workers = internalRun();
7573

7674
long startTime = System.nanoTime();
7775
do {
78-
ArrayList<Future<Counter<R>>> leftovers = new ArrayList<>();
79-
for (Future<Counter<R>> t : futures) {
76+
ArrayList<CounterThread<R>> leftovers = new ArrayList<>();
77+
for (CounterThread<R> t : workers) {
8078
try {
81-
result.merge(t.get(1, TimeUnit.SECONDS));
82-
} catch (TimeoutException e) {
83-
leftovers.add(t);
84-
} catch (ExecutionException | InterruptedException e) {
79+
t.join(1000);
80+
81+
if (t.throwable() != null) {
82+
return dumpFailure(Status.TEST_ERROR, "Unrecoverable error while running", t.throwable());
83+
}
84+
Counter<R> res = t.result();
85+
if (res != null) {
86+
result.merge(res);
87+
} else {
88+
leftovers.add(t);
89+
}
90+
} catch (InterruptedException e) {
8591
return dumpFailure(Status.TEST_ERROR, "Unrecoverable error while running", e.getCause());
8692
}
8793
}
@@ -92,8 +98,8 @@ public TestResult run() {
9298
return dumpFailure(Status.TIMEOUT_ERROR, "Timeout waiting for tasks to complete: " + timeSpent + " ms");
9399
}
94100

95-
futures = leftovers;
96-
} while (!futures.isEmpty());
101+
workers = leftovers;
102+
} while (!workers.isEmpty());
97103
}
98104

99105
return dump(result);
@@ -126,6 +132,6 @@ public boolean forceExit() {
126132

127133
public abstract void sanityCheck(Counter<R> counter) throws Throwable;
128134

129-
public abstract ArrayList<Future<Counter<R>>> internalRun();
135+
public abstract ArrayList<CounterThread<R>> internalRun();
130136

131137
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright (c) 2021, Red Hat, Inc. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package org.openjdk.jcstress.infra.runners;
26+
27+
public abstract class VoidThread extends AbstractThread {
28+
@Override
29+
public void run() {
30+
try {
31+
internalRun();
32+
} catch (Throwable e) {
33+
throwable = e;
34+
}
35+
}
36+
37+
protected abstract void internalRun();
38+
}

0 commit comments

Comments
 (0)
Please sign in to comment.