Search in sources :

Example 6 with Environment

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

the class MemoizingEvaluatorTest method bubbleRace.

@Test
public // Regression test for bug: "[skyframe-m1]: registerIfDone() crash".
void bubbleRace() throws Exception {
    // The top-level value declares dependencies on a "badValue" in error, and a "sleepyValue"
    // which is very slow. After "badValue" fails, the builder interrupts the "sleepyValue" and
    // attempts to re-run "top" for error bubbling. Make sure this doesn't cause a precondition
    // failure because "top" still has an outstanding dep ("sleepyValue").
    tester.getOrCreate("top").setBuilder(new NoExtractorFunction() {

        @Override
        public SkyValue compute(SkyKey skyKey, Environment env) throws InterruptedException {
            env.getValue(toSkyKey("sleepyValue"));
            try {
                env.getValueOrThrow(toSkyKey("badValue"), SomeErrorException.class);
            } catch (SomeErrorException e) {
                // In order to trigger this bug, we need to request a dep on an already computed value.
                env.getValue(toSkyKey("otherValue1"));
            }
            if (!env.valuesMissing()) {
                throw new AssertionError("SleepyValue should always be unavailable");
            }
            return null;
        }
    });
    tester.getOrCreate("sleepyValue").setBuilder(new NoExtractorFunction() {

        @Override
        public SkyValue compute(SkyKey skyKey, Environment env) throws InterruptedException {
            Thread.sleep(99999);
            throw new AssertionError("I should have been interrupted");
        }
    });
    tester.getOrCreate("badValue").addDependency("otherValue1").setHasError(true);
    tester.getOrCreate("otherValue1").setConstantValue(new StringValue("otherVal1"));
    EvaluationResult<SkyValue> result = tester.eval(false, "top");
    assertTrue(result.hasError());
    assertEquals(toSkyKey("badValue"), Iterables.getOnlyElement(result.getError().getRootCauses()));
    assertThat(result.keyNames()).isEmpty();
}
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) Test(org.junit.Test)

Example 7 with Environment

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

the class MemoizingEvaluatorTest method changePruningAfterParentPrunes.

@Test
public void changePruningAfterParentPrunes() throws Exception {
    initializeTester();
    final SkyKey leaf = GraphTester.toSkyKey("leaf");
    SkyKey top = GraphTester.toSkyKey("top");
    tester.set(leaf, new StringValue("leafy"));
    // When top depends on leaf, but always returns the same value,
    final StringValue fixedTopValue = new StringValue("top");
    final AtomicBoolean topEvaluated = new AtomicBoolean(false);
    tester.getOrCreate(top).setBuilder(new SkyFunction() {

        @Override
        public SkyValue compute(SkyKey skyKey, Environment env) throws InterruptedException {
            topEvaluated.set(true);
            return env.getValue(leaf) == null ? null : fixedTopValue;
        }

        @Nullable
        @Override
        public String extractTag(SkyKey skyKey) {
            return null;
        }
    });
    // And top is evaluated,
    StringValue topValue = (StringValue) tester.evalAndGet("top");
    // Then top's value is as expected,
    assertEquals(fixedTopValue, topValue);
    // And top was actually evaluated.
    assertThat(topEvaluated.get()).isTrue();
    // When leaf is changed,
    tester.set(leaf, new StringValue("crunchy"));
    tester.invalidate();
    topEvaluated.set(false);
    // And top is evaluated,
    StringValue topValue2 = (StringValue) tester.evalAndGet("top");
    // Then top's value is as expected,
    assertEquals(fixedTopValue, topValue2);
    // And top was actually evaluated.
    assertThat(topEvaluated.get()).isTrue();
    // When leaf is invalidated but not actually changed,
    tester.getOrCreate(leaf, /*markAsModified=*/
    true);
    tester.invalidate();
    topEvaluated.set(false);
    // And top is evaluated,
    StringValue topValue3 = (StringValue) tester.evalAndGet("top");
    // Then top's value is as expected,
    assertEquals(fixedTopValue, topValue3);
    // And top was *not* actually evaluated, because change pruning cut off evaluation.
    assertThat(topEvaluated.get()).isFalse();
}
Also used : AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) 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 8 with Environment

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

