public WaitReason canStart()

in server/src/jetbrains/buildServer/sharedResources/server/SharedResourcesStartBuildPrecondition.java [100:203]


  public WaitReason canStart(@NotNull QueuedBuildInfo queuedBuildInfo,
                             @NotNull Map<QueuedBuildInfo, BuildAgent> canBeStarted,
                             @NotNull BuildDistributorInput buildDistributorInput,
                             boolean isEmulationMode) {
    final DistributionDataAccessor accessor = new DistributionDataAccessor(buildDistributorInput);
    Supplier<Map<Resource, TakenLock>> takenLocksSupplier = new Lazy<Map<Resource, TakenLock>>() {
      @Override
      protected Map<Resource, TakenLock> createValue() {
        return myTakenLocks.collectTakenLocks(myRunningBuildsManager.getRunningBuildsEx(), canBeStarted.keySet());
      }
    };

    CachingProjectResourcesMap resourcesMap = new CachingProjectResourcesMap(myResources);

    // get or create our collection of resources
    WaitReason reason = null;
    final BuildPromotionEx myPromotion = (BuildPromotionEx)queuedBuildInfo.getBuildPromotionInfo();

    // we're preserving the values of distributed builds only, if our filter allowed some previous build
    // to start and reserved a value for it, there can be another filter for the same build which actually
    // prevented it from starting; by doing this cleanup we're removing reserved values of builds which could not start
    final ReservedValuesProvider reservedValuesProvider = accessor.getReservedValuesProvider();
    reservedValuesProvider.cleanupValuesReservedByObsoleteBuilds(() -> canBeStarted.keySet().stream()
                                                                                   .map(QueuedBuildInfo::getBuildPromotionInfo)
                                                                                   .map(BuildPromotionInfo::getId)
                                                                                   .collect(Collectors.toSet()));

    if (TeamCityProperties.getBooleanOrTrue(SharedResourcesPluginConstants.RESOURCES_IN_CHAINS_ENABLED) && myPromotion.isPartOfBuildChain()) {
      LOG.debug("Queued build is part of build chain");
      final List<BuildPromotionEx> depPromos = myPromotion.getDependentCompositePromotions();
      if (depPromos.isEmpty()) {
        LOG.debug("Queued build does not have dependent composite promotions");
        reason = processSingleBuild(myPromotion, accessor, takenLocksSupplier, myPromotion, isEmulationMode);
      } else {
        LOG.debug("Queued build does have " + depPromos.size() + " dependent composite " + StringUtil.pluralize("promotion", depPromos.size()));
        // contains resources and locks that are INSIDE the build chain
        final Map<Resource, Map<BuildPromotionEx, Lock>> chainLocks = new HashMap<>(); // resource -> {promotion -> lock}
        // first - get top of the chain. Builds that are already running.
        // they have locks already taken
        depPromos.forEach(promo -> {
                   if (myLocksStorage.locksStored(promo)) {
                     final BuildTypeEx buildType = promo.getBuildType();
                     if (buildType == null) return;
                     LOG.debug("build promotion" + promo.getId() + " is running. Loading locks");
                     final Map<String, Lock> currentNodeLocks = myLocksStorage.load(promo);
                     if (!currentNodeLocks.isEmpty()) {
                       // if there are locks - resolve locks against resources according to project hierarchy of composite build
                       resolve(chainLocks, resourcesMap.getResourcesMap(promo.getBuildType().getProject()), promo, currentNodeLocks);
                     }
                   }
                 });

        // rest are queued builds.
        // make sure queued builds can start.
        // builds inside composite build chain are not affected by the locks taken in the same chain
        final List<SQueuedBuild> queued = depPromos.stream()
                                                   .map(BuildPromotion::getQueuedBuild)
                                                   .filter(Objects::nonNull)
                                                   .collect(Collectors.toList());
        for (SQueuedBuild compositeQueuedBuild : queued) {
          final BuildPromotion compositeBp = compositeQueuedBuild.getBuildPromotion();
          SBuildType compositeBuildType = compositeBp.getBuildType();
          if (compositeBuildType == null) continue;

          final Collection<SharedResourcesFeature> features = myFeatures.searchForFeatures(compositeBp);
          if (!features.isEmpty()) {
            final Map<String, Lock> locksToTake = myLocks.fromBuildFeaturesAsMap(features);
            if (!locksToTake.isEmpty()) {
              // resolve locks that build wants to take against actual resources
              reason = processBuildInChain(accessor, takenLocksSupplier,
                                           resourcesMap.getResourcesMap(compositeBuildType.getProject()),
                                           chainLocks, locksToTake, compositeBp, isEmulationMode);
              if (reason != null) {
                if (LOG.isDebugEnabled()) {
                  LOG.debug("Preventing start of the queued build [" + compositeQueuedBuild + "] with reason: [" + reason.getDescription() + "]");
                }
                break;
              }
            }
          }
        }

        // process build itself
        if (reason == null) {
          final BuildTypeEx promoBuildType = myPromotion.getBuildType();
          if (promoBuildType != null) {
            final Collection<SharedResourcesFeature> features = myFeatures.searchForFeatures(myPromotion);

            if (!features.isEmpty()) {
              reason = checkForInvalidLocks(myPromotion);
            }
            final Map<String, Lock> locksToTake = myLocks.fromBuildFeaturesAsMap(features);
            if (!locksToTake.isEmpty()) {
              reason = processBuildInChain(accessor, takenLocksSupplier, resourcesMap.getResourcesMap(promoBuildType.getProject()), chainLocks, locksToTake, myPromotion, isEmulationMode);
            }
          }
        }
      }
    } else {
      reason = processSingleBuild(myPromotion, accessor, takenLocksSupplier, myPromotion, isEmulationMode);
    }

    return reason;
  }