blob: d1cedd1e58819e05c1eab8fcf6e4066d5c3a866d [file] [log] [blame]
// Copyright 2018 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.worker;
import com.google.common.annotations.VisibleForTesting;
import com.google.devtools.build.lib.actions.UserExecException;
import com.google.devtools.build.lib.events.EventHandler;
import com.google.devtools.build.lib.events.Reporter;
import com.google.devtools.build.lib.server.FailureDetails;
import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
import com.google.devtools.build.lib.server.FailureDetails.Worker.Code;
import com.google.devtools.build.lib.vfs.Path;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nullable;
/**
* A manager to instantiate and destroy multiplexers. There should only be one {@code
* WorkerMultiplexer} corresponding to workers with the same {@code WorkerKey}. If the {@code
* WorkerMultiplexer} has been constructed, other workers should point to the same one.
*/
public class WorkerMultiplexerManager {
/**
* A map from the hash of {@code WorkerKey} objects to the corresponding information about the
* multiplexer instance.
*/
private static final Map<WorkerKey, InstanceInfo> multiplexerInstance = new HashMap<>();
private WorkerMultiplexerManager() {}
/**
* Returns a {@code WorkerMultiplexer} instance to {@code WorkerProxy}. {@code WorkerProxy}
* objects with the same {@code WorkerKey} talk to the same {@code WorkerMultiplexer}. Also,
* record how many {@code WorkerProxy} objects are talking to this {@code WorkerMultiplexer}.
*/
public static synchronized WorkerMultiplexer getInstance(WorkerKey key, Path logFile) {
InstanceInfo instanceInfo =
multiplexerInstance.computeIfAbsent(
key, k -> new InstanceInfo(new WorkerMultiplexer(logFile, k)));
instanceInfo.increaseRefCount();
return instanceInfo.getWorkerMultiplexer();
}
static void beforeCommand(Reporter reporter) {
setReporter(reporter);
}
static void afterCommand() {
setReporter(null);
}
/**
* Sets the reporter for all existing multiplexer instances. This allows reporting problems
* encountered while fetching an instance, e.g. during WorkerProxy validation.
*/
private static synchronized void setReporter(@Nullable EventHandler reporter) {
for (InstanceInfo m : multiplexerInstance.values()) {
m.workerMultiplexer.setReporter(reporter);
}
}
/** Removes a {@code WorkerProxy} instance and reference count since it is no longer in use. */
public static synchronized void removeInstance(WorkerKey key) throws UserExecException {
InstanceInfo instanceInfo = multiplexerInstance.get(key);
if (instanceInfo == null) {
throw createUserExecException(
String.format(
"Attempting to remove non-existent %s multiplexer instance.", key.getMnemonic()),
Code.MULTIPLEXER_INSTANCE_REMOVAL_FAILURE);
}
instanceInfo.decreaseRefCount();
if (instanceInfo.getRefCount() == 0) {
instanceInfo.getWorkerMultiplexer().destroyMultiplexer();
multiplexerInstance.remove(key);
}
}
@VisibleForTesting
static WorkerMultiplexer getMultiplexer(WorkerKey key) throws UserExecException {
InstanceInfo instanceInfo = multiplexerInstance.get(key);
if (instanceInfo == null) {
throw createUserExecException(
"Accessing non-existent multiplexer instance.", Code.MULTIPLEXER_DOES_NOT_EXIST);
}
return instanceInfo.getWorkerMultiplexer();
}
@VisibleForTesting
static Integer getRefCount(WorkerKey key) throws UserExecException {
InstanceInfo instanceInfo = multiplexerInstance.get(key);
if (instanceInfo == null) {
throw createUserExecException(
"Accessing non-existent multiplexer instance.", Code.MULTIPLEXER_DOES_NOT_EXIST);
}
return instanceInfo.getRefCount();
}
@VisibleForTesting
static Integer getInstanceCount() {
return multiplexerInstance.keySet().size();
}
private static UserExecException createUserExecException(String message, Code detailedCode) {
return new UserExecException(
FailureDetail.newBuilder()
.setMessage(message)
.setWorker(FailureDetails.Worker.newBuilder().setCode(detailedCode))
.build());
}
/** Contains the WorkerMultiplexer instance and reference count. */
static class InstanceInfo {
private final WorkerMultiplexer workerMultiplexer;
private Integer refCount;
public InstanceInfo(WorkerMultiplexer workerMultiplexer) {
this.workerMultiplexer = workerMultiplexer;
this.refCount = 0;
}
public void increaseRefCount() {
refCount = refCount + 1;
}
public void decreaseRefCount() {
refCount = refCount - 1;
}
public WorkerMultiplexer getWorkerMultiplexer() {
return workerMultiplexer;
}
public Integer getRefCount() {
return refCount;
}
}
/** Resets the instances. For testing only. */
@VisibleForTesting
static void resetForTesting() {
for (InstanceInfo i : multiplexerInstance.values()) {
i.workerMultiplexer.destroyMultiplexer();
}
multiplexerInstance.clear();
}
/** Injects a given WorkerMultiplexer into the instance map with refcount 0. For testing only. */
@VisibleForTesting
static synchronized void injectForTesting(WorkerKey key, WorkerMultiplexer multiplexer) {
multiplexerInstance.put(key, new InstanceInfo(multiplexer));
}
}