Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2012 the original author or authors.
*
* 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 org.gradle.execution.plan;
import org.gradle.api.Action;
import org.gradle.api.NonNullApi;
import org.gradle.api.logging.Logger;
import org.gradle.api.logging.Logging;
import org.gradle.initialization.BuildCancellationToken;
import org.gradle.internal.Cast;
import org.gradle.internal.MutableReference;
import org.gradle.internal.UncheckedException;
import org.gradle.internal.build.ExecutionResult;
import org.gradle.internal.buildoption.InternalFlag;
import org.gradle.internal.buildoption.InternalOptions;
import org.gradle.internal.concurrent.CompositeStoppable;
import org.gradle.internal.concurrent.ExecutorFactory;
import org.gradle.internal.concurrent.ManagedExecutor;
import org.gradle.internal.concurrent.Stoppable;
import org.gradle.internal.logging.text.TreeFormatter;
import org.gradle.internal.resources.ResourceLockCoordinationService;
import org.gradle.internal.work.WorkerLeaseRegistry.WorkerLease;
import org.gradle.internal.work.WorkerLeaseService;
import org.gradle.internal.work.WorkerLimits;
import javax.annotation.Nullable;
import java.io.Closeable;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.ToLongFunction;
import static org.gradle.internal.resources.ResourceLockState.Disposition.FINISHED;
import static org.gradle.internal.resources.ResourceLockState.Disposition.RETRY;
@NonNullApi
public class DefaultPlanExecutor implements PlanExecutor, Stoppable {
public static final InternalFlag STATS = new InternalFlag("org.gradle.internal.executor.stats");
private static final Logger LOGGER = Logging.getLogger(DefaultPlanExecutor.class);
private final WorkerLimits workerLimits;
private final WorkerLeaseService workerLeaseService;
private final BuildCancellationToken cancellationToken;
private final ResourceLockCoordinationService coordinationService;
private final ManagedExecutor executor;
private final MergedQueues queue;
private final ExecutorState state = new ExecutorState();
private final ExecutorStats stats;
public DefaultPlanExecutor(
WorkerLimits workerLimits,
ExecutorFactory executorFactory,
WorkerLeaseService workerLeaseService,
BuildCancellationToken cancellationToken,
ResourceLockCoordinationService coordinationService,
InternalOptions internalOptions
) {
this.workerLimits = workerLimits;
this.cancellationToken = cancellationToken;
this.coordinationService = coordinationService;
this.workerLeaseService = workerLeaseService;
this.stats = internalOptions.getOption(STATS).get() ? new CollectingExecutorStats(state) : state;
this.queue = new MergedQueues(coordinationService, false);
this.executor = executorFactory.create("Execution worker");
}
@Override
public void stop() {
try {
CompositeStoppable.stoppable(queue, executor).stop();
} finally {
stats.report();
}
}
@Override
public ExecutionResult process(WorkSource workSource, Action worker) {
PlanDetails planDetails = new PlanDetails(Cast.uncheckedCast(workSource), Cast.uncheckedCast(worker));
queue.add(planDetails);
maybeStartWorkers(queue, executor);
// Run the work from the source from this thread as well, given that it will be blocked waiting for that work to complete anyway
WorkerLease currentWorkerLease = workerLeaseService.getCurrentWorkerLease();
MergedQueues thisPlanOnly = new MergedQueues(coordinationService, true);
thisPlanOnly.add(planDetails);
new ExecutorWorker(thisPlanOnly, currentWorkerLease, cancellationToken, coordinationService, workerLeaseService, stats).run();
List failures = new ArrayList<>();
awaitCompletion(workSource, currentWorkerLease, failures);
return ExecutionResult.maybeFailed(failures);
}
@Override
public void assertHealthy() {
// Wait until execution state is healthy.
// When all workers are waiting for work and work becomes available, there is a small period between signalling the workers and at least one worker waking up and starting work.
// If the health check is run during that period, it will fail because it appears that all workers are stuck.
//
// This situation can happen when the last task in an included build is executed by the thread that called process() and all other workers are waiting for work to be enabled in the
// other builds in the tree. The thread will signal the other threads and then stop running as a worker. At this point in time, work will be ready to start but all the (other) workers will
// still be waiting. A short time later, the workers will wake up and start running the work.
Instant expiry = Instant.now().plus(2, ChronoUnit.SECONDS);
ExecutorState.HealthState healthState;
do {
healthState = coordinationService.withStateLock(() -> state.healthCheck(queue));
if (healthState == null) {
// Health is ok
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw UncheckedException.throwAsUncheckedException(e);
}
} while (expiry.compareTo(Instant.now()) > 0);
// Health is not ok
// Log some diagnostic information to the console, in addition to aborting execution with an exception that will also be logged
// Given that the execution infrastructure is in an unhealthy state, it may not shut down cleanly and report the execution.
// So, log some details here just in case
System.out.println(healthState.detailMessage);
IllegalStateException failure = new IllegalStateException("Unable to make progress running work. There are items queued for execution but none of them can be started");
coordinationService.withStateLock(() -> queue.abortAllAndFail(failure));
}
/**
* Blocks until all items in the queue have been processed. This method will only return when every item in the queue has either completed, failed or been skipped.
*/
private void awaitCompletion(WorkSource> workSource, WorkerLease workerLease, Collection super Throwable> failures) {
coordinationService.withStateLock(resourceLockState -> {
if (workSource.allExecutionComplete()) {
// Need to hold a worker lease in order to finish up
if (!workerLease.isLockedByCurrentThread()) {
if (!workerLease.tryLock()) {
return RETRY;
}
}
workSource.collectFailures(failures);
queue.removeFinishedPlans();
return FINISHED;
} else {
// Release worker lease (if held) while waiting for work to complete
workerLease.unlock();
return RETRY;
}
});
}
private void maybeStartWorkers(MergedQueues queue, Executor executor) {
int executorCount = workerLimits.getMaxWorkerCount();
state.maybeStartWorkers(() -> {
LOGGER.debug("Using {} parallel executor threads", executorCount);
for (int i = 1; i < executorCount; i++) {
executor.execute(new ExecutorWorker(queue, null, cancellationToken, coordinationService, workerLeaseService, stats));
}
});
}
private static class PlanDetails {
final WorkSource