blob: e81cebc036e65989f7c6da956810996c98a65edc [file] [log] [blame]
// Copyright 2017 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.devtools.build.lib.shell;
import com.google.devtools.build.lib.shell.Consumers.OutErrConsumers;
import java.io.IOException;
/**
* Basic and only implementation of {@link FutureCommandResult} for use by implementations of
* {@link SubprocessFactory}.
*/
final class FutureCommandResultImpl implements FutureCommandResult {
private final Command command;
private final Subprocess process;
private final OutErrConsumers outErrConsumers;
private final boolean killSubprocessOnInterrupt;
public FutureCommandResultImpl(
Command command,
Subprocess process,
OutErrConsumers outErrConsumers,
boolean killSubprocessOnInterrupt) {
this.command = command;
this.process = process;
this.outErrConsumers = outErrConsumers;
this.killSubprocessOnInterrupt = killSubprocessOnInterrupt;
}
@Override
public CommandResult get() throws AbnormalTerminationException {
TerminationStatus status = waitForProcess(process, killSubprocessOnInterrupt);
try {
if (Thread.currentThread().isInterrupted()) {
outErrConsumers.cancel();
} else {
outErrConsumers.waitForCompletion();
}
} catch (IOException ioe) {
CommandResult noOutputResult =
CommandResult.builder()
.setStdoutStream(CommandResult.EMPTY_OUTPUT)
.setStderrStream(CommandResult.EMPTY_OUTPUT)
.setTerminationStatus(status)
.build();
if (status.success()) {
// If command was otherwise successful, throw an exception about this
throw new AbnormalTerminationException(command, noOutputResult, ioe);
} else {
// Otherwise, throw the more important exception -- command
// was not successful
String message = status
+ "; also encountered an error while attempting to retrieve output";
throw status.exited()
? new BadExitStatusException(command, noOutputResult, message, ioe)
: new AbnormalTerminationException(command, noOutputResult, message, ioe);
}
} finally {
process.close();
}
CommandResult commandResult =
CommandResult.builder()
.setStdoutStream(outErrConsumers.getAccumulatedOut())
.setStderrStream(outErrConsumers.getAccumulatedErr())
.setTerminationStatus(status)
.build();
commandResult.logThis();
if (status.success()) {
return commandResult;
} else if (status.exited()) {
throw new BadExitStatusException(command, commandResult, status.toString());
} else {
throw new AbnormalTerminationException(command, commandResult, status.toString());
}
}
private static TerminationStatus waitForProcess(
Subprocess process, boolean killSubprocessOnInterrupt) {
boolean wasInterrupted = false;
try {
while (true) {
try {
process.waitFor();
return new TerminationStatus(process.exitValue(), process.timedout());
} catch (InterruptedException ie) {
wasInterrupted = true;
if (killSubprocessOnInterrupt) {
process.destroy();
}
}
}
} finally {
// Read this for detailed explanation: http://www.ibm.com/developerworks/library/j-jtp05236/
if (wasInterrupted) {
Thread.currentThread().interrupt(); // preserve interrupted status
}
}
}
@Override
public void cancel() {
process.destroy();
}
@Override
public boolean isDone() {
return process.finished();
}
}