Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1 | // Copyright 2014 Google Inc. 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. |
| 14 | package com.google.devtools.build.lib.skyframe; |
| 15 | |
| 16 | import com.google.common.base.Preconditions; |
| 17 | import com.google.common.base.Predicate; |
| 18 | import com.google.common.base.Supplier; |
| 19 | import com.google.common.base.Suppliers; |
| 20 | import com.google.common.base.Throwables; |
| 21 | import com.google.common.collect.ImmutableList; |
Miguel Alcon Pinto | 7cf2365 | 2015-03-10 21:27:48 +0000 | [diff] [blame] | 22 | import com.google.common.collect.Range; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 23 | import com.google.common.collect.Sets; |
| 24 | import com.google.common.util.concurrent.ThreadFactoryBuilder; |
| 25 | import com.google.devtools.build.lib.actions.Artifact; |
Eric Fellheimer | 6a9d7e5 | 2015-06-18 22:08:32 +0000 | [diff] [blame] | 26 | import com.google.devtools.build.lib.concurrent.ExecutorUtil; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 27 | import com.google.devtools.build.lib.concurrent.Sharder; |
| 28 | import com.google.devtools.build.lib.concurrent.ThrowableRecordingRunnableWrapper; |
Janak Ramakrishnan | 6ddbb6e | 2015-07-28 21:39:22 +0000 | [diff] [blame] | 29 | import com.google.devtools.build.lib.skyframe.SkyValueDirtinessChecker.DirtyResult; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 30 | import com.google.devtools.build.lib.util.LoggingUtil; |
| 31 | import com.google.devtools.build.lib.util.Pair; |
| 32 | import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor; |
| 33 | import com.google.devtools.build.lib.vfs.BatchStat; |
| 34 | import com.google.devtools.build.lib.vfs.FileStatusWithDigest; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 35 | import com.google.devtools.build.skyframe.Differencer; |
| 36 | import com.google.devtools.build.skyframe.MemoizingEvaluator; |
| 37 | import com.google.devtools.build.skyframe.SkyFunctionName; |
| 38 | import com.google.devtools.build.skyframe.SkyKey; |
| 39 | import com.google.devtools.build.skyframe.SkyValue; |
| 40 | |
| 41 | import java.io.IOException; |
| 42 | import java.util.Collection; |
| 43 | import java.util.Collections; |
| 44 | import java.util.HashMap; |
| 45 | import java.util.List; |
| 46 | import java.util.Map; |
| 47 | import java.util.Set; |
| 48 | import java.util.concurrent.ConcurrentHashMap; |
| 49 | import java.util.concurrent.ExecutorService; |
| 50 | import java.util.concurrent.Executors; |
| 51 | import java.util.concurrent.atomic.AtomicInteger; |
| 52 | import java.util.logging.Level; |
| 53 | import java.util.logging.Logger; |
| 54 | |
| 55 | import javax.annotation.Nullable; |
| 56 | |
| 57 | /** |
| 58 | * A helper class to find dirty values by accessing the filesystem directly (contrast with |
| 59 | * {@link DiffAwareness}). |
| 60 | */ |
| 61 | class FilesystemValueChecker { |
| 62 | |
| 63 | private static final int DIRTINESS_CHECK_THREADS = 50; |
| 64 | private static final Logger LOG = Logger.getLogger(FilesystemValueChecker.class.getName()); |
| 65 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 66 | private static final Predicate<SkyKey> ACTION_FILTER = |
| 67 | SkyFunctionName.functionIs(SkyFunctions.ACTION_EXECUTION); |
| 68 | |
| 69 | private final TimestampGranularityMonitor tsgm; |
Nathan Harmata | 9b38b2c | 2015-08-27 16:11:07 +0000 | [diff] [blame] | 70 | @Nullable |
Miguel Alcon Pinto | 7cf2365 | 2015-03-10 21:27:48 +0000 | [diff] [blame] | 71 | private final Range<Long> lastExecutionTimeRange; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 72 | private final Supplier<Map<SkyKey, SkyValue>> valuesSupplier; |
| 73 | private AtomicInteger modifiedOutputFilesCounter = new AtomicInteger(0); |
Miguel Alcon Pinto | 7cf2365 | 2015-03-10 21:27:48 +0000 | [diff] [blame] | 74 | private AtomicInteger modifiedOutputFilesIntraBuildCounter = new AtomicInteger(0); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 75 | |
Nathan Harmata | 2ff0a6d | 2015-08-12 21:10:56 +0000 | [diff] [blame] | 76 | FilesystemValueChecker(Supplier<Map<SkyKey, SkyValue>> valuesSupplier, |
Nathan Harmata | 9b38b2c | 2015-08-27 16:11:07 +0000 | [diff] [blame] | 77 | TimestampGranularityMonitor tsgm, @Nullable Range<Long> lastExecutionTimeRange) { |
Nathan Harmata | 2ff0a6d | 2015-08-12 21:10:56 +0000 | [diff] [blame] | 78 | this.valuesSupplier = valuesSupplier; |
| 79 | this.tsgm = tsgm; |
| 80 | this.lastExecutionTimeRange = lastExecutionTimeRange; |
| 81 | } |
| 82 | |
Miguel Alcon Pinto | 7cf2365 | 2015-03-10 21:27:48 +0000 | [diff] [blame] | 83 | FilesystemValueChecker(final MemoizingEvaluator evaluator, TimestampGranularityMonitor tsgm, |
Nathan Harmata | 9b38b2c | 2015-08-27 16:11:07 +0000 | [diff] [blame] | 84 | @Nullable Range<Long> lastExecutionTimeRange) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 85 | this.tsgm = tsgm; |
Miguel Alcon Pinto | 7cf2365 | 2015-03-10 21:27:48 +0000 | [diff] [blame] | 86 | this.lastExecutionTimeRange = lastExecutionTimeRange; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 87 | |
| 88 | // Construct the full map view of the entire graph at most once ("memoized"), lazily. If |
| 89 | // getDirtyFilesystemValues(Iterable<SkyKey>) is called on an empty Iterable, we avoid having |
| 90 | // to create the Map of value keys to values. This is useful in the case where the graph |
| 91 | // getValues() method could be slow. |
| 92 | this.valuesSupplier = Suppliers.memoize(new Supplier<Map<SkyKey, SkyValue>>() { |
| 93 | @Override |
| 94 | public Map<SkyKey, SkyValue> get() { |
| 95 | return evaluator.getValues(); |
| 96 | } |
| 97 | }); |
| 98 | } |
| 99 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 100 | /** |
Nathan Harmata | 2ff0a6d | 2015-08-12 21:10:56 +0000 | [diff] [blame] | 101 | * Returns a {@link Differencer.DiffWithDelta} containing keys from the backing graph (of the |
| 102 | * {@link MemoizingEvaluator} given at construction time) that are dirty according to the |
| 103 | * passed-in {@code dirtinessChecker}. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 104 | */ |
Nathan Harmata | 2ff0a6d | 2015-08-12 21:10:56 +0000 | [diff] [blame] | 105 | Differencer.DiffWithDelta getDirtyKeys(SkyValueDirtinessChecker dirtinessChecker) |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 106 | throws InterruptedException { |
Nathan Harmata | 2ff0a6d | 2015-08-12 21:10:56 +0000 | [diff] [blame] | 107 | return getDirtyValues(valuesSupplier.get().keySet(), dirtinessChecker, |
| 108 | /*checkMissingValues=*/false); |
| 109 | } |
| 110 | |
| 111 | /** |
| 112 | * Returns a {@link Differencer.DiffWithDelta} containing keys that are dirty according to the |
| 113 | * passed-in {@code dirtinessChecker}. |
| 114 | */ |
| 115 | Differencer.DiffWithDelta getNewAndOldValues(Iterable<SkyKey> keys, |
| 116 | SkyValueDirtinessChecker dirtinessChecker) throws InterruptedException { |
| 117 | return getDirtyValues(keys, dirtinessChecker, /*checkMissingValues=*/true); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 118 | } |
| 119 | |
| 120 | /** |
| 121 | * Return a collection of action values which have output files that are not in-sync with |
| 122 | * the on-disk file value (were modified externally). |
| 123 | */ |
Janak Ramakrishnan | 6ddbb6e | 2015-07-28 21:39:22 +0000 | [diff] [blame] | 124 | Collection<SkyKey> getDirtyActionValues(@Nullable final BatchStat batchStatter) |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 125 | throws InterruptedException { |
| 126 | // CPU-bound (usually) stat() calls, plus a fudge factor. |
| 127 | LOG.info("Accumulating dirty actions"); |
| 128 | final int numOutputJobs = Runtime.getRuntime().availableProcessors() * 4; |
| 129 | final Set<SkyKey> actionSkyKeys = |
| 130 | Sets.filter(valuesSupplier.get().keySet(), ACTION_FILTER); |
| 131 | final Sharder<Pair<SkyKey, ActionExecutionValue>> outputShards = |
| 132 | new Sharder<>(numOutputJobs, actionSkyKeys.size()); |
| 133 | |
| 134 | for (SkyKey key : actionSkyKeys) { |
| 135 | outputShards.add(Pair.of(key, (ActionExecutionValue) valuesSupplier.get().get(key))); |
| 136 | } |
| 137 | LOG.info("Sharded action values for batching"); |
| 138 | |
| 139 | ExecutorService executor = Executors.newFixedThreadPool( |
| 140 | numOutputJobs, |
| 141 | new ThreadFactoryBuilder().setNameFormat("FileSystem Output File Invalidator %d").build()); |
| 142 | |
| 143 | Collection<SkyKey> dirtyKeys = Sets.newConcurrentHashSet(); |
| 144 | ThrowableRecordingRunnableWrapper wrapper = |
| 145 | new ThrowableRecordingRunnableWrapper("FileSystemValueChecker#getDirtyActionValues"); |
| 146 | |
| 147 | modifiedOutputFilesCounter.set(0); |
Miguel Alcon Pinto | 7cf2365 | 2015-03-10 21:27:48 +0000 | [diff] [blame] | 148 | modifiedOutputFilesIntraBuildCounter.set(0); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 149 | for (List<Pair<SkyKey, ActionExecutionValue>> shard : outputShards) { |
| 150 | Runnable job = (batchStatter == null) |
| 151 | ? outputStatJob(dirtyKeys, shard) |
| 152 | : batchStatJob(dirtyKeys, shard, batchStatter); |
| 153 | executor.submit(wrapper.wrap(job)); |
| 154 | } |
| 155 | |
Eric Fellheimer | 6a9d7e5 | 2015-06-18 22:08:32 +0000 | [diff] [blame] | 156 | boolean interrupted = ExecutorUtil.interruptibleShutdown(executor); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 157 | Throwables.propagateIfPossible(wrapper.getFirstThrownError()); |
| 158 | LOG.info("Completed output file stat checks"); |
| 159 | if (interrupted) { |
| 160 | throw new InterruptedException(); |
| 161 | } |
| 162 | return dirtyKeys; |
| 163 | } |
| 164 | |
| 165 | private Runnable batchStatJob(final Collection<SkyKey> dirtyKeys, |
| 166 | final List<Pair<SkyKey, ActionExecutionValue>> shard, |
| 167 | final BatchStat batchStatter) { |
| 168 | return new Runnable() { |
| 169 | @Override |
| 170 | public void run() { |
| 171 | Map<Artifact, Pair<SkyKey, ActionExecutionValue>> artifactToKeyAndValue = new HashMap<>(); |
| 172 | for (Pair<SkyKey, ActionExecutionValue> keyAndValue : shard) { |
| 173 | ActionExecutionValue actionValue = keyAndValue.getSecond(); |
| 174 | if (actionValue == null) { |
| 175 | dirtyKeys.add(keyAndValue.getFirst()); |
| 176 | } else { |
| 177 | for (Artifact artifact : actionValue.getAllOutputArtifactData().keySet()) { |
| 178 | artifactToKeyAndValue.put(artifact, keyAndValue); |
| 179 | } |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | List<Artifact> artifacts = ImmutableList.copyOf(artifactToKeyAndValue.keySet()); |
| 184 | List<FileStatusWithDigest> stats; |
| 185 | try { |
| 186 | stats = batchStatter.batchStat(/*includeDigest=*/true, /*includeLinks=*/true, |
| 187 | Artifact.asPathFragments(artifacts)); |
| 188 | } catch (IOException e) { |
| 189 | // Batch stat did not work. Log an exception and fall back on system calls. |
| 190 | LoggingUtil.logToRemote(Level.WARNING, "Unable to process batch stat", e); |
| 191 | outputStatJob(dirtyKeys, shard).run(); |
| 192 | return; |
| 193 | } catch (InterruptedException e) { |
| 194 | // We handle interrupt in the main thread. |
| 195 | return; |
| 196 | } |
| 197 | |
| 198 | Preconditions.checkState(artifacts.size() == stats.size(), |
| 199 | "artifacts.size() == %s stats.size() == %s", artifacts.size(), stats.size()); |
| 200 | for (int i = 0; i < artifacts.size(); i++) { |
| 201 | Artifact artifact = artifacts.get(i); |
| 202 | FileStatusWithDigest stat = stats.get(i); |
| 203 | Pair<SkyKey, ActionExecutionValue> keyAndValue = artifactToKeyAndValue.get(artifact); |
| 204 | ActionExecutionValue actionValue = keyAndValue.getSecond(); |
| 205 | SkyKey key = keyAndValue.getFirst(); |
| 206 | FileValue lastKnownData = actionValue.getAllOutputArtifactData().get(artifact); |
| 207 | try { |
Janak Ramakrishnan | a5c1f96 | 2015-04-03 23:06:31 +0000 | [diff] [blame] | 208 | FileValue newData = ActionMetadataHandler.fileValueFromArtifact(artifact, stat, tsgm); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 209 | if (!newData.equals(lastKnownData)) { |
Miguel Alcon Pinto | bdfdd09 | 2015-04-10 19:04:49 +0000 | [diff] [blame] | 210 | updateIntraBuildModifiedCounter(stat != null ? stat.getLastChangeTime() : -1, |
| 211 | lastKnownData.isSymlink(), newData.isSymlink()); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 212 | modifiedOutputFilesCounter.getAndIncrement(); |
| 213 | dirtyKeys.add(key); |
| 214 | } |
| 215 | } catch (IOException e) { |
| 216 | // This is an unexpected failure getting a digest or symlink target. |
| 217 | modifiedOutputFilesCounter.getAndIncrement(); |
| 218 | dirtyKeys.add(key); |
| 219 | } |
| 220 | } |
| 221 | } |
| 222 | }; |
| 223 | } |
| 224 | |
Miguel Alcon Pinto | bdfdd09 | 2015-04-10 19:04:49 +0000 | [diff] [blame] | 225 | private void updateIntraBuildModifiedCounter(long time, boolean oldWasSymlink, |
Nathan Harmata | 7a34427 | 2015-06-05 20:52:38 +0000 | [diff] [blame] | 226 | boolean newIsSymlink) { |
Miguel Alcon Pinto | bdfdd09 | 2015-04-10 19:04:49 +0000 | [diff] [blame] | 227 | if (lastExecutionTimeRange != null |
| 228 | && lastExecutionTimeRange.contains(time) |
| 229 | && !(oldWasSymlink && newIsSymlink)) { |
Miguel Alcon Pinto | 7cf2365 | 2015-03-10 21:27:48 +0000 | [diff] [blame] | 230 | modifiedOutputFilesIntraBuildCounter.incrementAndGet(); |
| 231 | } |
| 232 | } |
| 233 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 234 | private Runnable outputStatJob(final Collection<SkyKey> dirtyKeys, |
| 235 | final List<Pair<SkyKey, ActionExecutionValue>> shard) { |
| 236 | return new Runnable() { |
| 237 | @Override |
| 238 | public void run() { |
| 239 | for (Pair<SkyKey, ActionExecutionValue> keyAndValue : shard) { |
| 240 | ActionExecutionValue value = keyAndValue.getSecond(); |
| 241 | if (value == null || actionValueIsDirtyWithDirectSystemCalls(value)) { |
| 242 | dirtyKeys.add(keyAndValue.getFirst()); |
| 243 | } |
| 244 | } |
| 245 | } |
| 246 | }; |
| 247 | } |
| 248 | |
| 249 | /** |
Miguel Alcon Pinto | 7cf2365 | 2015-03-10 21:27:48 +0000 | [diff] [blame] | 250 | * Returns the number of modified output files inside of dirty actions. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 251 | */ |
| 252 | int getNumberOfModifiedOutputFiles() { |
| 253 | return modifiedOutputFilesCounter.get(); |
| 254 | } |
| 255 | |
Janak Ramakrishnan | 6ddbb6e | 2015-07-28 21:39:22 +0000 | [diff] [blame] | 256 | /** Returns the number of modified output files that occur during the previous build. */ |
| 257 | int getNumberOfModifiedOutputFilesDuringPreviousBuild() { |
Miguel Alcon Pinto | 7cf2365 | 2015-03-10 21:27:48 +0000 | [diff] [blame] | 258 | return modifiedOutputFilesIntraBuildCounter.get(); |
| 259 | } |
| 260 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 261 | private boolean actionValueIsDirtyWithDirectSystemCalls(ActionExecutionValue actionValue) { |
| 262 | boolean isDirty = false; |
| 263 | for (Map.Entry<Artifact, FileValue> entry : |
| 264 | actionValue.getAllOutputArtifactData().entrySet()) { |
| 265 | Artifact artifact = entry.getKey(); |
| 266 | FileValue lastKnownData = entry.getValue(); |
| 267 | try { |
Janak Ramakrishnan | a5c1f96 | 2015-04-03 23:06:31 +0000 | [diff] [blame] | 268 | FileValue fileValue = ActionMetadataHandler.fileValueFromArtifact(artifact, null, tsgm); |
Miguel Alcon Pinto | 7cf2365 | 2015-03-10 21:27:48 +0000 | [diff] [blame] | 269 | if (!fileValue.equals(lastKnownData)) { |
| 270 | updateIntraBuildModifiedCounter(fileValue.exists() |
| 271 | ? fileValue.realRootedPath().asPath().getLastModifiedTime() |
Miguel Alcon Pinto | bdfdd09 | 2015-04-10 19:04:49 +0000 | [diff] [blame] | 272 | : -1, lastKnownData.isSymlink(), fileValue.isSymlink()); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 273 | modifiedOutputFilesCounter.getAndIncrement(); |
| 274 | isDirty = true; |
| 275 | } |
| 276 | } catch (IOException e) { |
| 277 | // This is an unexpected failure getting a digest or symlink target. |
| 278 | modifiedOutputFilesCounter.getAndIncrement(); |
| 279 | isDirty = true; |
| 280 | } |
| 281 | } |
| 282 | return isDirty; |
| 283 | } |
| 284 | |
Janak Ramakrishnan | 6ddbb6e | 2015-07-28 21:39:22 +0000 | [diff] [blame] | 285 | private BatchDirtyResult getDirtyValues( |
Nathan Harmata | 2ff0a6d | 2015-08-12 21:10:56 +0000 | [diff] [blame] | 286 | Iterable<SkyKey> values, final SkyValueDirtinessChecker checker, |
| 287 | final boolean checkMissingValues) throws InterruptedException { |
Janak Ramakrishnan | 6ddbb6e | 2015-07-28 21:39:22 +0000 | [diff] [blame] | 288 | ExecutorService executor = |
| 289 | Executors.newFixedThreadPool( |
| 290 | DIRTINESS_CHECK_THREADS, |
| 291 | new ThreadFactoryBuilder().setNameFormat("FileSystem Value Invalidator %d").build()); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 292 | |
| 293 | final BatchDirtyResult batchResult = new BatchDirtyResult(); |
| 294 | ThrowableRecordingRunnableWrapper wrapper = |
| 295 | new ThrowableRecordingRunnableWrapper("FilesystemValueChecker#getDirtyValues"); |
| 296 | for (final SkyKey key : values) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 297 | final SkyValue value = valuesSupplier.get().get(key); |
Janak Ramakrishnan | 6ddbb6e | 2015-07-28 21:39:22 +0000 | [diff] [blame] | 298 | executor.execute( |
| 299 | wrapper.wrap( |
| 300 | new Runnable() { |
| 301 | @Override |
| 302 | public void run() { |
Nathan Harmata | 2ff0a6d | 2015-08-12 21:10:56 +0000 | [diff] [blame] | 303 | if (value != null || checkMissingValues) { |
Janak Ramakrishnan | 6ddbb6e | 2015-07-28 21:39:22 +0000 | [diff] [blame] | 304 | DirtyResult result = checker.maybeCheck(key, value, tsgm); |
| 305 | if (result != null && result.isDirty()) { |
Nathan Harmata | 2ff0a6d | 2015-08-12 21:10:56 +0000 | [diff] [blame] | 306 | batchResult.add(key, value, result.getNewValue()); |
Janak Ramakrishnan | 6ddbb6e | 2015-07-28 21:39:22 +0000 | [diff] [blame] | 307 | } |
| 308 | } |
| 309 | } |
| 310 | })); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 311 | } |
| 312 | |
Eric Fellheimer | 6a9d7e5 | 2015-06-18 22:08:32 +0000 | [diff] [blame] | 313 | boolean interrupted = ExecutorUtil.interruptibleShutdown(executor); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 314 | Throwables.propagateIfPossible(wrapper.getFirstThrownError()); |
| 315 | if (interrupted) { |
| 316 | throw new InterruptedException(); |
| 317 | } |
| 318 | return batchResult; |
| 319 | } |
| 320 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 321 | /** |
Janak Ramakrishnan | 6ddbb6e | 2015-07-28 21:39:22 +0000 | [diff] [blame] | 322 | * Result of a batch call to {@link SkyValueDirtinessChecker#maybeCheck}. Partitions the dirty |
| 323 | * values based on whether we have a new value available for them or not. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 324 | */ |
Nathan Harmata | 2ff0a6d | 2015-08-12 21:10:56 +0000 | [diff] [blame] | 325 | private static class BatchDirtyResult implements Differencer.DiffWithDelta { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 326 | |
| 327 | private final Set<SkyKey> concurrentDirtyKeysWithoutNewValues = |
| 328 | Collections.newSetFromMap(new ConcurrentHashMap<SkyKey, Boolean>()); |
Nathan Harmata | 2ff0a6d | 2015-08-12 21:10:56 +0000 | [diff] [blame] | 329 | private final ConcurrentHashMap<SkyKey, Delta> concurrentDirtyKeysWithNewAndOldValues = |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 330 | new ConcurrentHashMap<>(); |
| 331 | |
Nathan Harmata | 2ff0a6d | 2015-08-12 21:10:56 +0000 | [diff] [blame] | 332 | private void add(SkyKey key, @Nullable SkyValue oldValue, @Nullable SkyValue newValue) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 333 | if (newValue == null) { |
| 334 | concurrentDirtyKeysWithoutNewValues.add(key); |
| 335 | } else { |
Nathan Harmata | 2ff0a6d | 2015-08-12 21:10:56 +0000 | [diff] [blame] | 336 | if (oldValue == null) { |
| 337 | concurrentDirtyKeysWithNewAndOldValues.put(key, new Delta(newValue)); |
| 338 | } else { |
| 339 | concurrentDirtyKeysWithNewAndOldValues.put(key, new Delta(oldValue, newValue)); |
| 340 | } |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 341 | } |
| 342 | } |
| 343 | |
| 344 | @Override |
Michajlo Matijkiw | 2b71efe | 2015-06-19 19:23:16 +0000 | [diff] [blame] | 345 | public Collection<SkyKey> changedKeysWithoutNewValues() { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 346 | return concurrentDirtyKeysWithoutNewValues; |
| 347 | } |
| 348 | |
| 349 | @Override |
Nathan Harmata | 2ff0a6d | 2015-08-12 21:10:56 +0000 | [diff] [blame] | 350 | public Map<SkyKey, Delta> changedKeysWithNewAndOldValues() { |
| 351 | return concurrentDirtyKeysWithNewAndOldValues; |
| 352 | } |
| 353 | |
| 354 | @Override |
| 355 | public Map<SkyKey, SkyValue> changedKeysWithNewValues() { |
| 356 | return Delta.newValues(concurrentDirtyKeysWithNewAndOldValues); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 357 | } |
| 358 | } |
| 359 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 360 | } |