the class MemoizingEvaluatorTest method shutDownBuildOnCachedError_Verified.

/**
   * Test that the invalidated parent of a cached but invalidated error doesn't get marked clean.
   * First build the parent -- it will contain an error. Then invalidate the error via a dependency
   * (so it will not actually change) and then build the parent and another node that depends on the
   * error. The other node will wait to throw until the parent is signaled that all of its
   * dependencies are done, or until it is interrupted. If it throws, the parent will be
   * VERIFIED_CLEAN but not done, which is not a valid state once evaluation shuts down. The
   * evaluator avoids this situation by throwing when the error is encountered, even though the
   * error isn't evaluated or requested by an evaluating node.
   */
@Test
public void shutDownBuildOnCachedError_Verified() 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);
        }
    });
    // errorKey will be invalidated due to its dependence on invalidatedKey, but later revalidated
    // since invalidatedKey re-evaluates to the same value on a subsequent build.
    SkyKey errorKey = GraphTester.toSkyKey("error");
    SkyKey invalidatedKey = GraphTester.toSkyKey("invalidated-leaf");
    SkyKey changedKey = GraphTester.toSkyKey("changed-leaf");
    tester.set(invalidatedKey, new StringValue("invalidated-leaf-value"));
    tester.set(changedKey, new StringValue("changed-leaf-value"));
    // Names are alphabetized in reverse deps of errorKey.
    final SkyKey cachedParentKey = GraphTester.toSkyKey("A-cached-parent");
    final SkyKey uncachedParentKey = GraphTester.toSkyKey("B-uncached-parent");
    tester.getOrCreate(errorKey).addDependency(invalidatedKey).setHasError(true);
    tester.getOrCreate(cachedParentKey).addDependency(errorKey).setComputedValue(CONCATENATE);
    tester.getOrCreate(uncachedParentKey).addDependency(changedKey).addDependency(errorKey).setComputedValue(CONCATENATE);
    // We only want to force a particular order of operations at some points during evaluation. In
    // particular, we don't want to force anything during error bubbling.
    final AtomicBoolean synchronizeThreads = new AtomicBoolean(false);
    final CountDownLatch shutdownAwaiterStarted = new CountDownLatch(1);
    injectGraphListenerForTesting(new Listener() {

        private final CountDownLatch cachedSignaled = new CountDownLatch(1);

        @Override
        public void accept(SkyKey key, EventType type, Order order, Object context) {
            if (!synchronizeThreads.get() || order != Order.BEFORE || type != EventType.SIGNAL) {
                return;
            }
            TrackingAwaiter.INSTANCE.awaitLatchAndTrackExceptions(shutdownAwaiterStarted, "shutdown awaiter not started");
            if (key.equals(uncachedParentKey)) {
                // we wait until the cached parent is signaled too.
                try {
                    assertTrue(cachedSignaled.await(TestUtils.WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS));
                } catch (InterruptedException e) {
                    // Before the relevant bug was fixed, this code was not interrupted, and the
                    // uncached parent got to build, yielding an inconsistent state at a later point
                    // during evaluation. With the bugfix, the cached parent is never signaled
                    // before the evaluator shuts down, and so the above code is interrupted.
                    Thread.currentThread().interrupt();
                }
            } else if (key.equals(cachedParentKey)) {
                // This branch should never be reached by a well-behaved evaluator, since when the
                // error node is reached, the evaluator should shut down. However, we don't test
                // for that behavior here because that would be brittle and we expect that such an
                // evaluator will crash hard later on in any case.
                cachedSignaled.countDown();
                try {
                    // Sleep until we're interrupted by the evaluator, so we know it's shutting
                    // down.
                    Thread.sleep(TestUtils.WAIT_TIMEOUT_MILLISECONDS);
                    Thread currentThread = Thread.currentThread();
                    throw new IllegalStateException("no interruption in time in " + key + " for " + (currentThread.isInterrupted() ? "" : "un") + "interrupted " + currentThread + " with hash " + System.identityHashCode(currentThread) + " at " + System.currentTimeMillis());
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }, /*deterministic=*/
    true);
    // Initialize graph.
    tester.eval(/*keepGoing=*/
    true, cachedParentKey, uncachedParentKey);
    tester.getOrCreate(invalidatedKey, /*markAsModified=*/
    true);
    tester.set(changedKey, new StringValue("new value"));
    tester.invalidate();
    synchronizeThreads.set(true);
    SkyKey waitForShutdownKey = GraphTester.skyKey("wait-for-shutdown");
    tester.getOrCreate(waitForShutdownKey).setBuilder(new SkyFunction() {

        @Override
        public SkyValue compute(SkyKey skyKey, Environment env) throws InterruptedException {
            shutdownAwaiterStarted.countDown();
            TrackingAwaiter.INSTANCE.awaitLatchAndTrackExceptions(((SkyFunctionEnvironment) env).getExceptionLatchForTesting(), "exception not thrown");
            // Threadpool is shutting down. Don't try to synchronize anything in the future
            // during error bubbling.
            synchronizeThreads.set(false);
            throw new InterruptedException();
        }

        @Nullable
        @Override
        public String extractTag(SkyKey skyKey) {
            return null;
        }
    });
    EvaluationResult<StringValue> result = tester.eval(/*keepGoing=*/
    false, cachedParentKey, uncachedParentKey, waitForShutdownKey);
    assertWithMessage(result.toString()).that(result.hasError()).isTrue();
    tester.getOrCreate(invalidatedKey, /*markAsModified=*/
    true);
    tester.invalidate();
    result = tester.eval(/*keepGoing=*/
    false, cachedParentKey, uncachedParentKey);
    assertWithMessage(result.toString()).that(result.hasError()).isTrue();
}
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) TestThread(com.google.devtools.build.lib.testutil.TestThread) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) 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 9 with Environment

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

