1 /*
2 * Copyright (c) 2024, 2025, Oracle and/or its affiliates. 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 java.util.concurrent;
26
27 import java.lang.invoke.MethodHandles;
28 import java.lang.invoke.VarHandle;
29 import java.util.ArrayList;
30 import java.util.Comparator;
31 import java.util.List;
32 import java.util.NoSuchElementException;
33 import java.util.Objects;
34 import java.util.concurrent.StructuredTaskScope.Joiner;
35 import java.util.concurrent.StructuredTaskScope.Subtask;
36 import java.util.function.Predicate;
37 import jdk.internal.invoke.MhUtil;
38
39 /**
40 * Built-in StructuredTaskScope.Joiner implementations.
41 */
42 class Joiners {
43 private Joiners() { }
44
45 /**
46 * Throws IllegalArgumentException if the subtask is not in the UNAVAILABLE state.
47 */
48 private static void ensureUnavailable(Subtask<?> subtask) {
49 if (subtask.state() != Subtask.State.UNAVAILABLE) {
50 throw new IllegalArgumentException("Subtask not in UNAVAILABLE state");
51 }
52 }
53
54 /**
55 * Throws IllegalArgumentException if the subtask has not completed.
56 */
57 private static Subtask.State ensureCompleted(Subtask<?> subtask) {
58 Subtask.State state = subtask.state();
59 if (state == Subtask.State.UNAVAILABLE) {
60 throw new IllegalArgumentException("Subtask has not completed");
61 }
62 return state;
63 }
64
65 /**
66 * A joiner that returns a list of all results when all subtasks complete
67 * successfully. Cancels the scope if any subtask fails.
68 */
69 static final class AllSuccessful<T> implements Joiner<T, List<T>> {
70 private static final VarHandle FIRST_EXCEPTION =
71 MhUtil.findVarHandle(MethodHandles.lookup(), "firstException", Throwable.class);
72
73 // list of forked subtasks, created lazily, only accessed by owner thread
74 private List<Subtask<T>> subtasks;
75
76 private volatile Throwable firstException;
77
78 @Override
79 public boolean onFork(Subtask<T> subtask) {
80 ensureUnavailable(subtask);
81 if (subtasks == null) {
82 subtasks = new ArrayList<>();
83 }
84 subtasks.add(subtask);
85 return false;
86 }
87
88 @Override
89 public boolean onComplete(Subtask<T> subtask) {
90 Subtask.State state = ensureCompleted(subtask);
91 return (state == Subtask.State.FAILED)
92 && (firstException == null)
93 && FIRST_EXCEPTION.compareAndSet(this, null, subtask.exception());
94 }
95
96 @Override
97 public List<T> result() throws Throwable {
98 Throwable ex = firstException;
99 try {
100 if (ex != null) {
101 throw ex;
102 }
103 return (subtasks != null)
104 ? subtasks.stream().map(Subtask::get).toList()
105 : List.of();
106 } finally {
107 subtasks = null; // allow subtasks to be GC'ed
108 }
109 }
110 }
111
112 /**
113 * A joiner that returns the result of the first subtask to complete successfully.
114 * Cancels the scope if any subtasks succeeds.
115 */
116 static final class AnySuccessful<T> implements Joiner<T, T> {
117 private static final VarHandle SUBTASK =
118 MhUtil.findVarHandle(MethodHandles.lookup(), "subtask", Subtask.class);
119
120 // UNAVAILABLE < FAILED < SUCCESS
121 private static final Comparator<Subtask.State> SUBTASK_STATE_COMPARATOR =
122 Comparator.comparingInt(AnySuccessful::stateToInt);
123
124 private volatile Subtask<T> subtask;
125
126 /**
127 * Maps a Subtask.State to an int that can be compared.
128 */
129 private static int stateToInt(Subtask.State s) {
130 return switch (s) {
131 case UNAVAILABLE -> 0;
132 case FAILED -> 1;
133 case SUCCESS -> 2;
134 };
135 }
136
137 @Override
138 public boolean onComplete(Subtask<T> subtask) {
139 Subtask.State state = ensureCompleted(subtask);
140 Subtask<T> s;
141 while (((s = this.subtask) == null)
142 || SUBTASK_STATE_COMPARATOR.compare(s.state(), state) < 0) {
143 if (SUBTASK.compareAndSet(this, s, subtask)) {
144 return (state == Subtask.State.SUCCESS);
145 }
146 }
147 return false;
148 }
149
150 @Override
151 public T result() throws Throwable {
152 Subtask<T> subtask = this.subtask;
153 if (subtask == null) {
154 throw new NoSuchElementException("No subtasks completed");
155 }
156 return switch (subtask.state()) {
157 case SUCCESS -> subtask.get();
158 case FAILED -> throw subtask.exception();
159 default -> throw new InternalError();
160 };
161 }
162 }
163
164 /**
165 * A joiner that that waits for all successful subtasks. Cancels the scope if any
166 * subtask fails.
167 */
168 static final class AwaitSuccessful<T> implements Joiner<T, Void> {
169 private static final VarHandle FIRST_EXCEPTION =
170 MhUtil.findVarHandle(MethodHandles.lookup(), "firstException", Throwable.class);
171 private volatile Throwable firstException;
172
173 @Override
174 public boolean onComplete(Subtask<T> subtask) {
175 Subtask.State state = ensureCompleted(subtask);
176 return (state == Subtask.State.FAILED)
177 && (firstException == null)
178 && FIRST_EXCEPTION.compareAndSet(this, null, subtask.exception());
179 }
180
181 @Override
182 public Void result() throws Throwable {
183 Throwable ex = firstException;
184 if (ex != null) {
185 throw ex;
186 } else {
187 return null;
188 }
189 }
190 }
191
192 /**
193 * A joiner that returns a list of all subtasks.
194 */
195 static final class AllSubtasks<T> implements Joiner<T, List<Subtask<T>>> {
196 private final Predicate<Subtask<T>> isDone;
197
198 // list of forked subtasks, created lazily, only accessed by owner thread
199 private List<Subtask<T>> subtasks;
200
201 AllSubtasks(Predicate<Subtask<T>> isDone) {
202 this.isDone = Objects.requireNonNull(isDone);
203 }
204
205 @Override
206 public boolean onFork(Subtask<T> subtask) {
207 ensureUnavailable(subtask);
208 if (subtasks == null) {
209 subtasks = new ArrayList<>();
210 }
211 subtasks.add(subtask);
212 return false;
213 }
214
215 @Override
216 public boolean onComplete(Subtask<T> subtask) {
217 ensureCompleted(subtask);
218 return isDone.test(subtask);
219 }
220
221 @Override
222 public void onTimeout() {
223 // do nothing, this joiner does not throw TimeoutException
224 }
225
226 @Override
227 public List<Subtask<T>> result() {
228 if (subtasks != null) {
229 List<Subtask<T>> result = List.copyOf(subtasks);
230 subtasks = null; // allow subtasks to be GC'ed
231 return result;
232 } else {
233 return List.of();
234 }
235 }
236 }
237 }