1 //===------ ResourceTrackerTest.cpp - Unit tests ResourceTracker API 2 //-------===// 3 // 4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 5 // See https://llvm.org/LICENSE.txt for license information. 6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "OrcTestCommon.h" 11 #include "llvm/ADT/FunctionExtras.h" 12 #include "llvm/Config/llvm-config.h" 13 #include "llvm/ExecutionEngine/Orc/Core.h" 14 #include "llvm/ExecutionEngine/Orc/OrcError.h" 15 #include "llvm/Testing/Support/Error.h" 16 17 using namespace llvm; 18 using namespace llvm::orc; 19 20 class ResourceTrackerStandardTest : public CoreAPIsBasedStandardTest {}; 21 22 namespace { 23 24 template <typename ResourceT = unsigned> 25 class SimpleResourceManager : public ResourceManager { 26 public: 27 using HandleRemoveFunction = unique_function<Error(ResourceKey)>; 28 29 using HandleTransferFunction = 30 unique_function<void(ResourceKey, ResourceKey)>; 31 32 using RecordedResourcesMap = DenseMap<ResourceKey, ResourceT>; 33 34 SimpleResourceManager( 35 ExecutionSession &ES, 36 HandleRemoveFunction HandleRemove = HandleRemoveFunction(), 37 HandleTransferFunction HandleTransfer = HandleTransferFunction()) 38 : ES(ES), HandleRemove(std::move(HandleRemove)), 39 HandleTransfer(std::move(HandleTransfer)) { 40 41 // If HandleRemvoe is not supplied then use the default. 42 if (!this->HandleRemove) 43 this->HandleRemove = [&](ResourceKey K) -> Error { 44 ES.runSessionLocked([&] { removeResource(K); }); 45 return Error::success(); 46 }; 47 48 // If HandleTransfer is not supplied then use the default. 49 if (!this->HandleTransfer) 50 this->HandleTransfer = [this](ResourceKey DstKey, ResourceKey SrcKey) { 51 transferResources(DstKey, SrcKey); 52 }; 53 54 ES.registerResourceManager(*this); 55 } 56 57 SimpleResourceManager(const SimpleResourceManager &) = delete; 58 SimpleResourceManager &operator=(const SimpleResourceManager &) = delete; 59 SimpleResourceManager(SimpleResourceManager &&) = delete; 60 SimpleResourceManager &operator=(SimpleResourceManager &&) = delete; 61 62 ~SimpleResourceManager() { ES.deregisterResourceManager(*this); } 63 64 /// Create an association between the given key and resource. 65 template <typename MergeOp = std::plus<ResourceT>> 66 void recordResource(ResourceKey K, ResourceT Val = ResourceT(), 67 MergeOp Merge = MergeOp()) { 68 auto Tmp = std::move(Resources[K]); 69 Resources[K] = Merge(std::move(Tmp), std::move(Val)); 70 } 71 72 /// Remove the resource associated with K from the map if present. 73 void removeResource(ResourceKey K) { Resources.erase(K); } 74 75 /// Transfer resources from DstKey to SrcKey. 76 template <typename MergeOp = std::plus<ResourceT>> 77 void transferResources(ResourceKey DstKey, ResourceKey SrcKey, 78 MergeOp Merge = MergeOp()) { 79 auto &DstResourceRef = Resources[DstKey]; 80 ResourceT DstResources; 81 std::swap(DstResourceRef, DstResources); 82 83 auto SI = Resources.find(SrcKey); 84 assert(SI != Resources.end() && "No resource associated with SrcKey"); 85 86 DstResourceRef = Merge(std::move(DstResources), std::move(SI->second)); 87 Resources.erase(SI); 88 } 89 90 /// Return a reference to the Resources map. 91 RecordedResourcesMap &getRecordedResources() { return Resources; } 92 const RecordedResourcesMap &getRecordedResources() const { return Resources; } 93 94 Error handleRemoveResources(ResourceKey K) override { 95 return HandleRemove(K); 96 } 97 98 void handleTransferResources(ResourceKey DstKey, 99 ResourceKey SrcKey) override { 100 HandleTransfer(DstKey, SrcKey); 101 } 102 103 static void transferNotAllowed(ResourceKey DstKey, ResourceKey SrcKey) { 104 llvm_unreachable("Resource transfer not allowed"); 105 } 106 107 private: 108 ExecutionSession &ES; 109 HandleRemoveFunction HandleRemove; 110 HandleTransferFunction HandleTransfer; 111 RecordedResourcesMap Resources; 112 }; 113 114 TEST_F(ResourceTrackerStandardTest, 115 BasicDefineAndRemoveAllBeforeMaterializing) { 116 117 bool ResourceManagerGotRemove = false; 118 SimpleResourceManager<> SRM(ES, [&](ResourceKey K) -> Error { 119 ResourceManagerGotRemove = true; 120 EXPECT_EQ(SRM.getRecordedResources().size(), 0U) 121 << "Unexpected resources recorded"; 122 SRM.removeResource(K); 123 return Error::success(); 124 }); 125 126 bool MaterializationUnitDestroyed = false; 127 auto MU = std::make_unique<SimpleMaterializationUnit>( 128 SymbolFlagsMap({{Foo, FooSym.getFlags()}}), 129 [&](std::unique_ptr<MaterializationResponsibility> R) { 130 llvm_unreachable("Never called"); 131 }, 132 nullptr, SimpleMaterializationUnit::DiscardFunction(), 133 [&]() { MaterializationUnitDestroyed = true; }); 134 135 auto RT = JD.createResourceTracker(); 136 cantFail(JD.define(std::move(MU), RT)); 137 cantFail(RT->remove()); 138 auto SymFlags = cantFail(JD.lookupFlags( 139 LookupKind::Static, JITDylibLookupFlags::MatchExportedSymbolsOnly, 140 SymbolLookupSet(Foo))); 141 142 EXPECT_EQ(SymFlags.size(), 0U) 143 << "Symbols should have been removed from the symbol table"; 144 EXPECT_TRUE(ResourceManagerGotRemove) 145 << "ResourceManager did not receive handleRemoveResources"; 146 EXPECT_TRUE(MaterializationUnitDestroyed) 147 << "MaterializationUnit not destroyed in response to removal"; 148 } 149 150 TEST_F(ResourceTrackerStandardTest, BasicDefineAndRemoveAllAfterMaterializing) { 151 152 bool ResourceManagerGotRemove = false; 153 SimpleResourceManager<> SRM(ES, [&](ResourceKey K) -> Error { 154 ResourceManagerGotRemove = true; 155 EXPECT_EQ(SRM.getRecordedResources().size(), 1U) 156 << "Unexpected number of resources recorded"; 157 EXPECT_EQ(SRM.getRecordedResources().count(K), 1U) 158 << "Unexpected recorded resource"; 159 SRM.removeResource(K); 160 return Error::success(); 161 }); 162 163 auto MU = std::make_unique<SimpleMaterializationUnit>( 164 SymbolFlagsMap({{Foo, FooSym.getFlags()}}), 165 [&](std::unique_ptr<MaterializationResponsibility> R) { 166 cantFail(R->withResourceKeyDo( 167 [&](ResourceKey K) { SRM.recordResource(K); })); 168 cantFail(R->notifyResolved({{Foo, FooSym}})); 169 cantFail(R->notifyEmitted()); 170 }); 171 172 auto RT = JD.createResourceTracker(); 173 cantFail(JD.define(std::move(MU), RT)); 174 cantFail(ES.lookup({&JD}, Foo)); 175 cantFail(RT->remove()); 176 auto SymFlags = cantFail(JD.lookupFlags( 177 LookupKind::Static, JITDylibLookupFlags::MatchExportedSymbolsOnly, 178 SymbolLookupSet(Foo))); 179 180 EXPECT_EQ(SymFlags.size(), 0U) 181 << "Symbols should have been removed from the symbol table"; 182 EXPECT_TRUE(ResourceManagerGotRemove) 183 << "ResourceManager did not receive handleRemoveResources"; 184 } 185 186 TEST_F(ResourceTrackerStandardTest, BasicDefineAndRemoveAllWhileMaterializing) { 187 188 bool ResourceManagerGotRemove = false; 189 SimpleResourceManager<> SRM(ES, [&](ResourceKey K) -> Error { 190 ResourceManagerGotRemove = true; 191 EXPECT_EQ(SRM.getRecordedResources().size(), 0U) 192 << "Unexpected resources recorded"; 193 SRM.removeResource(K); 194 return Error::success(); 195 }); 196 197 std::unique_ptr<MaterializationResponsibility> MR; 198 auto MU = std::make_unique<SimpleMaterializationUnit>( 199 SymbolFlagsMap({{Foo, FooSym.getFlags()}}), 200 [&](std::unique_ptr<MaterializationResponsibility> R) { 201 MR = std::move(R); 202 }); 203 204 auto RT = JD.createResourceTracker(); 205 cantFail(JD.define(std::move(MU), RT)); 206 207 ES.lookup( 208 LookupKind::Static, makeJITDylibSearchOrder(&JD), SymbolLookupSet(Foo), 209 SymbolState::Ready, 210 [](Expected<SymbolMap> Result) { 211 EXPECT_THAT_EXPECTED(Result, Failed<FailedToMaterialize>()) 212 << "Lookup failed unexpectedly"; 213 }, 214 NoDependenciesToRegister); 215 216 cantFail(RT->remove()); 217 auto SymFlags = cantFail(JD.lookupFlags( 218 LookupKind::Static, JITDylibLookupFlags::MatchExportedSymbolsOnly, 219 SymbolLookupSet(Foo))); 220 221 EXPECT_EQ(SymFlags.size(), 0U) 222 << "Symbols should have been removed from the symbol table"; 223 EXPECT_TRUE(ResourceManagerGotRemove) 224 << "ResourceManager did not receive handleRemoveResources"; 225 226 EXPECT_THAT_ERROR(MR->withResourceKeyDo([](ResourceKey K) { 227 ADD_FAILURE() << "Should not reach withResourceKeyDo body for removed key"; 228 }), 229 Failed<ResourceTrackerDefunct>()) 230 << "withResourceKeyDo on MR with removed tracker should have failed"; 231 EXPECT_THAT_ERROR(MR->notifyResolved({{Foo, FooSym}}), 232 Failed<ResourceTrackerDefunct>()) 233 << "notifyResolved on MR with removed tracker should have failed"; 234 235 MR->failMaterialization(); 236 } 237 238 TEST_F(ResourceTrackerStandardTest, JITDylibClear) { 239 SimpleResourceManager<> SRM(ES); 240 241 // Add materializer for Foo. 242 cantFail(JD.define(std::make_unique<SimpleMaterializationUnit>( 243 SymbolFlagsMap({{Foo, FooSym.getFlags()}}), 244 [&](std::unique_ptr<MaterializationResponsibility> R) { 245 cantFail(R->withResourceKeyDo( 246 [&](ResourceKey K) { ++SRM.getRecordedResources()[K]; })); 247 cantFail(R->notifyResolved({{Foo, FooSym}})); 248 cantFail(R->notifyEmitted()); 249 }))); 250 251 // Add materializer for Bar. 252 cantFail(JD.define(std::make_unique<SimpleMaterializationUnit>( 253 SymbolFlagsMap({{Bar, BarSym.getFlags()}}), 254 [&](std::unique_ptr<MaterializationResponsibility> R) { 255 cantFail(R->withResourceKeyDo( 256 [&](ResourceKey K) { ++SRM.getRecordedResources()[K]; })); 257 cantFail(R->notifyResolved({{Bar, BarSym}})); 258 cantFail(R->notifyEmitted()); 259 }))); 260 261 EXPECT_TRUE(SRM.getRecordedResources().empty()) 262 << "Expected no resources recorded yet."; 263 264 cantFail( 265 ES.lookup(makeJITDylibSearchOrder(&JD), SymbolLookupSet({Foo, Bar}))); 266 267 auto JDResourceKey = JD.getDefaultResourceTracker()->getKeyUnsafe(); 268 EXPECT_EQ(SRM.getRecordedResources().size(), 1U) 269 << "Expected exactly one entry (for JD's ResourceKey)"; 270 EXPECT_EQ(SRM.getRecordedResources().count(JDResourceKey), 1U) 271 << "Expected an entry for JD's ResourceKey"; 272 EXPECT_EQ(SRM.getRecordedResources()[JDResourceKey], 2U) 273 << "Expected value of 2 for JD's ResourceKey " 274 "(+1 for each of Foo and Bar)"; 275 276 cantFail(JD.clear()); 277 278 EXPECT_TRUE(SRM.getRecordedResources().empty()) 279 << "Expected no resources recorded after clear"; 280 } 281 282 TEST_F(ResourceTrackerStandardTest, 283 BasicDefineAndExplicitTransferBeforeMaterializing) { 284 285 bool ResourceManagerGotTransfer = false; 286 SimpleResourceManager<> SRM( 287 ES, 288 [&](ResourceKey K) -> Error { 289 SRM.removeResource(K); 290 return Error::success(); 291 }, 292 [&](ResourceKey DstKey, ResourceKey SrcKey) { 293 ResourceManagerGotTransfer = true; 294 auto &RR = SRM.getRecordedResources(); 295 EXPECT_EQ(RR.size(), 0U) << "Expected no resources recorded yet"; 296 }); 297 298 auto MakeMU = [&](SymbolStringPtr Name, JITEvaluatedSymbol Sym) { 299 return std::make_unique<SimpleMaterializationUnit>( 300 SymbolFlagsMap({{Name, Sym.getFlags()}}), 301 [=, &SRM](std::unique_ptr<MaterializationResponsibility> R) { 302 cantFail(R->withResourceKeyDo( 303 [&](ResourceKey K) { SRM.recordResource(K); })); 304 cantFail(R->notifyResolved({{Name, Sym}})); 305 cantFail(R->notifyEmitted()); 306 }); 307 }; 308 309 auto FooRT = JD.createResourceTracker(); 310 cantFail(JD.define(MakeMU(Foo, FooSym), FooRT)); 311 312 auto BarRT = JD.createResourceTracker(); 313 cantFail(JD.define(MakeMU(Bar, BarSym), BarRT)); 314 315 BarRT->transferTo(*FooRT); 316 317 EXPECT_TRUE(ResourceManagerGotTransfer) 318 << "ResourceManager did not receive transfer"; 319 EXPECT_TRUE(BarRT->isDefunct()) << "BarRT should now be defunct"; 320 321 cantFail( 322 ES.lookup(makeJITDylibSearchOrder({&JD}), SymbolLookupSet({Foo, Bar}))); 323 324 EXPECT_EQ(SRM.getRecordedResources().size(), 1U) 325 << "Expected exactly one entry (for FooRT's Key)"; 326 EXPECT_EQ(SRM.getRecordedResources().count(FooRT->getKeyUnsafe()), 1U) 327 << "Expected an entry for FooRT's ResourceKey"; 328 EXPECT_EQ(SRM.getRecordedResources().count(BarRT->getKeyUnsafe()), 0U) 329 << "Expected no entry for BarRT's ResourceKey"; 330 331 // We need to explicitly destroy FooRT or its resources will be implicitly 332 // transferred to the default tracker triggering a second call to our 333 // transfer function above (which expects only one call). 334 cantFail(FooRT->remove()); 335 } 336 337 TEST_F(ResourceTrackerStandardTest, 338 BasicDefineAndExplicitTransferAfterMaterializing) { 339 340 bool ResourceManagerGotTransfer = false; 341 SimpleResourceManager<> SRM( 342 ES, 343 [&](ResourceKey K) -> Error { 344 SRM.removeResource(K); 345 return Error::success(); 346 }, 347 [&](ResourceKey DstKey, ResourceKey SrcKey) { 348 ResourceManagerGotTransfer = true; 349 SRM.transferResources(DstKey, SrcKey); 350 }); 351 352 auto MakeMU = [&](SymbolStringPtr Name, JITEvaluatedSymbol Sym) { 353 return std::make_unique<SimpleMaterializationUnit>( 354 SymbolFlagsMap({{Name, Sym.getFlags()}}), 355 [=, &SRM](std::unique_ptr<MaterializationResponsibility> R) { 356 cantFail(R->withResourceKeyDo( 357 [&](ResourceKey K) { SRM.recordResource(K, 1); })); 358 cantFail(R->notifyResolved({{Name, Sym}})); 359 cantFail(R->notifyEmitted()); 360 }); 361 }; 362 363 auto FooRT = JD.createResourceTracker(); 364 cantFail(JD.define(MakeMU(Foo, FooSym), FooRT)); 365 366 auto BarRT = JD.createResourceTracker(); 367 cantFail(JD.define(MakeMU(Bar, BarSym), BarRT)); 368 369 EXPECT_EQ(SRM.getRecordedResources().size(), 0U) 370 << "Expected no recorded resources yet"; 371 372 cantFail( 373 ES.lookup(makeJITDylibSearchOrder({&JD}), SymbolLookupSet({Foo, Bar}))); 374 375 EXPECT_EQ(SRM.getRecordedResources().size(), 2U) 376 << "Expected recorded resources for both Foo and Bar"; 377 378 BarRT->transferTo(*FooRT); 379 380 EXPECT_TRUE(ResourceManagerGotTransfer) 381 << "ResourceManager did not receive transfer"; 382 EXPECT_TRUE(BarRT->isDefunct()) << "BarRT should now be defunct"; 383 384 EXPECT_EQ(SRM.getRecordedResources().size(), 1U) 385 << "Expected recorded resources for Foo only"; 386 EXPECT_EQ(SRM.getRecordedResources().count(FooRT->getKeyUnsafe()), 1U) 387 << "Expected recorded resources for Foo"; 388 EXPECT_EQ(SRM.getRecordedResources()[FooRT->getKeyUnsafe()], 2U) 389 << "Expected resources value for for Foo to be '2'"; 390 } 391 392 TEST_F(ResourceTrackerStandardTest, 393 BasicDefineAndExplicitTransferWhileMaterializing) { 394 395 bool ResourceManagerGotTransfer = false; 396 SimpleResourceManager<> SRM( 397 ES, 398 [&](ResourceKey K) -> Error { 399 SRM.removeResource(K); 400 return Error::success(); 401 }, 402 [&](ResourceKey DstKey, ResourceKey SrcKey) { 403 ResourceManagerGotTransfer = true; 404 SRM.transferResources(DstKey, SrcKey); 405 }); 406 407 auto FooRT = JD.createResourceTracker(); 408 std::unique_ptr<MaterializationResponsibility> FooMR; 409 cantFail(JD.define(std::make_unique<SimpleMaterializationUnit>( 410 SymbolFlagsMap({{Foo, FooSym.getFlags()}}), 411 [&](std::unique_ptr<MaterializationResponsibility> R) { 412 FooMR = std::move(R); 413 }), 414 FooRT)); 415 416 auto BarRT = JD.createResourceTracker(); 417 418 ES.lookup( 419 LookupKind::Static, makeJITDylibSearchOrder(&JD), SymbolLookupSet(Foo), 420 SymbolState::Ready, 421 [](Expected<SymbolMap> Result) { cantFail(Result.takeError()); }, 422 NoDependenciesToRegister); 423 424 cantFail(FooMR->withResourceKeyDo([&](ResourceKey K) { 425 EXPECT_EQ(FooRT->getKeyUnsafe(), K) 426 << "Expected FooRT's ResourceKey for Foo here"; 427 SRM.recordResource(K, 1); 428 })); 429 430 EXPECT_EQ(SRM.getRecordedResources().size(), 1U) 431 << "Expected one recorded resource here"; 432 EXPECT_EQ(SRM.getRecordedResources()[FooRT->getKeyUnsafe()], 1U) 433 << "Expected Resource value for FooRT to be '1' here"; 434 435 FooRT->transferTo(*BarRT); 436 437 EXPECT_TRUE(ResourceManagerGotTransfer) 438 << "Expected resource manager to receive handleTransferResources call"; 439 440 cantFail(FooMR->withResourceKeyDo([&](ResourceKey K) { 441 EXPECT_EQ(BarRT->getKeyUnsafe(), K) 442 << "Expected BarRT's ResourceKey for Foo here"; 443 SRM.recordResource(K, 1); 444 })); 445 446 EXPECT_EQ(SRM.getRecordedResources().size(), 1U) 447 << "Expected one recorded resource here"; 448 EXPECT_EQ(SRM.getRecordedResources().count(BarRT->getKeyUnsafe()), 1U) 449 << "Expected RecordedResources to contain an entry for BarRT"; 450 EXPECT_EQ(SRM.getRecordedResources()[BarRT->getKeyUnsafe()], 2U) 451 << "Expected Resource value for BarRT to be '2' here"; 452 453 cantFail(FooMR->notifyResolved({{Foo, FooSym}})); 454 cantFail(FooMR->notifyEmitted()); 455 } 456 457 } // namespace 458