// 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.exec;
import com.google.devtools.build.lib.actions.ActionInput;
import com.google.devtools.build.lib.actions.ActionInputFileCache;
import com.google.devtools.build.lib.actions.ExecException;
import com.google.devtools.build.lib.actions.Spawn;
import com.google.devtools.build.lib.util.io.FileOutErr;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.io.IOException;
import java.util.SortedMap;
/**
* A runner for spawns. Implementations can execute spawns on the local machine as a subprocess with
* or without sandboxing, on a remote machine, or only consult a remote cache.
*
*
Environment Variables
*
* - Implementations MUST set the specified environment variables.
*
- Implementations MAY add TMPDIR as an additional env variable, if it is not set already.
*
- If an implementation sets TMPDIR, it MUST be set to an absolute path.
*
- Implementations MUST NOT add any other environment variables.
*
*
* Command line
*
* - Implementations MUST use the specified command line unmodified by default.
*
- Implementations MAY modify the specified command line if explicitly requested by the user.
*
*
* Process
*
* - Implementations MUST be thread-safe.
*
- Implementations MUST ensure that all child processes (including transitive) exit in all
* cases, including successful completion, interruption, and timeout
*
- Implementations MUST return the exit code as observed from the subprocess if the subprocess
* exits naturally; they MUST not throw an exception for non-zero exit codes
*
- Implementations MUST be interruptible; they MUST throw {@link InterruptedException} from
* {@link #exec} when interrupted
*
- Implementations MUST apply the specified timeout to the execution of the subprocess
*
* - If no timeout is specified, the implementation MAY apply an implementation-specific
* timeout
*
- If the specified timeout is larger than an implementation-dependent maximum, then the
* implementation MUST throw {@link IllegalArgumentException}; it MUST not silently change
* the timeout to a smaller value
*
- If the timeout is exceeded, the implementation MUST throw TimeoutException, with the
* timeout that was applied to the subprocess (TODO)
*
*
*
* Optimistic Concurrency
* Bazel may choose to execute a spawn using multiple {@link SpawnRunner} implementations
* simultaneously in order to minimize total latency. This is especially useful for builds with few
* actions where remotely executing the actions incurs high round trip times.
*
* - All implementations MUST call {@link SpawnExecutionPolicy#lockOutputFiles} before writing
* to any of the output files, but may write to stdout and stderr without calling it. Instead,
* all callers must provide temporary locations for stdout & stderr if they ever call multiple
* {@link SpawnRunner} implementations concurrently. Spawn runners that use the local machine
* MUST either call it before starting the subprocess, or ensure that subprocesses write to
* temporary locations (for example by running in a mount namespace) and then copy or move the
* outputs into place.
*
- Implementations SHOULD delay calling {@link SpawnExecutionPolicy#lockOutputFiles} until
* just before writing.
*
*/
public interface SpawnRunner {
/**
* Used to report progress on the current spawn. This is mainly used to report the current state
* of the subprocess to the user, but may also be used to trigger parallel execution. For example,
* a dynamic scheduler may use the signal that there was a cache miss to start parallel execution
* of the same Spawn - also see the {@link SpawnRunner} documentation section on "optimistic
* concurrency".
*
* {@link SpawnRunner} implementations should post a progress status before any potentially
* long-running operation.
*/
public enum ProgressStatus {
/** Spawn is waiting for local or remote resources to become available. */
SCHEDULING,
/** The {@link SpawnRunner} is looking for a cache hit. */
CHECKING_CACHE,
/**
* Resources are acquired, and there was probably no cache hit. This MUST be posted before
* attempting to execute the subprocess.
*
*
Caching {@link SpawnRunner} implementations should only post this after a failed cache
* lookup, but may post this if cache lookup and execution happen within the same step, e.g. as
* part of a single RPC call with no mechanism to report cache misses.
*/
EXECUTING,
/** Downloading outputs from a remote machine. */
DOWNLOADING;
}
/**
* A helper class to provide additional tools and methods to {@link SpawnRunner} implementations.
*
*
This interface may change without notice.
*
*
Implementations must be at least thread-compatible, i.e., they must be safe as long as
* each instance is only used within a single thread. Different instances of the same class may
* be used by different threads, so they MUST not call any shared non-thread-safe objects.
*/
public interface SpawnExecutionPolicy {
/** The input file cache for this specific spawn. */
ActionInputFileCache getActionInputFileCache();
/**
* All implementations must call this method before writing to the provided stdout / stderr or
* to any of the output file locations. This method is used to coordinate - implementations
* must throw an {@link InterruptedException} for all but one caller.
*/
void lockOutputFiles() throws InterruptedException;
/** Returns the timeout that should be applied for the given {@link Spawn} instance. */
long getTimeoutMillis();
/** The files to which to write stdout and stderr. */
FileOutErr getFileOutErr();
SortedMap getInputMapping() throws IOException;
/** Reports a progress update to the Spawn strategy. */
void report(ProgressStatus state);
}
/**
* Run the given spawn.
*
* @param spawn the spawn to run
* @param policy a helper that provides additional parameters
* @return the result from running the spawn
* @throws InterruptedException if the calling thread was interrupted, or if the runner could not
* lock the output files (see {@link SpawnExecutionPolicy#lockOutputFiles()})
* @throws IOException if something went wrong reading or writing to the local file system
* @throws ExecException if the request is malformed
*/
SpawnResult exec(
Spawn spawn,
SpawnExecutionPolicy policy)
throws InterruptedException, IOException, ExecException;
}