Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-05-15 04:21:40

0001 /**----------------------------------------------------------------------
0002   ----------------------------------------------------------------------*/
0003 #ifdef __clang__
0004 #pragma GCC diagnostic ignored "-Wc++20-extensions"
0005 #endif
0006 
0007 #include "FWCore/Framework/interface/Principal.h"
0008 
0009 #include "DataFormats/Provenance/interface/ProcessConfiguration.h"
0010 #include "DataFormats/Provenance/interface/ProductResolverIndexHelper.h"
0011 #include "DataFormats/Provenance/interface/ProductRegistry.h"
0012 #include "DataFormats/Common/interface/FunctorHandleExceptionFactory.h"
0013 #include "FWCore/Framework/interface/DelayedReader.h"
0014 #include "FWCore/Framework/interface/HistoryAppender.h"
0015 #include "FWCore/Framework/src/ProductDeletedException.h"
0016 #include "FWCore/Framework/interface/ProductPutterBase.h"
0017 #include "FWCore/Framework/interface/EDConsumerBase.h"
0018 #include "ProductResolvers.h"
0019 #include "FWCore/Utilities/interface/EDMException.h"
0020 #include "FWCore/Utilities/interface/ProductResolverIndex.h"
0021 #include "FWCore/Utilities/interface/TypeID.h"
0022 #include "FWCore/Utilities/interface/WrappedClassName.h"
0023 #include "FWCore/Utilities/interface/Likely.h"
0024 
0025 #include "FWCore/MessageLogger/interface/MessageLogger.h"
0026 
0027 #include <algorithm>
0028 #include <cstring>
0029 #include <limits>
0030 #include <sstream>
0031 #include <stdexcept>
0032 #include <typeinfo>
0033 #include <atomic>
0034 
0035 namespace edm {
0036 
0037   static ProcessHistory const s_emptyProcessHistory;
0038 
0039   static std::string appendCurrentProcessIfAlias(std::string const& processFromInputTag,
0040                                                  std::string const& currentProcess) {
0041     if (processFromInputTag == InputTag::kCurrentProcess) {
0042       std::string returnValue = processFromInputTag;
0043       returnValue += " (";
0044       returnValue += currentProcess;
0045       returnValue += ")";
0046       return returnValue;
0047     }
0048     return processFromInputTag;
0049   }
0050 
0051   static void throwProductNotFoundException(char const* where, errors::ErrorCodes error, BranchID const& bid) {
0052     throw Exception(error, "InvalidID") << "Principal::" << where << ": no product with given branch id: " << bid
0053                                         << "\n";
0054   }
0055 
0056   static std::shared_ptr<cms::Exception> makeNotFoundException(char const* where,
0057                                                                KindOfType kindOfType,
0058                                                                TypeID const& productType,
0059                                                                std::string const& label,
0060                                                                std::string const& instance,
0061                                                                std::string const& process) {
0062     std::shared_ptr<cms::Exception> exception = std::make_shared<Exception>(errors::ProductNotFound);
0063     if (kindOfType == PRODUCT_TYPE) {
0064       *exception << "Principal::" << where
0065                  << ": Found zero products matching all criteria\nLooking for type: " << productType << "\n"
0066                  << "Looking for module label: " << label << "\n"
0067                  << "Looking for productInstanceName: " << instance << "\n"
0068                  << (process.empty() ? "" : "Looking for process: ") << process << "\n";
0069     } else {
0070       *exception << "Principal::" << where
0071                  << ": Found zero products matching all criteria\nLooking for a container with elements of type: "
0072                  << productType << "\n"
0073                  << "Looking for module label: " << label << "\n"
0074                  << "Looking for productInstanceName: " << instance << "\n"
0075                  << (process.empty() ? "" : "Looking for process: ") << process << "\n";
0076     }
0077     return exception;
0078   }
0079 
0080   static void throwAmbiguousException(const char* where,
0081                                       TypeID const& productType,
0082                                       std::string const& label,
0083                                       std::string const& instance,
0084                                       std::string const& process) {
0085     cms::Exception exception("AmbiguousProduct");
0086     exception << "Principal::" << where
0087               << ": More than 1 product matches all criteria\nLooking for type: " << productType << "\n"
0088               << "Looking for module label: " << label << "\n"
0089               << "Looking for productInstanceName: " << instance << "\n"
0090               << (process.empty() ? "" : "Looking for process: ") << process << "\n"
0091               << "This can only occur with get function calls using a Handle<View> argument.\n"
0092               << "Try a get not using a View or change the instance name of one of the products";
0093     throw exception;
0094   }
0095 
0096   namespace {
0097     void failedToRegisterConsumes(KindOfType kindOfType,
0098                                   TypeID const& productType,
0099                                   std::string const& moduleLabel,
0100                                   std::string const& productInstanceName,
0101                                   std::string const& processName) {
0102       cms::Exception exception("GetByLabelWithoutRegistration");
0103       exception << "::getByLabel without corresponding call to consumes or mayConsumes for this module.\n"
0104                 << (kindOfType == PRODUCT_TYPE ? "  type: " : " type: edm::View<") << productType
0105                 << (kindOfType == PRODUCT_TYPE ? "\n  module label: " : ">\n  module label: ") << moduleLabel
0106                 << "\n  product instance name: '" << productInstanceName << "'\n  process name: '" << processName
0107                 << "'\n";
0108       throw exception;
0109     }
0110   }  // namespace
0111 
0112   //0 means unset
0113   static std::atomic<Principal::CacheIdentifier_t> s_nextIdentifier{1};
0114   static inline Principal::CacheIdentifier_t nextIdentifier() {
0115     return s_nextIdentifier.fetch_add(1, std::memory_order_acq_rel);
0116   }
0117 
0118   Principal::Principal(std::shared_ptr<ProductRegistry const> reg,
0119                        std::shared_ptr<ProductResolverIndexHelper const> productLookup,
0120                        ProcessConfiguration const& pc,
0121                        BranchType bt,
0122                        HistoryAppender* historyAppender,
0123                        bool isForPrimaryProcess)
0124       : EDProductGetter(),
0125         processHistoryPtr_(),
0126         processHistoryID_(),
0127         processHistoryIDBeforeConfig_(),
0128         processConfiguration_(&pc),
0129         productResolvers_(),
0130         preg_(reg),
0131         productLookup_(productLookup),
0132         lookupProcessOrder_(productLookup->lookupProcessNames().size(), 0),
0133         reader_(),
0134         branchType_(bt),
0135         historyAppender_(historyAppender),
0136         cacheIdentifier_(nextIdentifier()) {
0137     productResolvers_.resize(reg->getNextIndexValue(bt));
0138     //Now that these have been set, we can create the list of Branches we need.
0139     std::string const source("source");
0140     ProductRegistry::ProductList const& prodsList = reg->productList();
0141     // The constructor of an alias product holder takes as an argument the product holder for which it is an alias.
0142     // So, the non-alias product holders must be created first.
0143     // Therefore, on this first pass, skip current EDAliases.
0144     bool hasAliases = false;
0145     bool hasSwitchAliases = false;
0146     for (auto const& prod : prodsList) {
0147       BranchDescription const& bd = prod.second;
0148       if (bd.branchType() == branchType_) {
0149         if (isForPrimaryProcess or bd.processName() == pc.processName()) {
0150           if (bd.isAlias()) {
0151             hasAliases = true;
0152           } else if (bd.isSwitchAlias()) {
0153             hasSwitchAliases = true;
0154           } else {
0155             auto cbd = std::make_shared<BranchDescription const>(bd);
0156             if (bd.produced()) {
0157               if (bd.moduleLabel() == source) {
0158                 addSourceProduct(cbd);
0159               } else if (bd.onDemand()) {
0160                 assert(branchType_ == InEvent);
0161                 if (bd.isTransform()) {
0162                   addTransformProduct(cbd);
0163                 } else {
0164                   addUnscheduledProduct(cbd);
0165                 }
0166               } else {
0167                 addScheduledProduct(cbd);
0168               }
0169             } else {
0170               if (bd.onDemand()) {
0171                 addDelayedReaderInputProduct(cbd);
0172               } else {
0173                 addPutOnReadInputProduct(cbd);
0174               }
0175             }
0176           }
0177         } else {
0178           //We are in a SubProcess and this branch is from the parent
0179           auto cbd = std::make_shared<BranchDescription const>(bd);
0180           addParentProcessProduct(cbd);
0181         }
0182       }
0183     }
0184     // Now process any EDAliases
0185     if (hasAliases) {
0186       for (auto const& prod : prodsList) {
0187         BranchDescription const& bd = prod.second;
0188         if (bd.isAlias() && bd.branchType() == branchType_) {
0189           addAliasedProduct(std::make_shared<BranchDescription const>(bd));
0190         }
0191       }
0192     }
0193     // Finally process any SwitchProducer aliases
0194     if (hasSwitchAliases) {
0195       for (auto const& prod : prodsList) {
0196         BranchDescription const& bd = prod.second;
0197         if (bd.isSwitchAlias() && bd.branchType() == branchType_) {
0198           assert(branchType_ == InEvent);
0199           auto cbd = std::make_shared<BranchDescription const>(bd);
0200           // Need different implementation for SwitchProducers not
0201           // in any Path (onDemand) and for those in a Path in order
0202           // to prevent the switch-aliased-for EDProducers from
0203           // being run when the SwitchProducer is in a Path after a
0204           // failing EDFilter.
0205           if (bd.onDemand()) {
0206             addSwitchAliasProduct(cbd);
0207           } else {
0208             addSwitchProducerProduct(cbd);
0209           }
0210         }
0211       }
0212     }
0213 
0214     // Now create the ProductResolvers that search in reverse process
0215     // order and are used for queries where the process name is the
0216     // empty string
0217     std::vector<std::string> const& lookupProcessNames = productLookup_->lookupProcessNames();
0218     std::vector<ProductResolverIndex> matchingHolders(lookupProcessNames.size(), ProductResolverIndexInvalid);
0219     std::vector<bool> ambiguous(lookupProcessNames.size(), false);
0220     unsigned int beginElements = productLookup_->beginElements();
0221     std::vector<TypeID> const& sortedTypeIDs = productLookup_->sortedTypeIDs();
0222     std::vector<ProductResolverIndexHelper::Range> const& ranges = productLookup_->ranges();
0223     std::vector<ProductResolverIndexHelper::IndexAndNames> const& indexAndNames = productLookup_->indexAndNames();
0224     std::vector<char> const& processNamesCharArray = productLookup_->processNames();
0225 
0226     unsigned int numberOfMatches = 0;
0227     ProductResolverIndex lastMatchIndex = ProductResolverIndexInvalid;
0228     if (!sortedTypeIDs.empty()) {
0229       ProductResolverIndex productResolverIndex = ProductResolverIndexInvalid;
0230       for (unsigned int k = 0, kEnd = sortedTypeIDs.size(); k < kEnd; ++k) {
0231         ProductResolverIndexHelper::Range const& range = ranges.at(k);
0232         for (unsigned int i = range.begin(); i < range.end(); ++i) {
0233           ProductResolverIndexHelper::IndexAndNames const& product = indexAndNames.at(i);
0234           if (product.startInProcessNames() == 0) {
0235             if (productResolverIndex != ProductResolverIndexInvalid) {
0236               if ((numberOfMatches == 1) and (lastMatchIndex != ProductResolverIndexAmbiguous)) {
0237                 //only one choice so use a special resolver
0238                 productResolvers_.at(productResolverIndex) =
0239                     std::make_shared<SingleChoiceNoProcessProductResolver>(lastMatchIndex);
0240               } else {
0241                 bool productMadeAtEnd = false;
0242                 //Need to know if the product from this processes is added at end of transition
0243                 for (unsigned int j = 0; j < matchingHolders.size(); ++j) {
0244                   if ((not ambiguous[j]) and ProductResolverIndexInvalid != matchingHolders[j] and
0245                       productResolvers_[matchingHolders[j]]->branchDescription().availableOnlyAtEndTransition()) {
0246                     productMadeAtEnd = true;
0247                     break;
0248                   }
0249                 }
0250                 std::shared_ptr<ProductResolverBase> newHolder =
0251                     std::make_shared<NoProcessProductResolver>(matchingHolders, ambiguous, productMadeAtEnd);
0252                 productResolvers_.at(productResolverIndex) = newHolder;
0253               }
0254               matchingHolders.assign(lookupProcessNames.size(), ProductResolverIndexInvalid);
0255               ambiguous.assign(lookupProcessNames.size(), false);
0256               numberOfMatches = 0;
0257               lastMatchIndex = ProductResolverIndexInvalid;
0258             }
0259             productResolverIndex = product.index();
0260           } else {
0261             std::string process(&processNamesCharArray.at(product.startInProcessNames()));
0262             auto iter = std::find(lookupProcessNames.begin(), lookupProcessNames.end(), process);
0263             assert(iter != lookupProcessNames.end());
0264             ProductResolverIndex iMatchingIndex = product.index();
0265             lastMatchIndex = iMatchingIndex;
0266             assert(iMatchingIndex != ProductResolverIndexInvalid);
0267             ++numberOfMatches;
0268             if (iMatchingIndex == ProductResolverIndexAmbiguous) {
0269               assert(k >= beginElements);
0270               ambiguous.at(iter - lookupProcessNames.begin()) = true;
0271             } else {
0272               matchingHolders.at(iter - lookupProcessNames.begin()) = iMatchingIndex;
0273             }
0274           }
0275         }
0276       }
0277       //Need to know if the product from this processes is added at end of transition
0278       if ((numberOfMatches == 1) and (lastMatchIndex != ProductResolverIndexAmbiguous)) {
0279         //only one choice so use a special resolver
0280         productResolvers_.at(productResolverIndex) =
0281             std::make_shared<SingleChoiceNoProcessProductResolver>(lastMatchIndex);
0282       } else {
0283         bool productMadeAtEnd = false;
0284         for (unsigned int i = 0; i < matchingHolders.size(); ++i) {
0285           if ((not ambiguous[i]) and ProductResolverIndexInvalid != matchingHolders[i] and
0286               productResolvers_[matchingHolders[i]]->branchDescription().availableOnlyAtEndTransition()) {
0287             productMadeAtEnd = true;
0288             break;
0289           }
0290         }
0291         std::shared_ptr<ProductResolverBase> newHolder =
0292             std::make_shared<NoProcessProductResolver>(matchingHolders, ambiguous, productMadeAtEnd);
0293         productResolvers_.at(productResolverIndex) = newHolder;
0294       }
0295     }
0296   }
0297 
0298   Principal::~Principal() {}
0299 
0300   // Number of products in the Principal.
0301   // For products in an input file and not yet read in due to delayed read,
0302   // this routine assumes a real product is there.
0303   size_t Principal::size() const {
0304     size_t size = 0U;
0305     for (auto const& prod : *this) {
0306       if (prod->singleProduct() &&  // Not a NoProcessProductResolver
0307           !prod->productUnavailable() && !prod->unscheduledWasNotRun() && !prod->branchDescription().dropped()) {
0308         ++size;
0309       }
0310     }
0311     return size;
0312   }
0313 
0314   // adjust provenance for input products after new input file has been merged
0315   bool Principal::adjustToNewProductRegistry(ProductRegistry const& reg) {
0316     ProductRegistry::ProductList const& prodsList = reg.productList();
0317     for (auto const& prod : prodsList) {
0318       BranchDescription const& bd = prod.second;
0319       if (!bd.produced() && (bd.branchType() == branchType_)) {
0320         auto cbd = std::make_shared<BranchDescription const>(bd);
0321         auto phb = getExistingProduct(cbd->branchID());
0322         if (phb == nullptr || phb->branchDescription().branchName() != cbd->branchName()) {
0323           return false;
0324         }
0325         phb->resetBranchDescription(cbd);
0326       }
0327     }
0328     return true;
0329   }
0330 
0331   void Principal::addScheduledProduct(std::shared_ptr<BranchDescription const> bd) {
0332     auto phb = std::make_unique<PuttableProductResolver>(std::move(bd));
0333     addProductOrThrow(std::move(phb));
0334   }
0335 
0336   void Principal::addSourceProduct(std::shared_ptr<BranchDescription const> bd) {
0337     auto phb = std::make_unique<PuttableProductResolver>(std::move(bd));
0338     addProductOrThrow(std::move(phb));
0339   }
0340 
0341   void Principal::addDelayedReaderInputProduct(std::shared_ptr<BranchDescription const> bd) {
0342     addProductOrThrow(std::make_unique<DelayedReaderInputProductResolver>(std::move(bd)));
0343   }
0344 
0345   void Principal::addPutOnReadInputProduct(std::shared_ptr<BranchDescription const> bd) {
0346     addProductOrThrow(std::make_unique<PutOnReadInputProductResolver>(std::move(bd)));
0347   }
0348 
0349   void Principal::addUnscheduledProduct(std::shared_ptr<BranchDescription const> bd) {
0350     addProductOrThrow(std::make_unique<UnscheduledProductResolver>(std::move(bd)));
0351   }
0352 
0353   void Principal::addTransformProduct(std::shared_ptr<BranchDescription const> bd) {
0354     addProductOrThrow(std::make_unique<TransformingProductResolver>(std::move(bd)));
0355   }
0356 
0357   void Principal::addAliasedProduct(std::shared_ptr<BranchDescription const> bd) {
0358     ProductResolverIndex index = preg_->indexFrom(bd->originalBranchID());
0359     assert(index != ProductResolverIndexInvalid);
0360 
0361     addProductOrThrow(std::make_unique<AliasProductResolver>(
0362         std::move(bd), dynamic_cast<DataManagingOrAliasProductResolver&>(*productResolvers_[index])));
0363   }
0364 
0365   void Principal::addSwitchProducerProduct(std::shared_ptr<BranchDescription const> bd) {
0366     ProductResolverIndex index = preg_->indexFrom(bd->switchAliasForBranchID());
0367     assert(index != ProductResolverIndexInvalid);
0368 
0369     addProductOrThrow(std::make_unique<SwitchProducerProductResolver>(
0370         std::move(bd), dynamic_cast<DataManagingOrAliasProductResolver&>(*productResolvers_[index])));
0371   }
0372 
0373   void Principal::addSwitchAliasProduct(std::shared_ptr<BranchDescription const> bd) {
0374     ProductResolverIndex index = preg_->indexFrom(bd->switchAliasForBranchID());
0375     assert(index != ProductResolverIndexInvalid);
0376 
0377     addProductOrThrow(std::make_unique<SwitchAliasProductResolver>(
0378         std::move(bd), dynamic_cast<DataManagingOrAliasProductResolver&>(*productResolvers_[index])));
0379   }
0380 
0381   void Principal::addParentProcessProduct(std::shared_ptr<BranchDescription const> bd) {
0382     addProductOrThrow(std::make_unique<ParentProcessProductResolver>(std::move(bd)));
0383   }
0384 
0385   // "Zero" the principal so it can be reused for another Event.
0386   void Principal::clearPrincipal() {
0387     //We do not clear the product history information
0388     // because it rarely changes and recalculating takes
0389     // time.
0390     reader_ = nullptr;
0391     for (auto& prod : *this) {
0392       prod->resetProductData();
0393     }
0394   }
0395 
0396   void Principal::deleteProduct(BranchID const& id) const {
0397     auto phb = getExistingProduct(id);
0398     assert(nullptr != phb);
0399     phb->unsafe_deleteProduct();
0400   }
0401 
0402   void Principal::setupUnscheduled(UnscheduledConfigurator const& iConfigure) {
0403     applyToResolvers([&iConfigure](ProductResolverBase* iResolver) { iResolver->setupUnscheduled(iConfigure); });
0404   }
0405 
0406   void Principal::fillPrincipal(DelayedReader* reader) {
0407     //increment identifier here since clearPrincipal isn't called for Run/Lumi
0408     cacheIdentifier_ = nextIdentifier();
0409     if (reader) {
0410       reader_ = reader;
0411     }
0412   }
0413 
0414   // Set the principal for the Event, Lumi, or Run.
0415   void Principal::fillPrincipal(ProcessHistoryID const& hist,
0416                                 ProcessHistory const* processHistory,
0417                                 DelayedReader* reader) {
0418     fillPrincipal(reader);
0419 
0420     if (historyAppender_ && productRegistry().anyProductProduced()) {
0421       if ((not processHistoryPtr_) || (processHistoryIDBeforeConfig_ != hist)) {
0422         processHistoryPtr_ = historyAppender_->appendToProcessHistory(hist, processHistory, *processConfiguration_);
0423         processHistoryID_ = processHistoryPtr_->id();
0424         processHistoryIDBeforeConfig_ = hist;
0425       }
0426     } else {
0427       std::shared_ptr<ProcessHistory const> inputProcessHistory;
0428       if ((not processHistoryPtr_) || (processHistoryIDBeforeConfig_ != hist)) {
0429         if (hist.isValid()) {
0430           //does not own the pointer
0431           auto noDel = [](void const*) {};
0432           inputProcessHistory = std::shared_ptr<ProcessHistory const>(processHistory, noDel);
0433           if (inputProcessHistory.get() == nullptr) {
0434             throw Exception(errors::LogicError) << "Principal::fillPrincipal\n"
0435                                                 << "Input ProcessHistory not found in registry\n"
0436                                                 << "Contact a Framework developer\n";
0437           }
0438         } else {
0439           //Since this is static we don't want it deleted
0440           inputProcessHistory = std::shared_ptr<ProcessHistory const>(&s_emptyProcessHistory, [](void const*) {});
0441           //no need to do any ordering since it is empty
0442           orderProcessHistoryID_ = hist;
0443         }
0444         processHistoryID_ = hist;
0445         processHistoryPtr_ = inputProcessHistory;
0446         processHistoryIDBeforeConfig_ = hist;
0447       }
0448     }
0449 
0450     if (orderProcessHistoryID_ != processHistoryID_) {
0451       std::vector<std::string> const& lookupProcessNames = productLookup_->lookupProcessNames();
0452       lookupProcessOrder_.assign(lookupProcessNames.size(), 0);
0453       unsigned int k = 0;
0454 
0455       // We loop over processes in reverse order of the ProcessHistory.
0456       // If any entries in the product lookup tables are associated with
0457       // the process we add it to the vector of processes in the order
0458       // the lookup should be performed. There is one exception though,
0459       // We start with the current process even if it is not in the ProcessHistory.
0460       // The current process might be needed but not be in the process
0461       // history if all the products produced in the current process are
0462       // transient.
0463       {
0464         auto nameIterCurrentProcess =
0465             std::find(lookupProcessNames.begin(), lookupProcessNames.end(), processConfiguration_->processName());
0466         if (nameIterCurrentProcess != lookupProcessNames.end()) {
0467           lookupProcessOrder_.at(k) = nameIterCurrentProcess - lookupProcessNames.begin();
0468           ++k;
0469         }
0470       }
0471 
0472       // We just looked for the current process so skip it if
0473       // it is in the ProcessHistory.
0474       auto iter = processHistoryPtr_->rbegin();
0475       if (iter->processName() == processConfiguration_->processName()) {
0476         ++iter;
0477       }
0478 
0479       for (auto iEnd = processHistoryPtr_->rend(); iter != iEnd; ++iter) {
0480         auto nameIter = std::find(lookupProcessNames.begin(), lookupProcessNames.end(), iter->processName());
0481         if (nameIter == lookupProcessNames.end()) {
0482           continue;
0483         }
0484         lookupProcessOrder_.at(k) = nameIter - lookupProcessNames.begin();
0485         ++k;
0486       }
0487       orderProcessHistoryID_ = processHistoryID_;
0488     }
0489   }
0490 
0491   // Set the principal for the ProcessBlock
0492   void Principal::fillPrincipal(std::string const& processNameOfBlock, DelayedReader* reader) {
0493     fillPrincipal(reader);
0494 
0495     std::vector<std::string> const& lookupProcessNames = productLookup_->lookupProcessNames();
0496     lookupProcessOrder_.assign(lookupProcessNames.size(), 0);
0497     if (!lookupProcessOrder_.empty()) {
0498       auto iter = std::find(lookupProcessNames.begin(), lookupProcessNames.end(), processNameOfBlock);
0499       if (iter != lookupProcessNames.end()) {
0500         lookupProcessOrder_[0] = iter - lookupProcessNames.begin();
0501       }
0502     }
0503   }
0504 
0505   ProductResolverBase* Principal::getExistingProduct(BranchID const& branchID) {
0506     return const_cast<ProductResolverBase*>(const_cast<const Principal*>(this)->getExistingProduct(branchID));
0507   }
0508 
0509   ProductResolverBase const* Principal::getExistingProduct(BranchID const& branchID) const {
0510     ProductResolverIndex index = preg_->indexFrom(branchID);
0511     assert(index != ProductResolverIndexInvalid);
0512     return productResolvers_.at(index).get();
0513   }
0514 
0515   ProductResolverBase const* Principal::getExistingProduct(ProductResolverBase const& productResolver) const {
0516     auto phb = getExistingProduct(productResolver.branchDescription().branchID());
0517     if (nullptr != phb && BranchKey(productResolver.branchDescription()) != BranchKey(phb->branchDescription())) {
0518       BranchDescription const& newProduct = phb->branchDescription();
0519       BranchDescription const& existing = productResolver.branchDescription();
0520       if (newProduct.branchName() != existing.branchName() && newProduct.branchID() == existing.branchID()) {
0521         throw cms::Exception("HashCollision")
0522             << "Principal::getExistingProduct\n"
0523             << " Branch " << newProduct.branchName() << " has same branch ID as branch " << existing.branchName()
0524             << "\n"
0525             << "Workaround: change process name or product instance name of " << newProduct.branchName() << "\n";
0526       } else {
0527         assert(nullptr == phb || BranchKey(productResolver.branchDescription()) == BranchKey(phb->branchDescription()));
0528       }
0529     }
0530     return phb;
0531   }
0532 
0533   void Principal::addProduct_(std::unique_ptr<ProductResolverBase> productResolver) {
0534     BranchDescription const& bd = productResolver->branchDescription();
0535     assert(!bd.className().empty());
0536     assert(!bd.friendlyClassName().empty());
0537     assert(!bd.moduleLabel().empty());
0538     assert(!bd.processName().empty());
0539     SharedProductPtr phb(productResolver.release());
0540 
0541     ProductResolverIndex index = preg_->indexFrom(bd.branchID());
0542     assert(index != ProductResolverIndexInvalid);
0543     productResolvers_[index] = phb;
0544   }
0545 
0546   void Principal::addProductOrThrow(std::unique_ptr<ProductResolverBase> productResolver) {
0547     ProductResolverBase const* phb = getExistingProduct(*productResolver);
0548     if (phb != nullptr) {
0549       BranchDescription const& bd = productResolver->branchDescription();
0550       throw Exception(errors::InsertFailure, "AlreadyPresent")
0551           << "addProductOrThrow: Problem found while adding product, "
0552           << "product already exists for (" << bd.friendlyClassName() << "," << bd.moduleLabel() << ","
0553           << bd.productInstanceName() << "," << bd.processName() << ")\n";
0554     }
0555     addProduct_(std::move(productResolver));
0556   }
0557 
0558   Principal::ConstProductResolverPtr Principal::getProductResolver(BranchID const& bid) const {
0559     ProductResolverIndex index = preg_->indexFrom(bid);
0560     if (index == ProductResolverIndexInvalid) {
0561       return ConstProductResolverPtr();
0562     }
0563     return getProductResolverByIndex(index);
0564   }
0565 
0566   Principal::ConstProductResolverPtr Principal::getProductResolverByIndex(
0567       ProductResolverIndex const& index) const noexcept {
0568     ConstProductResolverPtr const phb = productResolvers_[index].get();
0569     return phb;
0570   }
0571 
0572   unsigned int Principal::processBlockIndex(std::string const&) const {
0573     throw Exception(errors::LogicError) << "Principal::processBlockIndex not implemented for this type of Principal";
0574   }
0575 
0576   BasicHandle Principal::getByLabel(KindOfType kindOfType,
0577                                     TypeID const& typeID,
0578                                     InputTag const& inputTag,
0579                                     EDConsumerBase const* consumer,
0580                                     SharedResourcesAcquirer* sra,
0581                                     ModuleCallingContext const* mcc) const {
0582     // Not implemented for ProcessBlocks, it might work though, not tested
0583     // The other getByLabel function is used for ProcessBlocks by TestProcessor
0584     assert(branchType_ != InProcess);
0585 
0586     ProductData const* result = findProductByLabel(kindOfType, typeID, inputTag, consumer, sra, mcc);
0587     if (result == nullptr) {
0588       return BasicHandle(makeHandleExceptionFactory([=, this]() -> std::shared_ptr<cms::Exception> {
0589         return makeNotFoundException(
0590             "getByLabel",
0591             kindOfType,
0592             typeID,
0593             inputTag.label(),
0594             inputTag.instance(),
0595             appendCurrentProcessIfAlias(inputTag.process(), processConfiguration_->processName()));
0596       }));
0597     }
0598     return BasicHandle(result->wrapper(), &(result->provenance()));
0599   }
0600 
0601   BasicHandle Principal::getByLabel(KindOfType kindOfType,
0602                                     TypeID const& typeID,
0603                                     std::string const& label,
0604                                     std::string const& instance,
0605                                     std::string const& process,
0606                                     EDConsumerBase const* consumer,
0607                                     SharedResourcesAcquirer* sra,
0608                                     ModuleCallingContext const* mcc) const {
0609     ProductData const* result = findProductByLabel(kindOfType, typeID, label, instance, process, consumer, sra, mcc);
0610     if (result == nullptr) {
0611       return BasicHandle(makeHandleExceptionFactory([=]() -> std::shared_ptr<cms::Exception> {
0612         return makeNotFoundException("getByLabel", kindOfType, typeID, label, instance, process);
0613       }));
0614     }
0615     return BasicHandle(result->wrapper(), &(result->provenance()));
0616   }
0617 
0618   BasicHandle Principal::getByToken(KindOfType,
0619                                     TypeID const&,
0620                                     ProductResolverIndex index,
0621                                     bool skipCurrentProcess,
0622                                     bool& ambiguous,
0623                                     SharedResourcesAcquirer* sra,
0624                                     ModuleCallingContext const* mcc) const {
0625     assert(index != ProductResolverIndexInvalid);
0626     auto& productResolver = productResolvers_[index];
0627     assert(nullptr != productResolver.get());
0628     auto resolution = productResolver->resolveProduct(*this, skipCurrentProcess, sra, mcc);
0629     if (resolution.isAmbiguous()) {
0630       ambiguous = true;
0631       //The caller is looking explicitly for this case
0632       // and uses the extra data at the caller to setup the exception
0633       return BasicHandle::makeInvalid();
0634     }
0635     auto productData = resolution.data();
0636     if (productData == nullptr) {
0637       //The caller is looking explicitly for this case
0638       // and uses the extra data at the caller to setup the exception
0639       return BasicHandle::makeInvalid();
0640     }
0641     return BasicHandle(productData->wrapper(), &(productData->provenance()));
0642   }
0643 
0644   void Principal::prefetchAsync(WaitingTaskHolder task,
0645                                 ProductResolverIndex index,
0646                                 bool skipCurrentProcess,
0647                                 ServiceToken const& token,
0648                                 ModuleCallingContext const* mcc) const {
0649     auto const& productResolver = productResolvers_.at(index);
0650     assert(nullptr != productResolver.get());
0651     productResolver->prefetchAsync(task, *this, skipCurrentProcess, token, nullptr, mcc);
0652   }
0653 
0654   ProductData const* Principal::findProductByLabel(KindOfType kindOfType,
0655                                                    TypeID const& typeID,
0656                                                    InputTag const& inputTag,
0657                                                    EDConsumerBase const* consumer,
0658                                                    SharedResourcesAcquirer* sra,
0659                                                    ModuleCallingContext const* mcc) const {
0660     bool skipCurrentProcess = inputTag.willSkipCurrentProcess();
0661 
0662     ProductResolverIndex index = inputTag.indexFor(typeID, branchType(), &productRegistry());
0663 
0664     if (index == ProductResolverIndexInvalid) {
0665       char const* processName = inputTag.process().c_str();
0666       if (skipCurrentProcess) {
0667         processName = "\0";
0668       } else if (inputTag.process() == InputTag::kCurrentProcess) {
0669         processName = processConfiguration_->processName().c_str();
0670       }
0671 
0672       index =
0673           productLookup().index(kindOfType, typeID, inputTag.label().c_str(), inputTag.instance().c_str(), processName);
0674 
0675       if (index == ProductResolverIndexAmbiguous) {
0676         throwAmbiguousException("findProductByLabel",
0677                                 typeID,
0678                                 inputTag.label(),
0679                                 inputTag.instance(),
0680                                 appendCurrentProcessIfAlias(inputTag.process(), processConfiguration_->processName()));
0681       } else if (index == ProductResolverIndexInvalid) {
0682         // can occur because of missing consumes if nothing else in the process consumes the product
0683         for (auto const& item : preg_->productList()) {
0684           auto const& bd = item.second;
0685           if (bd.present() and bd.unwrappedTypeID() == typeID and bd.moduleLabel() == inputTag.label() and
0686               bd.productInstanceName() == inputTag.instance()) {
0687             bool const inCurrentProcess = bd.processName() == processConfiguration_->processName();
0688             if (inputTag.process().empty() or bd.processName() == inputTag.process() or
0689                 (skipCurrentProcess and not inCurrentProcess) or
0690                 (inputTag.process() == InputTag::kCurrentProcess and inCurrentProcess)) {
0691               failedToRegisterConsumes(
0692                   kindOfType,
0693                   typeID,
0694                   inputTag.label(),
0695                   inputTag.instance(),
0696                   appendCurrentProcessIfAlias(inputTag.process(), processConfiguration_->processName()));
0697             }
0698           }
0699         }
0700         return nullptr;
0701       }
0702       inputTag.tryToCacheIndex(index, typeID, branchType(), &productRegistry());
0703     }
0704     if (UNLIKELY(consumer and (not consumer->registeredToConsume(index, skipCurrentProcess, branchType())))) {
0705       failedToRegisterConsumes(kindOfType,
0706                                typeID,
0707                                inputTag.label(),
0708                                inputTag.instance(),
0709                                appendCurrentProcessIfAlias(inputTag.process(), processConfiguration_->processName()));
0710     }
0711 
0712     auto const& productResolver = productResolvers_[index];
0713 
0714     auto resolution = productResolver->resolveProduct(*this, skipCurrentProcess, sra, mcc);
0715     if (resolution.isAmbiguous()) {
0716       throwAmbiguousException("findProductByLabel",
0717                               typeID,
0718                               inputTag.label(),
0719                               inputTag.instance(),
0720                               appendCurrentProcessIfAlias(inputTag.process(), processConfiguration_->processName()));
0721     }
0722     return resolution.data();
0723   }
0724 
0725   ProductData const* Principal::findProductByLabel(KindOfType kindOfType,
0726                                                    TypeID const& typeID,
0727                                                    std::string const& label,
0728                                                    std::string const& instance,
0729                                                    std::string const& process,
0730                                                    EDConsumerBase const* consumer,
0731                                                    SharedResourcesAcquirer* sra,
0732                                                    ModuleCallingContext const* mcc) const {
0733     ProductResolverIndex index =
0734         productLookup().index(kindOfType, typeID, label.c_str(), instance.c_str(), process.c_str());
0735 
0736     if (index == ProductResolverIndexAmbiguous) {
0737       throwAmbiguousException("findProductByLabel", typeID, label, instance, process);
0738     } else if (index == ProductResolverIndexInvalid) {
0739       // can occur because of missing consumes if nothing else in the process consumes the product
0740       for (auto const& item : preg_->productList()) {
0741         auto const& bd = item.second;
0742         if (bd.present() and bd.unwrappedTypeID() == typeID and bd.moduleLabel() == label and
0743             bd.productInstanceName() == instance) {
0744           if (process.empty() or bd.processName() == process) {
0745             failedToRegisterConsumes(kindOfType, typeID, label, instance, process);
0746           }
0747         }
0748       }
0749       return nullptr;
0750     }
0751 
0752     if (UNLIKELY(consumer and (not consumer->registeredToConsume(index, false, branchType())))) {
0753       failedToRegisterConsumes(kindOfType, typeID, label, instance, process);
0754     }
0755 
0756     auto const& productResolver = productResolvers_[index];
0757 
0758     auto resolution = productResolver->resolveProduct(*this, false, sra, mcc);
0759     if (resolution.isAmbiguous()) {
0760       throwAmbiguousException("findProductByLabel", typeID, label, instance, process);
0761     }
0762     return resolution.data();
0763   }
0764 
0765   ProductData const* Principal::findProductByTag(TypeID const& typeID,
0766                                                  InputTag const& tag,
0767                                                  ModuleCallingContext const* mcc) const {
0768     // Not implemented for ProcessBlocks
0769     assert(branchType_ != InProcess);
0770 
0771     ProductData const* productData = findProductByLabel(PRODUCT_TYPE, typeID, tag, nullptr, nullptr, mcc);
0772     return productData;
0773   }
0774 
0775   Provenance const& Principal::getProvenance(BranchID const& bid) const {
0776     ConstProductResolverPtr const phb = getProductResolver(bid);
0777     if (phb == nullptr) {
0778       throwProductNotFoundException("getProvenance", errors::ProductNotFound, bid);
0779     }
0780 
0781     if (phb->unscheduledWasNotRun()) {
0782       throw edm::Exception(errors::UnimplementedFeature)
0783           << "Requesting provenance from unrun EDProducer. The requested branch ID was: " << bid;
0784     }
0785     return *phb->provenance();
0786   }
0787 
0788   StableProvenance const& Principal::getStableProvenance(BranchID const& bid) const {
0789     ConstProductResolverPtr const phb = getProductResolver(bid);
0790     if (phb == nullptr) {
0791       throwProductNotFoundException("getStableProvenance", errors::ProductNotFound, bid);
0792     }
0793     //NOTE: in all implementations, this never returns a nullptr
0794     return *phb->stableProvenance();
0795   }
0796 
0797   // This one is mostly for test printout purposes
0798   // No attempt to trigger on demand execution
0799   // Skips provenance when the EDProduct is not there
0800   void Principal::getAllProvenance(std::vector<Provenance const*>& provenances) const {
0801     provenances.clear();
0802     for (auto const& productResolver : *this) {
0803       if (productResolver->singleProduct() && productResolver->provenanceAvailable() &&
0804           !productResolver->branchDescription().isAnyAlias()) {
0805         // We do not attempt to get the event/lumi/run status from the provenance,
0806         // because the per event provenance may have been dropped.
0807         if (productResolver->provenance()->branchDescription().present()) {
0808           provenances.push_back(productResolver->provenance());
0809         }
0810       }
0811     }
0812   }
0813 
0814   // This one is also mostly for test printout purposes
0815   // No attempt to trigger on demand execution
0816   // Skips provenance for dropped branches.
0817   void Principal::getAllStableProvenance(std::vector<StableProvenance const*>& provenances) const {
0818     provenances.clear();
0819     for (auto const& productResolver : *this) {
0820       if (productResolver->singleProduct() && !productResolver->branchDescription().isAnyAlias()) {
0821         if (productResolver->stableProvenance()->branchDescription().present()) {
0822           provenances.push_back(productResolver->stableProvenance());
0823         }
0824       }
0825     }
0826   }
0827 
0828   void Principal::recombine(Principal& other, std::vector<BranchID> const& bids) {
0829     for (auto& prod : bids) {
0830       ProductResolverIndex index = preg_->indexFrom(prod);
0831       assert(index != ProductResolverIndexInvalid);
0832       ProductResolverIndex indexO = other.preg_->indexFrom(prod);
0833       assert(indexO != ProductResolverIndexInvalid);
0834       get_underlying_safe(productResolvers_[index]).swap(get_underlying_safe(other.productResolvers_[indexO]));
0835     }
0836     reader_->mergeReaders(other.reader());
0837   }
0838 
0839   WrapperBase const* Principal::getIt(ProductID const&) const {
0840     assert(false);
0841     return nullptr;
0842   }
0843 
0844   std::optional<std::tuple<WrapperBase const*, unsigned int>> Principal::getThinnedProduct(ProductID const&,
0845                                                                                            unsigned int) const {
0846     assert(false);
0847     return std::nullopt;
0848   }
0849 
0850   void Principal::getThinnedProducts(ProductID const&,
0851                                      std::vector<WrapperBase const*>&,
0852                                      std::vector<unsigned int>&) const {
0853     assert(false);
0854   }
0855 
0856   OptionalThinnedKey Principal::getThinnedKeyFrom(ProductID const&, unsigned int, ProductID const&) const {
0857     assert(false);
0858     return std::monostate{};
0859   }
0860 
0861   void Principal::put_(std::unique_ptr<WrapperBase> prod, ProductResolverBase const* phb) const {
0862     dynamic_cast<ProductPutterBase const*>(phb)->putProduct(std::move(prod));
0863   }
0864 
0865   void Principal::put_(BranchDescription const& bd, std::unique_ptr<WrapperBase> edp) const {
0866     if (edp.get() == nullptr) {
0867       throw edm::Exception(edm::errors::InsertFailure, "Null Pointer")
0868           << "put: Cannot put because unique_ptr to product is null."
0869           << "\n";
0870     }
0871     auto phb = getExistingProduct(bd.branchID());
0872     assert(phb);
0873     // ProductResolver assumes ownership
0874     put_(std::move(edp), phb);
0875   }
0876 
0877   void Principal::adjustIndexesAfterProductRegistryAddition() {
0878     if (preg_->getNextIndexValue(branchType_) != productResolvers_.size()) {
0879       bool changed = false;
0880       productResolvers_.resize(preg_->getNextIndexValue(branchType_));
0881       for (auto const& prod : preg_->productList()) {
0882         BranchDescription const& bd = prod.second;
0883         if (bd.branchType() == branchType_) {
0884           ProductResolverIndex index = preg_->indexFrom(bd.branchID());
0885           assert(index != ProductResolverIndexInvalid);
0886           if (!productResolvers_[index]) {
0887             // no product holder.  Must add one. The new entry must be an input product holder.
0888             assert(!bd.produced());
0889             auto cbd = std::make_shared<BranchDescription const>(bd);
0890             if (bd.onDemand()) {
0891               addDelayedReaderInputProduct(cbd);
0892             } else {
0893               addPutOnReadInputProduct(cbd);
0894             }
0895             changed = true;
0896           }
0897         }
0898       }
0899       if (changed) {
0900         changedIndexes_();
0901       }
0902     }
0903     assert(preg_->getNextIndexValue(branchType_) == productResolvers_.size());
0904   }
0905 
0906   void Principal::readAllFromSourceAndMergeImmediately(MergeableRunProductMetadata const* mergeableRunProductMetadata) {
0907     if (not reader()) {
0908       return;
0909     }
0910 
0911     for (auto& prod : *this) {
0912       prod->retrieveAndMerge(*this, mergeableRunProductMetadata);
0913     }
0914   }
0915 }  // namespace edm