Skip to content

Commit af8fda4

Browse files
committed
[WFCORE-7278] Only send CancelAsyncRequest or report its failure if the ActiveOperation isn't complete
1 parent 1e6d802 commit af8fda4

File tree

2 files changed

+36
-12
lines changed

2 files changed

+36
-12
lines changed

controller-client/src/main/java/org/jboss/as/controller/client/impl/AbstractModelControllerClient.java

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.jboss.as.controller.client.OperationBuilder;
2525
import org.jboss.as.controller.client.OperationMessageHandler;
2626
import org.jboss.as.controller.client.OperationResponse;
27+
import org.jboss.as.controller.client.logging.ControllerClientLogger;
2728
import org.jboss.as.protocol.StreamUtils;
2829
import org.jboss.as.protocol.mgmt.AbstractManagementRequest;
2930
import org.jboss.as.protocol.mgmt.ActiveOperation;
@@ -37,6 +38,7 @@
3738
import org.jboss.as.protocol.mgmt.ManagementRequestHeader;
3839
import org.jboss.as.protocol.mgmt.ManagementResponseHeader;
3940
import org.jboss.dmr.ModelNode;
41+
import org.jboss.threads.AsyncFuture;
4042

4143

4244
/**
@@ -264,11 +266,27 @@ <T> CompletableFuture<T> executeRequest(final ManagementRequest<OperationRespons
264266
}
265267

266268
private void executeCancelAsyncRequest(ActiveOperation<?, ?> activeOperation) {
267-
try {
268-
getChannelAssociation().executeRequest(activeOperation.getOperationId(), new CancelAsyncRequest());
269-
} catch (Exception e) {
270-
throw new RuntimeException(e);
271-
}
269+
if (activeOperation.getResult().getStatus() == AsyncFuture.Status.WAITING) {
270+
// Tell the remote side to cancel
271+
Integer operationId = activeOperation.getOperationId();
272+
try {
273+
getChannelAssociation().executeRequest(operationId, new CancelAsyncRequest());
274+
} catch (Exception e) {
275+
AsyncFuture.Status status = activeOperation.getResult().getStatus();
276+
if (status == AsyncFuture.Status.WAITING) {
277+
if (e instanceof RuntimeException) {
278+
throw (RuntimeException) e;
279+
} else {
280+
throw new RuntimeException(e);
281+
}
282+
} else {
283+
// Most likely there was a race between our sending CancelAsyncRequest and the operation finishing.
284+
// The operation is done though, so just debug log the failure and move on.
285+
ControllerClientLogger.ROOT_LOGGER.debugf(e, "Executing CancelAsyncRequest for operation %s " +
286+
"failed but the operation reached status %s, so the failure is being ignored", operationId, status);
287+
}
288+
}
289+
} // else the ActiveOperation is already done and there's nothing to cancel
272290
}
273291

274292
static class OperationExecutionContext implements ActiveOperation.CompletedCallback<OperationResponse> {

protocol/src/main/java/org/jboss/as/protocol/mgmt/AsyncToCompletableFutureAdapter.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.util.function.Consumer;
1212
import java.util.function.Function;
1313

14+
import org.jboss.as.protocol.logging.ProtocolLogger;
1415
import org.jboss.threads.AsyncFuture;
1516

1617
/**
@@ -116,34 +117,39 @@ public boolean cancel(boolean mayInterruptIfRunning) {
116117
asyncCancelConsumer.accept(mayInterruptIfRunning);
117118
boolean afCancelled = asyncFuture.awaitUninterruptibly() == AsyncFuture.Status.CANCELLED;
118119

119-
if (!afCancelled) {
120+
if (afCancelled) {
121+
ProtocolLogger.ROOT_LOGGER.tracef("%s: AsyncFuture is cancelled; proceeding to cancel ourself",
122+
getClass().getEnclosingClass().getSimpleName(), getClass().getSimpleName());
123+
} else {
120124
// Our AsyncFuture.Listener completes this future asynchronously,
121125
// so block until it has done so before providing the resulting
122126
// state by calling super.cancel.
123127
//
124128
// We do this because canceling the async future may result in
125-
// the canceled management op returning with either outcome=cancelled
126-
// or outcome=failed. We're in this block, so we know it's the latter.
129+
// the canceled management op returning with outcome=success,
130+
// outcome=failed or outcome=cancelled.
127131
// We know this will result in the listener, in another
128-
// thread, eventually trying to complete this exceptionally.
132+
// thread, eventually trying to complete or cancel this CompletableFuture.
129133
// We don't want the determination of whether this method returns
130134
// true or false to be decided by a race between our call below to
131-
// super.cancel and the listener thread completing the
132-
// future as failed. So we block to let the listener thread win the race.
135+
// super.cancel and the listener thread completing or cancelling the
136+
// future. So we block to let the listener thread win the race.
133137
//
134138
// An alternative approach would be to call super.cancel first before
135139
// cancelling the asyncFuture. But that could result in inconsistency
136140
// between what this future reports vs the 'outcome' value of the
137141
// executed op. When ModelControllerClient async methods formerly returned an
138142
// AsyncFuture these results were consistent, so we're maintaining
139143
// that behavior with the CompletableFuture we now return.
144+
ProtocolLogger.ROOT_LOGGER.tracef("%s.%s: Awaiting AsyncFuture.Listener before proceeding to cancel ourself",
145+
getClass().getEnclosingClass().getSimpleName(), getClass().getSimpleName());
140146
awaitLatch();
141147
}
142148
return super.cancel(mayInterruptIfRunning);
143149
} finally {
144150
// Code hardening:
145151
// If for some reason asyncFuture.awaitUninterruptibly() returns CANCELLED
146-
// without the listener getting invoked, now that we're cancelled make sure
152+
// without the listener getting invoked (yet), now that we're cancelled make sure
147153
// the latch is counted down so other calls to cancel don't block.
148154
latch.countDown();
149155
}

0 commit comments

Comments
 (0)