return OS << "Materializing";
case SymbolState::Resolved:
return OS << "Resolved";
+ case SymbolState::Emitted:
+ return OS << "Emitted";
case SymbolState::Ready:
return OS << "Ready";
}
<< SymbolFlags << "\n";
});
- JD.notifyFailed(SymbolFlags);
+ JITDylib::FailedSymbolsWorklist Worklist;
+
+ for (auto &KV : SymbolFlags)
+ Worklist.push_back(std::make_pair(&JD, KV.first));
SymbolFlags.clear();
+
+ JD.notifyFailed(std::move(Worklist));
}
void MaterializationResponsibility::replace(
});
}
-JITDylib::AsynchronousSymbolQuerySet
-JITDylib::failSymbol(const SymbolStringPtr &Name) {
- assert(Symbols.count(Name) && "Name not in symbol table");
- assert(Symbols[Name].getFlags().hasError() &&
- "Failing symbol not in the error state");
-
- auto MII = MaterializingInfos.find(Name);
- if (MII == MaterializingInfos.end())
- return AsynchronousSymbolQuerySet();
-
- auto &MI = MII->second;
-
- // Visit all dependants.
- for (auto &KV : MI.Dependants) {
- auto &DependantJD = *KV.first;
- for (auto &DependantName : KV.second) {
- assert(DependantJD.Symbols.count(DependantName) &&
- "No symbol with DependantName in DependantJD");
- auto &DependantSymTabEntry = DependantJD.Symbols[DependantName];
- DependantSymTabEntry.setFlags(DependantSymTabEntry.getFlags() |
- JITSymbolFlags::HasError);
-
- assert(DependantJD.MaterializingInfos.count(DependantName) &&
- "Dependant symbol does not have MaterializingInfo?");
- auto &DependantMI = DependantJD.MaterializingInfos[DependantName];
-
- assert(DependantMI.UnemittedDependencies.count(this) &&
- "No unemitted dependency recorded for this JD?");
- auto UnemittedDepsI = DependantMI.UnemittedDependencies.find(this);
- assert(UnemittedDepsI != DependantMI.UnemittedDependencies.end() &&
- "No unemitted dependency on this JD");
- assert(UnemittedDepsI->second.count(Name) &&
- "No unemitted dependency on symbol Name in this JD");
-
- UnemittedDepsI->second.erase(Name);
- if (UnemittedDepsI->second.empty())
- DependantMI.UnemittedDependencies.erase(UnemittedDepsI);
- }
- }
-
- // Visit all unemitted dependencies and disconnect from them.
- for (auto &KV : MI.UnemittedDependencies) {
- auto &DependencyJD = *KV.first;
- for (auto &DependencyName : KV.second) {
- assert(DependencyJD.MaterializingInfos.count(DependencyName) &&
- "Dependency does not have MaterializingInfo");
- auto &DependencyMI = DependencyJD.MaterializingInfos[DependencyName];
- auto DependantsI = DependencyMI.Dependants.find(this);
- assert(DependantsI != DependencyMI.Dependants.end() &&
- "No dependnts entry recorded for this JD");
- assert(DependantsI->second.count(Name) &&
- "No dependants entry recorded for Name");
- DependantsI->second.erase(Name);
- if (DependantsI->second.empty())
- DependencyMI.Dependants.erase(DependantsI);
- }
- }
-
- AsynchronousSymbolQuerySet QueriesToFail;
- for (auto &Q : MI.takeAllPendingQueries())
- QueriesToFail.insert(std::move(Q));
- return QueriesToFail;
-}
-
void JITDylib::addDependencies(const SymbolStringPtr &Name,
const SymbolDependenceMap &Dependencies) {
assert(Symbols.count(Name) && "Name not in symbol table");
return;
auto &MI = MaterializingInfos[Name];
- assert(!MI.IsEmitted && "Can not add dependencies to an emitted symbol");
+ assert(Symbols[Name].getState() != SymbolState::Emitted &&
+ "Can not add dependencies to an emitted symbol");
bool DependsOnSymbolInErrorState = false;
for (auto &OtherSymbol : KV.second) {
// Check the sym entry for the dependency.
- auto SymI = OtherJITDylib.Symbols.find(OtherSymbol);
+ auto OtherSymI = OtherJITDylib.Symbols.find(OtherSymbol);
#ifndef NDEBUG
- // Assert that this symbol exists and has not been emitted already.
- assert(SymI != OtherJITDylib.Symbols.end() &&
- (SymI->second.getState() != SymbolState::Ready &&
- "Dependency on emitted symbol"));
+ // Assert that this symbol exists and has not reached the ready state
+ // already.
+ assert(OtherSymI != OtherJITDylib.Symbols.end() &&
+ (OtherSymI->second.getState() != SymbolState::Ready &&
+ "Dependency on emitted/ready symbol"));
#endif
+ auto &OtherSymEntry = OtherSymI->second;
+
// If the dependency is in an error state then note this and continue,
// we will move this symbol to the error state below.
- if (SymI->second.getFlags().hasError()) {
+ if (OtherSymEntry.getFlags().hasError()) {
DependsOnSymbolInErrorState = true;
continue;
}
"No MaterializingInfo for dependency");
auto &OtherMI = OtherJITDylib.MaterializingInfos[OtherSymbol];
- if (OtherMI.IsEmitted)
+ if (OtherSymEntry.getState() == SymbolState::Emitted)
transferEmittedNodeDependencies(MI, Name, OtherMI);
else if (&OtherJITDylib != this || OtherSymbol != Name) {
OtherMI.Dependants[this].insert(Name);
Worklist.pop_back();
auto &Name = SymI->first;
+ auto &SymEntry = SymI->second;
+
+ // Move symbol to the emitted state.
+ assert(SymEntry.getState() == SymbolState::Resolved &&
+ "Emitting from state other than Resolved");
+ SymEntry.setState(SymbolState::Emitted);
auto MII = MaterializingInfos.find(Name);
assert(MII != MaterializingInfos.end() &&
DependantJD.transferEmittedNodeDependencies(DependantMI,
DependantName, MI);
+ auto DependantSymI = DependantJD.Symbols.find(DependantName);
+ assert(DependantSymI != DependantJD.Symbols.end() &&
+ "Dependant has no entry in the Symbols table");
+ auto &DependantSymEntry = DependantSymI->second;
+
// If the dependant is emitted and this node was the last of its
// unemitted dependencies then the dependant node is now ready, so
// notify any pending queries on the dependant node.
- if (DependantMI.IsEmitted &&
+ if (DependantSymEntry.getState() == SymbolState::Emitted &&
DependantMI.UnemittedDependencies.empty()) {
assert(DependantMI.Dependants.empty() &&
"Dependants should be empty by now");
// Since this dependant is now ready, we erase its MaterializingInfo
// and update its materializing state.
- auto DependantSymI = DependantJD.Symbols.find(DependantName);
- assert(DependantSymI != DependantJD.Symbols.end() &&
- "Dependant has no entry in the Symbols table");
- DependantSymI->second.setState(SymbolState::Ready);
+ DependantSymEntry.setState(SymbolState::Ready);
for (auto &Q : DependantMI.takeQueriesMeeting(SymbolState::Ready)) {
Q->notifySymbolMetRequiredState(
}
}
}
- MI.Dependants.clear();
- MI.IsEmitted = true;
+ MI.Dependants.clear();
if (MI.UnemittedDependencies.empty()) {
SymI->second.setState(SymbolState::Ready);
for (auto &Q : MI.takeQueriesMeeting(SymbolState::Ready)) {
return Error::success();
}
-void JITDylib::notifyFailed(const SymbolFlagsMap &FailedSymbols) {
+void JITDylib::notifyFailed(FailedSymbolsWorklist Worklist) {
AsynchronousSymbolQuerySet FailedQueries;
+ auto FailedSymbolsMap = std::make_shared<SymbolDependenceMap>();
+
+ // Failing no symbols is a no-op.
+ if (Worklist.empty())
+ return;
+
+ auto &ES = Worklist.front().first->getExecutionSession();
ES.runSessionLocked([&]() {
- std::vector<const SymbolStringPtr *> MaterializerNamesToFail;
+ while (!Worklist.empty()) {
+ assert(Worklist.back().first && "Failed JITDylib can not be null");
+ auto &JD = *Worklist.back().first;
+ auto Name = std::move(Worklist.back().second);
+ Worklist.pop_back();
- for (auto &KV : FailedSymbols) {
- auto &Name = KV.first;
+ (*FailedSymbolsMap)[&JD].insert(Name);
- assert(Symbols.count(Name) && "No symbol table entry for Name");
- auto &Sym = Symbols[Name];
+ assert(JD.Symbols.count(Name) && "No symbol table entry for Name");
+ auto &Sym = JD.Symbols[Name];
// Move the symbol into the error state.
// Note that this may be redundant: The symbol might already have been
// FIXME: Come up with a sane mapping of state to
// presence-of-MaterializingInfo so that we can assert presence / absence
// here, rather than testing it.
- auto MII = MaterializingInfos.find(Name);
+ auto MII = JD.MaterializingInfos.find(Name);
- if (MII == MaterializingInfos.end())
+ if (MII == JD.MaterializingInfos.end())
continue;
auto &MI = MII->second;
- MaterializerNamesToFail.push_back(&KV.first);
// Move all dependants to the error state and disconnect from them.
for (auto &KV : MI.Dependants) {
"No MaterializingInfo for dependant");
auto &DependantMI = DependantJD.MaterializingInfos[DependantName];
- auto UnemittedDepI = DependantMI.UnemittedDependencies.find(this);
+ auto UnemittedDepI = DependantMI.UnemittedDependencies.find(&JD);
assert(UnemittedDepI != DependantMI.UnemittedDependencies.end() &&
"No UnemittedDependencies entry for this JITDylib");
assert(UnemittedDepI->second.count(Name) &&
UnemittedDepI->second.erase(Name);
if (UnemittedDepI->second.empty())
DependantMI.UnemittedDependencies.erase(UnemittedDepI);
+
+ // If this symbol is already in the emitted state then we need to
+ // take responsibility for failing its queries, so add it to the
+ // worklist.
+ if (DependantSym.getState() == SymbolState::Emitted) {
+ assert(DependantMI.Dependants.empty() &&
+ "Emitted symbol should not have dependants");
+ Worklist.push_back(std::make_pair(&DependantJD, DependantName));
+ }
}
}
+ MI.Dependants.clear();
// Disconnect from all unemitted depenencies.
for (auto &KV : MI.UnemittedDependencies) {
UnemittedDepJD.MaterializingInfos.find(UnemittedDepName);
assert(UnemittedDepMII != UnemittedDepJD.MaterializingInfos.end() &&
"Missing MII for unemitted dependency");
- assert(UnemittedDepMII->second.Dependants.count(this) &&
+ assert(UnemittedDepMII->second.Dependants.count(&JD) &&
"JD not listed as a dependant of unemitted dependency");
- assert(UnemittedDepMII->second.Dependants[this].count(Name) &&
+ assert(UnemittedDepMII->second.Dependants[&JD].count(Name) &&
"Name is not listed as a dependant of unemitted dependency");
- UnemittedDepMII->second.Dependants[this].erase(Name);
- if (UnemittedDepMII->second.Dependants[this].empty())
- UnemittedDepMII->second.Dependants.erase(this);
+ UnemittedDepMII->second.Dependants[&JD].erase(Name);
+ if (UnemittedDepMII->second.Dependants[&JD].empty())
+ UnemittedDepMII->second.Dependants.erase(&JD);
}
}
+ MI.UnemittedDependencies.clear();
// Collect queries to be failed for this MII.
- for (auto &Q : MII->second.pendingQueries())
+ for (auto &Q : MII->second.pendingQueries()) {
+ // Add the query to the list to be failed and detach it.
FailedQueries.insert(Q);
- }
-
- // Detach failed queries.
- for (auto &Q : FailedQueries)
- Q->detach();
+ Q->detach();
+ }
- // Remove the MaterializingInfos.
- while (!MaterializerNamesToFail.empty()) {
- MaterializingInfos.erase(*MaterializerNamesToFail.back());
- MaterializerNamesToFail.pop_back();
+ assert(MI.Dependants.empty() &&
+ "Can not delete MaterializingInfo with dependants still attached");
+ assert(MI.UnemittedDependencies.empty() &&
+ "Can not delete MaterializingInfo with unemitted dependencies "
+ "still attached");
+ assert(!MI.hasQueriesPending() &&
+ "Can not delete MaterializingInfo with queries pending");
+ JD.MaterializingInfos.erase(MII);
}
});
- auto FailedSymbolsMap = std::make_shared<SymbolDependenceMap>();
- for (auto &KV : FailedSymbols)
- (*FailedSymbolsMap)[this].insert(KV.first);
for (auto &Q : FailedQueries)
Q->handleFailed(make_error<FailedToMaterialize>(FailedSymbolsMap));
}
OS << " MaterializingInfos entries:\n";
for (auto &KV : MaterializingInfos) {
OS << " \"" << *KV.first << "\":\n"
- << " IsEmitted = " << (KV.second.IsEmitted ? "true" : "false")
- << "\n"
<< " " << KV.second.pendingQueries().size()
<< " pending queries: { ";
for (const auto &Q : KV.second.pendingQueries())
<< "Lookup on failed symbol should fail";
}
+TEST_F(CoreAPIsStandardTest, FailAfterMaterialization) {
+ Optional<MaterializationResponsibility> FooR;
+ Optional<MaterializationResponsibility> BarR;
+
+ // Create a MaterializationUnit for each symbol that moves the
+ // MaterializationResponsibility into one of the locals above.
+ auto FooMU = std::make_unique<SimpleMaterializationUnit>(
+ SymbolFlagsMap({{Foo, FooSym.getFlags()}}),
+ [&](MaterializationResponsibility R) { FooR.emplace(std::move(R)); });
+
+ auto BarMU = std::make_unique<SimpleMaterializationUnit>(
+ SymbolFlagsMap({{Bar, BarSym.getFlags()}}),
+ [&](MaterializationResponsibility R) { BarR.emplace(std::move(R)); });
+
+ // Define the symbols.
+ cantFail(JD.define(FooMU));
+ cantFail(JD.define(BarMU));
+
+ bool OnFooReadyRun = false;
+ auto OnFooReady = [&](Expected<SymbolMap> Result) {
+ EXPECT_THAT_EXPECTED(std::move(Result), Failed());
+ OnFooReadyRun = true;
+ };
+
+ ES.lookup(JITDylibSearchList({{&JD, false}}), {Foo}, SymbolState::Ready,
+ std::move(OnFooReady), NoDependenciesToRegister);
+
+ bool OnBarReadyRun = false;
+ auto OnBarReady = [&](Expected<SymbolMap> Result) {
+ EXPECT_THAT_EXPECTED(std::move(Result), Failed());
+ OnBarReadyRun = true;
+ };
+
+ ES.lookup(JITDylibSearchList({{&JD, false}}), {Bar}, SymbolState::Ready,
+ std::move(OnBarReady), NoDependenciesToRegister);
+
+ // Add a dependency by Foo on Bar and vice-versa.
+ FooR->addDependenciesForAll({{&JD, SymbolNameSet({Bar})}});
+ BarR->addDependenciesForAll({{&JD, SymbolNameSet({Foo})}});
+
+ // Materialize Foo.
+ EXPECT_THAT_ERROR(FooR->notifyResolved({{Foo, FooSym}}), Succeeded())
+ << "Expected resolution for \"Foo\" to succeed.";
+ EXPECT_THAT_ERROR(FooR->notifyEmitted(), Succeeded())
+ << "Expected emission for \"Foo\" to succeed.";
+
+ // Fail bar.
+ BarR->failMaterialization();
+
+ // Verify that both queries failed.
+ EXPECT_TRUE(OnFooReadyRun) << "Query for Foo did not run";
+ EXPECT_TRUE(OnBarReadyRun) << "Query for Bar did not run";
+}
+
TEST_F(CoreAPIsStandardTest, FailMaterializerWithUnqueriedSymbols) {
// Make sure that symbols with no queries aganist them still
// fail correctly.