Search in sources :

Example 1 with Environment

use of com.google.devtools.build.skyframe.SkyFunction.Environment in project bazel by bazelbuild.

the class MemoizingEvaluatorTest method cycleAboveIndependentCycle.

/** @see ParallelEvaluatorTest#cycleAboveIndependentCycle() */
@Test
public void cycleAboveIndependentCycle() throws Exception {
    makeGraphDeterministic();
    SkyKey aKey = GraphTester.toSkyKey("a");
    final SkyKey bKey = GraphTester.toSkyKey("b");
    SkyKey cKey = GraphTester.toSkyKey("c");
    final SkyKey leafKey = GraphTester.toSkyKey("leaf");
    // When aKey depends on leafKey and bKey,
    tester.getOrCreate(aKey).setBuilder(new SkyFunction() {

        @Nullable
        @Override
        public SkyValue compute(SkyKey skyKey, Environment env) throws InterruptedException {
            env.getValues(ImmutableList.of(leafKey, bKey));
            return null;
        }

        @Nullable
        @Override
        public String extractTag(SkyKey skyKey) {
            return null;
        }
    });
    // And bKey depends on cKey,
    tester.getOrCreate(bKey).addDependency(cKey);
    // And cKey depends on aKey and bKey in that order,
    tester.getOrCreate(cKey).addDependency(aKey).addDependency(bKey);
    // And leafKey is a leaf node,
    tester.set(leafKey, new StringValue("leafy"));
    // Then when we evaluate,
    EvaluationResult<StringValue> result = tester.eval(/*keepGoing=*/
    true, aKey);
    // aKey has an error,
    assertEquals(null, result.get(aKey));
    if (cyclesDetected()) {
        // And both cycles were found underneath aKey: the (aKey->bKey->cKey) cycle, and the
        // aKey->(bKey->cKey) cycle. This is because cKey depended on aKey and then bKey, so it pushed
        // them down on the stack in that order, so bKey was processed first. It found its cycle, then
        // popped off the stack, and then aKey was processed and found its cycle.
        assertThatEvaluationResult(result).hasErrorEntryForKeyThat(aKey).hasCycleInfoThat().containsExactly(new CycleInfo(ImmutableList.of(aKey, bKey, cKey)), new CycleInfo(ImmutableList.of(aKey), ImmutableList.of(bKey, cKey)));
    } else {
        assertThatEvaluationResult(result).hasErrorEntryForKeyThat(aKey).hasCycleInfoThat().hasSize(1);
    }
    // When leafKey is changed, so that aKey will be marked as NEEDS_REBUILDING,
    tester.set(leafKey, new StringValue("crunchy"));
    // And cKey is invalidated, so that cycle checking will have to explore the full graph,
    tester.getOrCreate(cKey, /*markAsModified=*/
    true);
    tester.invalidate();
    // Then when we evaluate,
    EvaluationResult<StringValue> result2 = tester.eval(/*keepGoing=*/
    true, aKey);
    // Things are just as before.
    assertEquals(null, result2.get(aKey));
    if (cyclesDetected()) {
        assertThatEvaluationResult(result).hasErrorEntryForKeyThat(aKey).hasCycleInfoThat().containsExactly(new CycleInfo(ImmutableList.of(aKey, bKey, cKey)), new CycleInfo(ImmutableList.of(aKey), ImmutableList.of(bKey, cKey)));
    } else {
        assertThatEvaluationResult(result).hasErrorEntryForKeyThat(aKey).hasCycleInfoThat().hasSize(1);
    }
}
Also used : Environment(com.google.devtools.build.skyframe.SkyFunction.Environment) NotComparableStringValue(com.google.devtools.build.skyframe.GraphTester.NotComparableStringValue) StringValue(com.google.devtools.build.skyframe.GraphTester.StringValue) Nullable(javax.annotation.Nullable) Test(org.junit.Test)

Example 2 with Environment

use of com.google.devtools.build.skyframe.SkyFunction.Environment in project bazel by bazelbuild.

the class MemoizingEvaluatorTest method checkNotComparableNotPruned.