the class MemoizingEvaluatorTest method sameDepInTwoGroups.

/**
   * The same dep is requested in two groups, but its value determines what the other dep in the
   * second group is. When it changes, the other dep in the second group should not be requested.
   */
@Test
public void sameDepInTwoGroups() throws Exception {
    initializeTester();
    // leaf4 should not built in the second build.
    final SkyKey leaf4 = GraphTester.toSkyKey("leaf4");
    final AtomicBoolean shouldNotBuildLeaf4 = new AtomicBoolean(false);
    injectGraphListenerForTesting(new Listener() {

        @Override
        public void accept(SkyKey key, EventType type, Order order, Object context) {
            if (shouldNotBuildLeaf4.get() && key.equals(leaf4) && type != EventType.REMOVE_REVERSE_DEP) {
                throw new IllegalStateException("leaf4 should not have been considered this build: " + type + ", " + order + ", " + context);
            }
        }
    }, /*deterministic=*/
    false);
    tester.set(leaf4, new StringValue("leaf4"));
    // Create leaf0, leaf1 and leaf2 values with values "leaf2", "leaf3", "leaf4" respectively.
    // These will be requested as one dependency group. In the second build, leaf2 will have the
    // value "leaf5".
    final List<SkyKey> leaves = new ArrayList<>();
    for (int i = 0; i <= 2; i++) {
        SkyKey leaf = GraphTester.toSkyKey("leaf" + i);
        leaves.add(leaf);
        tester.set(leaf, new StringValue("leaf" + (i + 2)));
    }
    // Create "top" value. It depends on all leaf values in two overlapping dependency groups.
    SkyKey topKey = GraphTester.toSkyKey("top");
    final SkyValue topValue = new StringValue("top");
    tester.getOrCreate(topKey).setBuilder(new NoExtractorFunction() {

        @Override
        public SkyValue compute(SkyKey skyKey, Environment env) throws SkyFunctionException, InterruptedException {
            // Request the first group, [leaf0, leaf1, leaf2].
            // In the first build, it has values ["leaf2", "leaf3", "leaf4"].
            // In the second build it has values ["leaf2", "leaf3", "leaf5"]
            Map<SkyKey, SkyValue> values = env.getValues(leaves);
            if (env.valuesMissing()) {
                return null;
            }
            // Request the second group. In the first build it's [leaf2, leaf4].
            // In the second build it's [leaf2, leaf5]
            env.getValues(ImmutableList.of(leaves.get(2), GraphTester.toSkyKey(((StringValue) values.get(leaves.get(2))).getValue())));
            if (env.valuesMissing()) {
                return null;
            }
            return topValue;
        }
    });
    // First build: assert we can evaluate "top".
    assertEquals(topValue, tester.evalAndGet(/*keepGoing=*/
    false, topKey));
    // Second build: replace "leaf4" by "leaf5" in leaf2's value. Assert leaf4 is not requested.
    final SkyKey leaf5 = GraphTester.toSkyKey("leaf5");
    tester.set(leaf5, new StringValue("leaf5"));
    tester.set(leaves.get(2), new StringValue("leaf5"));
    tester.invalidate();
    shouldNotBuildLeaf4.set(true);
    assertEquals(topValue, tester.evalAndGet(/*keepGoing=*/
    false, topKey));
}
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) ArrayList(java.util.ArrayList) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) Environment(com.google.devtools.build.skyframe.SkyFunction.Environment) NotComparableStringValue(com.google.devtools.build.skyframe.GraphTester.NotComparableStringValue) StringValue(com.google.devtools.build.skyframe.GraphTester.StringValue) Map(java.util.Map) ImmutableMap(com.google.common.collect.ImmutableMap) Test(org.junit.Test)

