Janak Ramakrishnan | cb8a97d | 2017-03-23 20:50:08 +0000 | [diff] [blame] | 1 | // Copyright 2014 The Bazel Authors. 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.skyframe; |
| 15 | |
| 16 | import com.google.common.base.MoreObjects; |
tomlu | a155b53 | 2017-11-08 20:12:47 +0100 | [diff] [blame] | 17 | import com.google.common.base.Preconditions; |
Janak Ramakrishnan | cb8a97d | 2017-03-23 20:50:08 +0000 | [diff] [blame] | 18 | import com.google.common.collect.ImmutableList; |
| 19 | import com.google.common.collect.ImmutableSet; |
| 20 | import com.google.common.collect.Iterables; |
| 21 | import com.google.common.collect.Lists; |
philwo | 3bcb9f6 | 2017-09-06 12:52:21 +0200 | [diff] [blame] | 22 | import com.google.devtools.build.lib.collect.compacthashset.CompactHashSet; |
Janak Ramakrishnan | cb8a97d | 2017-03-23 20:50:08 +0000 | [diff] [blame] | 23 | import com.google.devtools.build.skyframe.KeyToConsolidate.Op; |
| 24 | import com.google.devtools.build.skyframe.KeyToConsolidate.OpToStoreBare; |
| 25 | import java.util.ArrayList; |
| 26 | import java.util.Collection; |
| 27 | import java.util.HashSet; |
| 28 | import java.util.List; |
| 29 | import java.util.Set; |
| 30 | |
| 31 | /** |
| 32 | * A utility class that allows us to keep the reverse dependencies as an array list instead of a |
| 33 | * set. This is more memory-efficient. At the same time it allows us to group the removals and |
| 34 | * uniqueness checks so that it also performs well. |
| 35 | * |
| 36 | * <p>We could simply make {@link InMemoryNodeEntry} extend this class, but we would be less |
| 37 | * memory-efficient since object memory alignment does not cross classes (you would have two memory |
| 38 | * alignments, one for the base class and one for the extended one). We could also merge this |
| 39 | * functionality directly into {@link InMemoryNodeEntry} at the cost of increasing its size and |
| 40 | * complexity even more. |
| 41 | * |
| 42 | * <p>The operations {@link #addReverseDeps}, {@link #checkReverseDep}, and {@link |
| 43 | * #removeReverseDep} here are optimized for a done entry. Done entries rarely have rdeps added and |
| 44 | * removed, but do have {@link Op#CHECK} operations performed frequently. As well, done node entries |
| 45 | * may never have their data forcibly consolidated, since their reverse deps will only be retrieved |
| 46 | * as a whole if they are marked dirty. Thus, we consolidate periodically. |
| 47 | * |
| 48 | * <p>{@link InMemoryNodeEntry} manages pending reverse dep operations on a marked-dirty or initally |
| 49 | * evaluating node itself, using similar logic tuned to those cases, and calls into {@link |
| 50 | * #consolidateDataAndReturnNewElements(InMemoryNodeEntry, OpToStoreBare)} when transitioning to |
| 51 | * done. |
| 52 | */ |
| 53 | abstract class ReverseDepsUtility { |
| 54 | private ReverseDepsUtility() {} |
| 55 | |
| 56 | static final int MAYBE_CHECK_THRESHOLD = 10; |
| 57 | |
| 58 | /** |
| 59 | * We can store one type of operation bare in order to save memory. For done nodes, most |
| 60 | * operations are CHECKS. |
| 61 | */ |
| 62 | private static final OpToStoreBare DEFAULT_OP_TO_STORE_BARE = OpToStoreBare.CHECK; |
| 63 | |
| 64 | private static void maybeDelayReverseDepOp( |
| 65 | InMemoryNodeEntry entry, Iterable<SkyKey> reverseDeps, Op op) { |
| 66 | List<Object> consolidations = entry.getReverseDepsDataToConsolidateForReverseDepsUtil(); |
| 67 | int currentReverseDepSize = getCurrentReverseDepSize(entry); |
| 68 | if (consolidations == null) { |
| 69 | consolidations = new ArrayList<>(currentReverseDepSize); |
| 70 | entry.setReverseDepsDataToConsolidateForReverseDepsUtil(consolidations); |
| 71 | } |
| 72 | for (SkyKey reverseDep : reverseDeps) { |
| 73 | consolidations.add(KeyToConsolidate.create(reverseDep, op, DEFAULT_OP_TO_STORE_BARE)); |
| 74 | } |
| 75 | // TODO(janakr): Should we consolidate more aggressively? This threshold can be customized. |
| 76 | if (consolidations.size() >= currentReverseDepSize) { |
| 77 | consolidateData(entry); |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | private static boolean isSingleReverseDep(InMemoryNodeEntry entry) { |
| 82 | return !(entry.getReverseDepsRawForReverseDepsUtil() instanceof List); |
| 83 | } |
| 84 | |
| 85 | /** |
| 86 | * We only check if reverse deps is small and there are no delayed data to consolidate, since then |
| 87 | * presence or absence would not be known. |
| 88 | */ |
| 89 | static void maybeCheckReverseDepNotPresent(InMemoryNodeEntry entry, SkyKey reverseDep) { |
| 90 | if (entry.getReverseDepsDataToConsolidateForReverseDepsUtil() != null) { |
| 91 | return; |
| 92 | } |
| 93 | if (isSingleReverseDep(entry)) { |
| 94 | Preconditions.checkState( |
| 95 | !entry.getReverseDepsRawForReverseDepsUtil().equals(reverseDep), |
| 96 | "Reverse dep %s already present in %s", |
| 97 | reverseDep, |
| 98 | entry); |
| 99 | return; |
| 100 | } |
| 101 | @SuppressWarnings("unchecked") |
| 102 | List<SkyKey> asList = (List<SkyKey>) entry.getReverseDepsRawForReverseDepsUtil(); |
| 103 | if (asList.size() < MAYBE_CHECK_THRESHOLD) { |
| 104 | Preconditions.checkState( |
| 105 | !asList.contains(reverseDep), |
| 106 | "Reverse dep %s already present in %s for %s", |
| 107 | reverseDep, |
| 108 | asList, |
| 109 | entry); |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | @SuppressWarnings("unchecked") // Cast to list. |
| 114 | private static int getCurrentReverseDepSize(InMemoryNodeEntry entry) { |
| 115 | return isSingleReverseDep(entry) |
| 116 | ? 1 |
| 117 | : ((List<SkyKey>) entry.getReverseDepsRawForReverseDepsUtil()).size(); |
| 118 | } |
| 119 | |
| 120 | /** |
| 121 | * We use a memory-efficient trick to keep reverseDeps memory usage low. Edges in Bazel are |
| 122 | * dominant over the number of nodes. |
| 123 | * |
| 124 | * <p>Most of the nodes have zero or one reverse dep. That is why we use immutable versions of the |
| 125 | * lists for those cases. In case of the size being > 1 we switch to an ArrayList. That is because |
| 126 | * we also have a decent number of nodes for which the reverseDeps are huge (for example almost |
| 127 | * everything depends on BuildInfo node). |
| 128 | * |
| 129 | * <p>We also optimize for the case where we have only one dependency. In that case we keep the |
| 130 | * object directly instead of a wrapper list. |
| 131 | */ |
| 132 | @SuppressWarnings("unchecked") // Cast to SkyKey and List. |
| 133 | static void addReverseDeps(InMemoryNodeEntry entry, Collection<SkyKey> newReverseDeps) { |
| 134 | if (newReverseDeps.isEmpty()) { |
| 135 | return; |
| 136 | } |
| 137 | List<Object> dataToConsolidate = entry.getReverseDepsDataToConsolidateForReverseDepsUtil(); |
| 138 | if (dataToConsolidate != null) { |
| 139 | maybeDelayReverseDepOp(entry, newReverseDeps, Op.ADD); |
| 140 | return; |
| 141 | } |
| 142 | Object reverseDeps = entry.getReverseDepsRawForReverseDepsUtil(); |
| 143 | int reverseDepsSize = isSingleReverseDep(entry) ? 1 : ((List<SkyKey>) reverseDeps).size(); |
| 144 | int newSize = reverseDepsSize + newReverseDeps.size(); |
| 145 | if (newSize == 1) { |
| 146 | entry.setSingleReverseDepForReverseDepsUtil(Iterables.getOnlyElement(newReverseDeps)); |
| 147 | } else if (reverseDepsSize == 0) { |
| 148 | entry.setReverseDepsForReverseDepsUtil(Lists.newArrayList(newReverseDeps)); |
| 149 | } else if (reverseDepsSize == 1) { |
| 150 | List<SkyKey> newList = Lists.newArrayListWithExpectedSize(newSize); |
| 151 | newList.add((SkyKey) reverseDeps); |
| 152 | newList.addAll(newReverseDeps); |
| 153 | entry.setReverseDepsForReverseDepsUtil(newList); |
| 154 | } else { |
| 155 | ((List<SkyKey>) reverseDeps).addAll(newReverseDeps); |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | static void checkReverseDep(InMemoryNodeEntry entry, SkyKey reverseDep) { |
| 160 | maybeDelayReverseDepOp(entry, ImmutableList.of(reverseDep), Op.CHECK); |
| 161 | } |
| 162 | |
| 163 | /** See {@code addReverseDeps} method. */ |
| 164 | static void removeReverseDep(InMemoryNodeEntry entry, SkyKey reverseDep) { |
| 165 | maybeDelayReverseDepOp(entry, ImmutableList.of(reverseDep), Op.REMOVE); |
| 166 | } |
| 167 | |
| 168 | static ImmutableSet<SkyKey> getReverseDeps(InMemoryNodeEntry entry) { |
| 169 | consolidateData(entry); |
| 170 | |
| 171 | // TODO(bazel-team): Unfortunately, we need to make a copy here right now to be on the safe side |
| 172 | // wrt. thread-safety. The parents of a node get modified when any of the parents is deleted, |
| 173 | // and we can't handle that right now. |
| 174 | if (isSingleReverseDep(entry)) { |
| 175 | return ImmutableSet.of((SkyKey) entry.getReverseDepsRawForReverseDepsUtil()); |
| 176 | } else { |
| 177 | @SuppressWarnings("unchecked") |
| 178 | List<SkyKey> reverseDeps = (List<SkyKey>) entry.getReverseDepsRawForReverseDepsUtil(); |
| 179 | ImmutableSet<SkyKey> set = ImmutableSet.copyOf(reverseDeps); |
| 180 | Preconditions.checkState( |
| 181 | set.size() == reverseDeps.size(), |
| 182 | "Duplicate reverse deps present in %s: %s", |
| 183 | reverseDeps, |
| 184 | entry); |
| 185 | return set; |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | static Set<SkyKey> returnNewElements(InMemoryNodeEntry entry, OpToStoreBare opToStoreBare) { |
| 190 | return consolidateDataAndReturnNewElements(entry, false, opToStoreBare); |
| 191 | } |
| 192 | |
| 193 | @SuppressWarnings("unchecked") // List and bare SkyKey casts. |
| 194 | private static Set<SkyKey> consolidateDataAndReturnNewElements( |
| 195 | InMemoryNodeEntry entry, boolean mutateObject, OpToStoreBare opToStoreBare) { |
| 196 | List<Object> dataToConsolidate = entry.getReverseDepsDataToConsolidateForReverseDepsUtil(); |
| 197 | if (dataToConsolidate == null) { |
| 198 | return ImmutableSet.of(); |
| 199 | } |
| 200 | Set<SkyKey> reverseDepsAsSet; |
| 201 | Object reverseDeps = entry.getReverseDepsRawForReverseDepsUtil(); |
| 202 | if (isSingleReverseDep(entry)) { |
| 203 | reverseDepsAsSet = CompactHashSet.create((SkyKey) reverseDeps); |
| 204 | } else { |
| 205 | List<SkyKey> reverseDepsAsList = (List<SkyKey>) reverseDeps; |
| 206 | reverseDepsAsSet = getReverseDepsSet(entry, reverseDepsAsList); |
| 207 | } |
| 208 | Set<SkyKey> newData = CompactHashSet.create(); |
| 209 | for (Object keyToConsolidate : dataToConsolidate) { |
| 210 | SkyKey key = KeyToConsolidate.key(keyToConsolidate); |
| 211 | switch (KeyToConsolidate.op(keyToConsolidate, opToStoreBare)) { |
| 212 | case CHECK: |
| 213 | Preconditions.checkState( |
| 214 | reverseDepsAsSet.contains(key), |
tomlu | 32d2edc | 2017-11-08 18:14:23 +0100 | [diff] [blame] | 215 | "Reverse dep not present: %s %s %s %s", |
Janak Ramakrishnan | cb8a97d | 2017-03-23 20:50:08 +0000 | [diff] [blame] | 216 | keyToConsolidate, |
| 217 | reverseDepsAsSet, |
Janak Ramakrishnan | cb8a97d | 2017-03-23 20:50:08 +0000 | [diff] [blame] | 218 | dataToConsolidate, |
| 219 | entry); |
| 220 | Preconditions.checkState( |
| 221 | newData.add(key), |
tomlu | 32d2edc | 2017-11-08 18:14:23 +0100 | [diff] [blame] | 222 | "Duplicate new reverse dep: %s %s %s %s", |
Janak Ramakrishnan | cb8a97d | 2017-03-23 20:50:08 +0000 | [diff] [blame] | 223 | keyToConsolidate, |
| 224 | reverseDepsAsSet, |
Janak Ramakrishnan | cb8a97d | 2017-03-23 20:50:08 +0000 | [diff] [blame] | 225 | dataToConsolidate, |
| 226 | entry); |
| 227 | break; |
| 228 | case REMOVE: |
| 229 | Preconditions.checkState( |
| 230 | reverseDepsAsSet.remove(key), |
tomlu | 32d2edc | 2017-11-08 18:14:23 +0100 | [diff] [blame] | 231 | "Reverse dep to be removed not present: %s %s %s %s", |
Janak Ramakrishnan | cb8a97d | 2017-03-23 20:50:08 +0000 | [diff] [blame] | 232 | keyToConsolidate, |
| 233 | reverseDepsAsSet, |
Janak Ramakrishnan | cb8a97d | 2017-03-23 20:50:08 +0000 | [diff] [blame] | 234 | dataToConsolidate, |
| 235 | entry); |
| 236 | Preconditions.checkState( |
| 237 | newData.remove(key), |
tomlu | 32d2edc | 2017-11-08 18:14:23 +0100 | [diff] [blame] | 238 | "Reverse dep to be removed not present: %s %s %s %s", |
Janak Ramakrishnan | cb8a97d | 2017-03-23 20:50:08 +0000 | [diff] [blame] | 239 | keyToConsolidate, |
| 240 | reverseDepsAsSet, |
Janak Ramakrishnan | cb8a97d | 2017-03-23 20:50:08 +0000 | [diff] [blame] | 241 | dataToConsolidate, |
| 242 | entry); |
| 243 | break; |
| 244 | case REMOVE_OLD: |
| 245 | Preconditions.checkState( |
| 246 | reverseDepsAsSet.remove(key), |
tomlu | 32d2edc | 2017-11-08 18:14:23 +0100 | [diff] [blame] | 247 | "Reverse dep to be removed not present: %s %s %s %s", |
Janak Ramakrishnan | cb8a97d | 2017-03-23 20:50:08 +0000 | [diff] [blame] | 248 | keyToConsolidate, |
| 249 | reverseDepsAsSet, |
Janak Ramakrishnan | cb8a97d | 2017-03-23 20:50:08 +0000 | [diff] [blame] | 250 | dataToConsolidate, |
| 251 | entry); |
| 252 | Preconditions.checkState( |
| 253 | !newData.contains(key), |
tomlu | 32d2edc | 2017-11-08 18:14:23 +0100 | [diff] [blame] | 254 | "Reverse dep shouldn't have been added to new: %s %s %s %s", |
Janak Ramakrishnan | cb8a97d | 2017-03-23 20:50:08 +0000 | [diff] [blame] | 255 | keyToConsolidate, |
| 256 | reverseDepsAsSet, |
Janak Ramakrishnan | cb8a97d | 2017-03-23 20:50:08 +0000 | [diff] [blame] | 257 | dataToConsolidate, |
| 258 | entry); |
| 259 | break; |
| 260 | case ADD: |
| 261 | Preconditions.checkState( |
| 262 | reverseDepsAsSet.add(key), |
tomlu | 32d2edc | 2017-11-08 18:14:23 +0100 | [diff] [blame] | 263 | "Duplicate reverse deps: %s %s %s %s", |
Janak Ramakrishnan | cb8a97d | 2017-03-23 20:50:08 +0000 | [diff] [blame] | 264 | keyToConsolidate, |
| 265 | reverseDeps, |
Janak Ramakrishnan | cb8a97d | 2017-03-23 20:50:08 +0000 | [diff] [blame] | 266 | dataToConsolidate, |
| 267 | entry); |
| 268 | Preconditions.checkState( |
| 269 | newData.add(key), |
tomlu | 32d2edc | 2017-11-08 18:14:23 +0100 | [diff] [blame] | 270 | "Duplicate new reverse deps: %s %s %s %s", |
Janak Ramakrishnan | cb8a97d | 2017-03-23 20:50:08 +0000 | [diff] [blame] | 271 | keyToConsolidate, |
| 272 | reverseDeps, |
Janak Ramakrishnan | cb8a97d | 2017-03-23 20:50:08 +0000 | [diff] [blame] | 273 | dataToConsolidate, |
| 274 | entry); |
| 275 | break; |
| 276 | default: |
| 277 | throw new IllegalStateException( |
| 278 | keyToConsolidate + ", " + reverseDepsAsSet + ", " + dataToConsolidate + ", " + entry); |
| 279 | } |
| 280 | } |
| 281 | if (mutateObject) { |
| 282 | entry.setReverseDepsDataToConsolidateForReverseDepsUtil(null); |
| 283 | writeReverseDepsSet(entry, reverseDepsAsSet); |
| 284 | } |
| 285 | return newData; |
| 286 | } |
| 287 | |
| 288 | static Set<SkyKey> consolidateDataAndReturnNewElements( |
| 289 | InMemoryNodeEntry entry, OpToStoreBare opToStoreBare) { |
| 290 | return consolidateDataAndReturnNewElements(entry, true, opToStoreBare); |
| 291 | } |
| 292 | |
| 293 | @SuppressWarnings("unchecked") // Casts to SkyKey and List. |
| 294 | private static void consolidateData(InMemoryNodeEntry entry) { |
| 295 | List<Object> dataToConsolidate = entry.getReverseDepsDataToConsolidateForReverseDepsUtil(); |
| 296 | if (dataToConsolidate == null) { |
| 297 | return; |
| 298 | } |
| 299 | entry.setReverseDepsDataToConsolidateForReverseDepsUtil(null); |
| 300 | Object reverseDeps = entry.getReverseDepsRawForReverseDepsUtil(); |
| 301 | if (isSingleReverseDep(entry)) { |
| 302 | Preconditions.checkState( |
| 303 | dataToConsolidate.size() == 1, |
| 304 | "dataToConsolidate not size 1 even though only one rdep: %s %s %s", |
| 305 | dataToConsolidate, |
| 306 | reverseDeps, |
| 307 | entry); |
| 308 | Object keyToConsolidate = Iterables.getOnlyElement(dataToConsolidate); |
| 309 | SkyKey key = KeyToConsolidate.key(keyToConsolidate); |
| 310 | switch (KeyToConsolidate.op(keyToConsolidate, DEFAULT_OP_TO_STORE_BARE)) { |
| 311 | case REMOVE: |
| 312 | entry.setReverseDepsForReverseDepsUtil(ImmutableList.<SkyKey>of()); |
| 313 | // Fall through to check. |
| 314 | case CHECK: |
| 315 | Preconditions.checkState( |
| 316 | key.equals(reverseDeps), "%s %s %s", keyToConsolidate, reverseDeps, entry); |
| 317 | break; |
| 318 | case ADD: |
| 319 | throw new IllegalStateException( |
| 320 | "Shouldn't delay add if only one element: " |
| 321 | + keyToConsolidate |
| 322 | + ", " |
| 323 | + reverseDeps |
| 324 | + ", " |
| 325 | + entry); |
| 326 | case REMOVE_OLD: |
| 327 | throw new IllegalStateException( |
| 328 | "Shouldn't be removing old deps if node already done: " |
| 329 | + keyToConsolidate |
| 330 | + ", " |
| 331 | + reverseDeps |
| 332 | + ", " |
| 333 | + entry); |
| 334 | default: |
| 335 | throw new IllegalStateException(keyToConsolidate + ", " + reverseDeps + ", " + entry); |
| 336 | } |
| 337 | return; |
| 338 | } |
| 339 | List<SkyKey> reverseDepsAsList = (List<SkyKey>) reverseDeps; |
| 340 | Set<SkyKey> reverseDepsAsSet = getReverseDepsSet(entry, reverseDepsAsList); |
| 341 | |
| 342 | for (Object keyToConsolidate : dataToConsolidate) { |
| 343 | SkyKey key = KeyToConsolidate.key(keyToConsolidate); |
| 344 | switch (KeyToConsolidate.op(keyToConsolidate, DEFAULT_OP_TO_STORE_BARE)) { |
| 345 | case CHECK: |
| 346 | Preconditions.checkState( |
| 347 | reverseDepsAsSet.contains(key), |
| 348 | "%s %s %s %s", |
| 349 | keyToConsolidate, |
| 350 | reverseDepsAsSet, |
| 351 | dataToConsolidate, |
| 352 | entry); |
| 353 | break; |
| 354 | case REMOVE: |
| 355 | Preconditions.checkState( |
| 356 | reverseDepsAsSet.remove(key), |
| 357 | "%s %s %s %s", |
| 358 | keyToConsolidate, |
| 359 | reverseDeps, |
| 360 | dataToConsolidate, |
| 361 | entry); |
| 362 | break; |
| 363 | case ADD: |
| 364 | Preconditions.checkState( |
| 365 | reverseDepsAsSet.add(key), |
| 366 | "%s %s %s %s", |
| 367 | keyToConsolidate, |
| 368 | reverseDeps, |
| 369 | dataToConsolidate, |
| 370 | entry); |
| 371 | break; |
| 372 | case REMOVE_OLD: |
| 373 | throw new IllegalStateException( |
| 374 | "Shouldn't be removing old deps if node already done: " |
| 375 | + keyToConsolidate |
| 376 | + ", " |
| 377 | + reverseDeps |
| 378 | + ", " |
| 379 | + dataToConsolidate |
| 380 | + ", " |
| 381 | + entry); |
| 382 | default: |
| 383 | throw new IllegalStateException( |
| 384 | keyToConsolidate + ", " + reverseDepsAsSet + ", " + dataToConsolidate + ", " + entry); |
| 385 | } |
| 386 | } |
| 387 | writeReverseDepsSet(entry, reverseDepsAsSet); |
| 388 | } |
| 389 | |
| 390 | private static void writeReverseDepsSet(InMemoryNodeEntry entry, Set<SkyKey> reverseDepsAsSet) { |
| 391 | if (reverseDepsAsSet.isEmpty()) { |
| 392 | entry.setReverseDepsForReverseDepsUtil(ImmutableList.<SkyKey>of()); |
| 393 | } else if (reverseDepsAsSet.size() == 1) { |
| 394 | entry.setSingleReverseDepForReverseDepsUtil(Iterables.getOnlyElement(reverseDepsAsSet)); |
| 395 | } else { |
| 396 | entry.setReverseDepsForReverseDepsUtil(new ArrayList<>(reverseDepsAsSet)); |
| 397 | } |
| 398 | } |
| 399 | |
| 400 | private static Set<SkyKey> getReverseDepsSet( |
| 401 | InMemoryNodeEntry entry, List<SkyKey> reverseDepsAsList) { |
| 402 | Set<SkyKey> reverseDepsAsSet = CompactHashSet.create(reverseDepsAsList); |
| 403 | |
| 404 | if (reverseDepsAsSet.size() != reverseDepsAsList.size()) { |
| 405 | // We're about to crash. Try to print an informative error message. |
| 406 | Set<SkyKey> seen = new HashSet<>(); |
| 407 | List<SkyKey> duplicates = new ArrayList<>(); |
| 408 | for (SkyKey key : reverseDepsAsList) { |
| 409 | if (!seen.add(key)) { |
| 410 | duplicates.add(key); |
| 411 | } |
| 412 | } |
| 413 | throw new IllegalStateException( |
| 414 | (reverseDepsAsList.size() - reverseDepsAsSet.size()) |
| 415 | + " duplicates: " |
| 416 | + duplicates |
| 417 | + " for " |
| 418 | + entry); |
| 419 | } |
| 420 | return reverseDepsAsSet; |
| 421 | } |
| 422 | |
| 423 | static String toString(InMemoryNodeEntry entry) { |
| 424 | return MoreObjects.toStringHelper("ReverseDeps") |
| 425 | .add("reverseDeps", entry.getReverseDepsRawForReverseDepsUtil()) |
| 426 | .add("singleReverseDep", isSingleReverseDep(entry)) |
| 427 | .add("dataToConsolidate", entry.getReverseDepsDataToConsolidateForReverseDepsUtil()) |
| 428 | .toString(); |
| 429 | } |
| 430 | } |