private void checkNotComparableNotPruned(boolean hasEvent) throws Exception {
    initializeTester();
    SkyKey parent = GraphTester.toSkyKey("parent");
    SkyKey child = GraphTester.toSkyKey("child");
    NotComparableStringValue notComparableString = new NotComparableStringValue("not comparable");
    if (hasEvent) {
        tester.getOrCreate(child).setConstantValue(notComparableString).setWarning("shmoop");
    } else {
        tester.getOrCreate(child).setConstantValue(notComparableString);
    }
    final AtomicInteger parentEvaluated = new AtomicInteger();
    final String val = "some val";
    tester.getOrCreate(parent).addDependency(child).setComputedValue(new ValueComputer() {

        @Override
        public SkyValue compute(Map<SkyKey, SkyValue> deps, Environment env) throws InterruptedException {
            parentEvaluated.incrementAndGet();
            return new StringValue(val);
        }
    });
    assertStringValue(val, tester.evalAndGet(/*keepGoing=*/
    false, parent));
    assertThat(parentEvaluated.get()).isEqualTo(1);
    if (hasEvent) {
        assertContainsEvent(eventCollector, "shmoop");
    } else {
        assertEventCount(0, eventCollector);
    }
    tester.resetPlayedEvents();
    tester.getOrCreate(child, /*markAsModified=*/
    true);
    tester.invalidate();
    assertStringValue(val, tester.evalAndGet(/*keepGoing=*/
    false, parent));
    assertThat(parentEvaluated.get()).isEqualTo(2);
    if (hasEvent) {
        assertContainsEvent(eventCollector, "shmoop");
    } else {
        assertEventCount(0, eventCollector);
    }
}
Also used : AtomicInteger(java.util.concurrent.atomic.AtomicInteger) NotComparableStringValue(com.google.devtools.build.skyframe.GraphTester.NotComparableStringValue) Environment(com.google.devtools.build.skyframe.SkyFunction.Environment) NotComparableStringValue(com.google.devtools.build.skyframe.GraphTester.NotComparableStringValue) StringValue(com.google.devtools.build.skyframe.GraphTester.StringValue) ValueComputer(com.google.devtools.build.skyframe.GraphTester.ValueComputer)

Example 3 with Environment

use of com.google.devtools.build.skyframe.SkyFunction.Environment in project bazel by bazelbuild.

the class MemoizingEvaluatorTest method cachedErrorCausesRestart.

/**
   * Tests that a race between a node being marked clean and another node requesting it is benign.
   * Here, we first evaluate errorKey, depending on invalidatedKey. Then we invalidate
   * invalidatedKey (without actually changing it) and evaluate errorKey and topKey together.
   * Through forced synchronization, we make sure that the following sequence of events happens:
   *
   * <ol>
   * <li>topKey requests errorKey;
   * <li>errorKey is marked clean;
   * <li>topKey finishes its first evaluation and registers its deps;
   * <li>topKey restarts, since it sees that its only dep, errorKey, is done;
   * <li>topKey sees the error thrown by errorKey and throws the error, shutting down the
   *     threadpool;
   * </ol>
   */