Example 10 with Environment

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

the class MemoizingEvaluatorTest method cycleAndSelfEdgeWithDirtyValueInSameGroup.

@Test
public void cycleAndSelfEdgeWithDirtyValueInSameGroup() throws Exception {
    makeGraphDeterministic();
    final SkyKey cycleKey1 = GraphTester.toSkyKey("ZcycleKey1");
    final SkyKey cycleKey2 = GraphTester.toSkyKey("AcycleKey2");
    tester.getOrCreate(cycleKey2).addDependency(cycleKey2).setComputedValue(CONCATENATE);
    tester.getOrCreate(cycleKey1).setBuilder(new SkyFunction() {

        @Nullable
        @Override
        public SkyValue compute(SkyKey skyKey, Environment env) throws SkyFunctionException, InterruptedException {
            // The order here is important -- 2 before 1.
            Map<SkyKey, SkyValue> result = env.getValues(ImmutableList.of(cycleKey2, cycleKey1));
            Preconditions.checkState(env.valuesMissing(), result);
            return null;
        }

        @Nullable
        @Override
        public String extractTag(SkyKey skyKey) {
            return null;
        }
    });
    // Evaluate twice to make sure nothing strange happens with invalidation the second time.
    for (int i = 0; i < 2; i++) {
        EvaluationResult<SkyValue> result = tester.eval(/*keepGoing=*/
        true, cycleKey1);
        assertEquals(null, result.get(cycleKey1));
        ErrorInfo errorInfo = result.getError(cycleKey1);
        CycleInfo cycleInfo = Iterables.getOnlyElement(errorInfo.getCycleInfo());
        if (cyclesDetected()) {
            assertThat(cycleInfo.getCycle()).containsExactly(cycleKey1).inOrder();
            assertThat(cycleInfo.getPathToCycle()).isEmpty();
        }
    }
}
Also used : ErrorInfoSubjectFactory.assertThatErrorInfo(com.google.devtools.build.skyframe.ErrorInfoSubjectFactory.assertThatErrorInfo) Environment(com.google.devtools.build.skyframe.SkyFunction.Environment) Map(java.util.Map) ImmutableMap(com.google.common.collect.ImmutableMap) Nullable(javax.annotation.Nullable) 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