blob: 750e2b4a81362fb58d319a13129e982413d1abd8 [file] [log] [blame]
jingwenb6f2ff192018-11-21 12:38:23 -08001// Copyright 2018 The Bazel Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.lib.dynamic;
15
16import static com.google.common.base.Preconditions.checkNotNull;
17
18import com.google.common.collect.ImmutableList;
19import com.google.common.util.concurrent.ThreadFactoryBuilder;
20import com.google.devtools.build.lib.actions.ExecutionStrategy;
21import com.google.devtools.build.lib.actions.ExecutorInitException;
22import com.google.devtools.build.lib.actions.Spawn;
23import com.google.devtools.build.lib.actions.SpawnActionContext;
24import com.google.devtools.build.lib.actions.Spawns;
25import com.google.devtools.build.lib.buildtool.BuildRequest;
26import com.google.devtools.build.lib.concurrent.ExecutorUtil;
27import com.google.devtools.build.lib.exec.ExecutionPolicy;
28import com.google.devtools.build.lib.exec.ExecutorBuilder;
29import com.google.devtools.build.lib.runtime.BlazeModule;
30import com.google.devtools.build.lib.runtime.Command;
31import com.google.devtools.build.lib.runtime.CommandEnvironment;
32import com.google.devtools.common.options.OptionsBase;
33import java.util.Arrays;
34import java.util.concurrent.ExecutorService;
35import java.util.concurrent.Executors;
36
37/**
38 * {@link BlazeModule} providing support for dynamic spawn execution and scheduling.
39 */
40public class DynamicExecutionModule extends BlazeModule {
41 private ExecutorService executorService;
42
43 @Override
44 public Iterable<Class<? extends OptionsBase>> getCommandOptions(Command command) {
45 return "build".equals(command.name())
46 ? ImmutableList.<Class<? extends OptionsBase>>of(DynamicExecutionOptions.class)
47 : ImmutableList.<Class<? extends OptionsBase>>of();
48 }
49
50 @Override
51 public void beforeCommand(CommandEnvironment env) {
52 executorService =
53 Executors.newCachedThreadPool(
54 new ThreadFactoryBuilder().setNameFormat("dynamic-execution-thread-%d").build());
55 env.getEventBus().register(this);
56 }
57
58 /**
59 * Adds a strategy that backs the dynamic scheduler to the executor builder.
60 *
61 * @param builder the executor builder to modify
62 * @param name the name of the strategy
63 * @param flagName name of the flag the strategy came from; used for error reporting
64 * purposes only
65 * @throws ExecutorInitException if the provided strategy would cause a scheduling cycle
66 */
67 private static void addBackingStrategy(ExecutorBuilder builder, String name, String flagName)
68 throws ExecutorInitException {
69 ExecutionStrategy strategy = DynamicSpawnStrategy.class.getAnnotation(ExecutionStrategy.class);
70 checkNotNull(strategy, "DynamicSpawnStrategy lacks expected ExecutionStrategy annotation");
71
72 if (Arrays.asList(strategy.name()).contains(name)) {
73 throw new ExecutorInitException("Cannot use strategy " + name + " in flag " + flagName
74 + " as it would create a cycle during execution");
75 }
76
77 builder.addStrategyByContext(SpawnActionContext.class, name);
78 }
79
80 @Override
81 public void executorInit(CommandEnvironment env, BuildRequest request, ExecutorBuilder builder)
82 throws ExecutorInitException {
83 DynamicExecutionOptions options = env.getOptions().getOptions(DynamicExecutionOptions.class);
84 if (options.internalSpawnScheduler) {
85 builder.addActionContext(
86 new DynamicSpawnStrategy(executorService, options, this::getExecutionPolicy));
87 builder.addStrategyByContext(SpawnActionContext.class, "dynamic");
88 addBackingStrategy(builder, options.dynamicLocalStrategy, "--dynamic_local_strategy");
89 addBackingStrategy(builder, options.dynamicRemoteStrategy, "--dynamic_remote_strategy");
90 addBackingStrategy(builder, options.dynamicWorkerStrategy, "--dynamic_worker_strategy");
91 }
92 }
93
94 /**
95 * Use the {@link Spawn} metadata to determine if it can be executed locally, remotely, or both.
96 * @param spawn the {@link Spawn} action
97 * @return the {@link ExecutionPolicy} containing local/remote execution policies
98 */
99 protected ExecutionPolicy getExecutionPolicy(Spawn spawn) {
100 if (!Spawns.mayBeExecutedRemotely(spawn)) {
101 return ExecutionPolicy.LOCAL_EXECUTION_ONLY;
102 }
103
104 return ExecutionPolicy.ANYWHERE;
105 }
106
107 @Override
108 public void afterCommand() {
109 ExecutorUtil.interruptibleShutdown(executorService);
110 executorService = null;
111 }
112}