@Test
public void cachedErrorCausesRestart() throws Exception {
    // TrackingProgressReceiver does unnecessary examination of node values.
    initializeTester(new TrackingProgressReceiver() {

        @Override
        public void evaluated(SkyKey skyKey, Supplier<SkyValue> skyValueSupplier, EvaluationState state) {
            evaluated.add(skyKey);
        }
    });
    final SkyKey errorKey = GraphTester.toSkyKey("error");
    SkyKey invalidatedKey = GraphTester.toSkyKey("invalidated");
    final SkyKey topKey = GraphTester.toSkyKey("top");
    tester.getOrCreate(errorKey).addDependency(invalidatedKey).setHasError(true);
    tester.getOrCreate(invalidatedKey).setConstantValue(new StringValue("constant"));
    final CountDownLatch topSecondEval = new CountDownLatch(2);
    final CountDownLatch topRequestedError = new CountDownLatch(1);
    final CountDownLatch errorMarkedClean = new CountDownLatch(1);
    injectGraphListenerForTesting(new Listener() {

        @Override
        public void accept(SkyKey key, EventType type, Order order, Object context) {
            if (errorKey.equals(key) && type == EventType.MARK_CLEAN) {
                if (order == Order.BEFORE) {
                    TrackingAwaiter.INSTANCE.awaitLatchAndTrackExceptions(topRequestedError, "top didn't request");
                } else {
                    errorMarkedClean.countDown();
                    TrackingAwaiter.INSTANCE.awaitLatchAndTrackExceptions(topSecondEval, "top didn't restart");
                    // Make sure that the other thread notices the error and interrupts this thread.
                    try {
                        Thread.sleep(TestUtils.WAIT_TIMEOUT_MILLISECONDS);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }
            }
        }
    }, /*deterministic=*/
    false);
    EvaluationResult<StringValue> result = tester.eval(/*keepGoing=*/
    false, errorKey);
    assertThatEvaluationResult(result).hasError();
    assertThatEvaluationResult(result).hasErrorEntryForKeyThat(errorKey).hasExceptionThat().isNotNull();
    tester.getOrCreate(topKey).setBuilder(new SkyFunction() {

        @Nullable
        @Override
        public SkyValue compute(SkyKey skyKey, Environment env) throws SkyFunctionException, InterruptedException {
            topSecondEval.countDown();
            env.getValue(errorKey);
            topRequestedError.countDown();
            assertThat(env.valuesMissing()).isTrue();
            TrackingAwaiter.INSTANCE.awaitLatchAndTrackExceptions(errorMarkedClean, "error not marked clean");
            return null;
        }

        @Nullable
        @Override
        public String extractTag(SkyKey skyKey) {
            return null;
        }
    });
    tester.getOrCreate(invalidatedKey, /*markAsModified=*/
    true);
    tester.invalidate();
    EvaluationResult<StringValue> result2 = tester.eval(/*keepGoing=*/
    false, errorKey, topKey);
    assertThatEvaluationResult(result2).hasError();
    assertThatEvaluationResult(result2).hasErrorEntryForKeyThat(errorKey).hasExceptionThat().isNotNull();
    assertThatEvaluationResult(result2).hasErrorEntryForKeyThat(topKey).hasExceptionThat().isNotNull();
    assertThatEvaluationResult(result2).hasErrorEntryForKeyThat(topKey).rootCauseOfExceptionIs(errorKey);
}
Also used : Order(com.google.devtools.build.skyframe.NotifyingHelper.Order) Listener(com.google.devtools.build.skyframe.NotifyingHelper.Listener) EventType(com.google.devtools.build.skyframe.NotifyingHelper.EventType) CountDownLatch(java.util.concurrent.CountDownLatch) Environment(com.google.devtools.build.skyframe.SkyFunction.Environment) NotComparableStringValue(com.google.devtools.build.skyframe.GraphTester.NotComparableStringValue) StringValue(com.google.devtools.build.skyframe.GraphTester.StringValue) Nullable(javax.annotation.Nullable) Test(org.junit.Test)

Example 4 with Environment

use of com.google.devtools.build.skyframe.SkyFunction.Environment in project bazel by bazelbuild.

the class MemoizingEvaluatorTest method nodeInvalidatedThenDoubleCycle.

@Test
public void nodeInvalidatedThenDoubleCycle() throws InterruptedException {
    makeGraphDeterministic();
    // When topKey depends on depKey, and both are top-level nodes in the graph,
    final SkyKey topKey = skyKey("bKey");
    final SkyKey depKey = skyKey("aKey");
    tester.getOrCreate(topKey).addDependency(depKey).setConstantValue(new StringValue("a"));
    tester.getOrCreate(depKey).setConstantValue(new StringValue("b"));
    // Then evaluation is as expected.
    EvaluationResult<StringValue> result1 = tester.eval(/*keepGoing=*/
    true, topKey, depKey);
    assertThatEvaluationResult(result1).hasEntryThat(topKey).isEqualTo(new StringValue("a"));
    assertThatEvaluationResult(result1).hasEntryThat(depKey).isEqualTo(new StringValue("b"));
    assertThatEvaluationResult(result1).hasNoError();
    // When both nodes acquire self-edges, with topKey still also depending on depKey, in the same
    // group,
    tester.getOrCreate(depKey, /*markAsModified=*/
    true).addDependency(depKey);
    tester.getOrCreate(topKey, /*markAsModified=*/
    true).setConstantValue(null).removeDependency(depKey).setBuilder(new SkyFunction() {

        @Nullable
        @Override
        public SkyValue compute(SkyKey skyKey, Environment env) throws SkyFunctionException, InterruptedException {
            env.getValues(ImmutableList.of(topKey, depKey));
            assertThat(env.valuesMissing()).isTrue();
            return null;
        }

        @Nullable
        @Override
        public String extractTag(SkyKey skyKey) {
            return null;
        }
    });
    tester.invalidate();
    // Then evaluation is as expected -- topKey has removed its dep on depKey (since depKey was not
    // done when topKey found its cycle), and both topKey and depKey have cycles.
    EvaluationResult<StringValue> result2 = tester.eval(/*keepGoing=*/
    true, topKey, depKey);
    if (cyclesDetected()) {
        assertThatEvaluationResult(result2).hasErrorEntryForKeyThat(topKey).hasCycleInfoThat().containsExactly(new CycleInfo(ImmutableList.of(topKey)));
        assertThatEvaluationResult(result2).hasDirectDepsInGraphThat(topKey).containsExactly(topKey);
        assertThatEvaluationResult(result2).hasErrorEntryForKeyThat(depKey).hasCycleInfoThat().containsExactly(new CycleInfo(ImmutableList.of(depKey)));
    } else {
        assertThatEvaluationResult(result2).hasErrorEntryForKeyThat(topKey).hasCycleInfoThat().hasSize(1);
        assertThatEvaluationResult(result2).hasErrorEntryForKeyThat(depKey).hasCycleInfoThat().hasSize(1);
    }
    // When the nodes return to their original, error-free state,
    tester.getOrCreate(topKey, /*markAsModified=*/
    true).setBuilder(null).addDependency(depKey).setConstantValue(new StringValue("a"));
    tester.getOrCreate(depKey, /*markAsModified=*/
    true).removeDependency(depKey);
    tester.invalidate();
    // Then evaluation is as expected.
    EvaluationResult<StringValue> result3 = tester.eval(/*keepGoing=*/
    true, topKey, depKey);
    assertThatEvaluationResult(result3).hasEntryThat(topKey).isEqualTo(new StringValue("a"));
    assertThatEvaluationResult(result3).hasEntryThat(depKey).isEqualTo(new StringValue("b"));
    assertThatEvaluationResult(result3).hasNoError();
}
Also used : Environment(com.google.devtools.build.skyframe.SkyFunction.Environment) NotComparableStringValue(com.google.devtools.build.skyframe.GraphTester.NotComparableStringValue) StringValue(com.google.devtools.build.skyframe.GraphTester.StringValue) Nullable(javax.annotation.Nullable) Test(org.junit.Test)

Example 5 with Environment

use of com.google.devtools.build.skyframe.SkyFunction.Environment in project bazel by bazelbuild.

the class MemoizingEvaluatorTest method raceConditionWithNoKeepGoingErrors_InflightError.

@Test
public void raceConditionWithNoKeepGoingErrors_InflightError() throws Exception {
    // Given a graph of two nodes, errorKey and otherErrorKey,
    final SkyKey errorKey = GraphTester.toSkyKey("errorKey");
    final SkyKey otherErrorKey = GraphTester.toSkyKey("otherErrorKey");
    final CountDownLatch errorCommitted = new CountDownLatch(1);
    final CountDownLatch otherStarted = new CountDownLatch(1);
    final CountDownLatch otherDone = new CountDownLatch(1);
    final AtomicInteger numOtherInvocations = new AtomicInteger(0);
    final AtomicReference<String> bogusInvocationMessage = new AtomicReference<>(null);
    final AtomicReference<String> nonNullValueMessage = new AtomicReference<>(null);
    tester.getOrCreate(errorKey).setBuilder(new SkyFunction() {

        @Override
        public SkyValue compute(SkyKey skyKey, Environment env) throws SkyFunctionException {
            // Given that errorKey waits for otherErrorKey to begin evaluation before completing
            // its evaluation,
            TrackingAwaiter.INSTANCE.awaitLatchAndTrackExceptions(otherStarted, "otherErrorKey's SkyFunction didn't start in time.");
            // And given that errorKey throws an error,
            throw new GenericFunctionException(new SomeErrorException("error"), Transience.PERSISTENT);
        }

        @Override
        public String extractTag(SkyKey skyKey) {
            return null;
        }
    });
    tester.getOrCreate(otherErrorKey).setBuilder(new SkyFunction() {

        @Override
        public SkyValue compute(SkyKey skyKey, Environment env) throws SkyFunctionException, InterruptedException {
            otherStarted.countDown();
            int invocations = numOtherInvocations.incrementAndGet();
            // And given that otherErrorKey waits for errorKey's error to be committed before
            // trying to get errorKey's value,
            TrackingAwaiter.INSTANCE.awaitLatchAndTrackExceptions(errorCommitted, "errorKey's error didn't get committed to the graph in time");
            try {
                SkyValue value = env.getValueOrThrow(errorKey, SomeErrorException.class);
                if (value != null) {
                    nonNullValueMessage.set("bogus non-null value " + value);
                }
                if (invocations != 1) {
                    bogusInvocationMessage.set("bogus invocation count: " + invocations);
                }
                otherDone.countDown();
                // And given that otherErrorKey throws an error,
                throw new GenericFunctionException(new SomeErrorException("other"), Transience.PERSISTENT);
            } catch (SomeErrorException e) {
                fail();
                return null;
            }
        }

        @Override
        public String extractTag(SkyKey skyKey) {
            return null;
        }
    });
    injectGraphListenerForTesting(new Listener() {

        @Override
        public void accept(SkyKey key, EventType type, Order order, Object context) {
            if (key.equals(errorKey) && type == EventType.SET_VALUE && order == Order.AFTER) {
                errorCommitted.countDown();
                TrackingAwaiter.INSTANCE.awaitLatchAndTrackExceptions(otherDone, "otherErrorKey's SkyFunction didn't finish in time.");
            }
        }
    }, /*deterministic=*/
    false);
    // When the graph is evaluated in noKeepGoing mode,
    EvaluationResult<StringValue> result = tester.eval(/*keepGoing=*/
    false, errorKey, otherErrorKey);
    // Then the result reports that an error occurred because of errorKey,
    assertTrue(result.hasError());
    assertEquals(errorKey, result.getError().getRootCauseOfException());
    // And no value is committed for otherErrorKey,
    assertNull(tester.driver.getExistingErrorForTesting(otherErrorKey));
    assertNull(tester.driver.getExistingValueForTesting(otherErrorKey));
    // And no value was committed for errorKey,
    assertNull(nonNullValueMessage.get(), nonNullValueMessage.get());
    // And the SkyFunction for otherErrorKey was evaluated exactly once.
    assertEquals(numOtherInvocations.get(), 1);
    assertNull(bogusInvocationMessage.get(), bogusInvocationMessage.get());
// NB: The SkyFunction for otherErrorKey gets evaluated exactly once--it does not get
// re-evaluated during error bubbling. Why? When otherErrorKey throws, it is always the
// second error encountered, because it waited for errorKey's error to be committed before
// trying to get it. In fail-fast evaluations only the first failing SkyFunction's
// newly-discovered-dependencies are registered. Therefore, there won't be a reverse-dep from
// errorKey to otherErrorKey for the error to bubble through.
}
Also used : Order(com.google.devtools.build.skyframe.NotifyingHelper.Order) Listener(com.google.devtools.build.skyframe.NotifyingHelper.Listener) EventType(com.google.devtools.build.skyframe.NotifyingHelper.EventType) AtomicReference(java.util.concurrent.atomic.AtomicReference) CountDownLatch(java.util.concurrent.CountDownLatch) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Environment(com.google.devtools.build.skyframe.SkyFunction.Environment) NotComparableStringValue(com.google.devtools.build.skyframe.GraphTester.NotComparableStringValue) StringValue(com.google.devtools.build.skyframe.GraphTester.StringValue) Test(org.junit.Test)

Aggregations

Environment (com.google.devtools.build.skyframe.SkyFunction.Environment)21 NotComparableStringValue (com.google.devtools.build.skyframe.GraphTester.NotComparableStringValue)19 StringValue (com.google.devtools.build.skyframe.GraphTester.StringValue)19 Test (org.junit.Test)19 Nullable (javax.annotation.Nullable)9 CountDownLatch (java.util.concurrent.CountDownLatch)8 AtomicBoolean (java.util.concurrent.atomic.AtomicBoolean)6 EventType (com.google.devtools.build.skyframe.NotifyingHelper.EventType)5 Listener (com.google.devtools.build.skyframe.NotifyingHelper.Listener)5 Order (com.google.devtools.build.skyframe.NotifyingHelper.Order)5 ImmutableMap (com.google.common.collect.ImmutableMap)2 TestThread (com.google.devtools.build.lib.testutil.TestThread)2 ArrayList (java.util.ArrayList)2 Map (java.util.Map)2 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)2 ActionExecutionContext (com.google.devtools.build.lib.actions.ActionExecutionContext)1 ActionExecutionException (com.google.devtools.build.lib.actions.ActionExecutionException)1 Artifact (com.google.devtools.build.lib.actions.Artifact)1 ErrorInfoSubjectFactory.assertThatErrorInfo (com.google.devtools.build.skyframe.ErrorInfoSubjectFactory.assertThatErrorInfo)1 ValueComputer (com.google.devtools.build.skyframe.GraphTester.ValueComputer)1