1 //===------ ResourceTrackerTest.cpp - Unit tests ResourceTracker API ------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "OrcTestCommon.h"
10 #include "llvm/ADT/FunctionExtras.h"
11 #include "llvm/Config/llvm-config.h"
12 #include "llvm/ExecutionEngine/Orc/Core.h"
13 #include "llvm/ExecutionEngine/Orc/Shared/OrcError.h"
14 #include "llvm/Testing/Support/Error.h"
17 using namespace llvm::orc
;
19 class ResourceTrackerStandardTest
: public CoreAPIsBasedStandardTest
{};
23 template <typename ResourceT
= unsigned>
24 class SimpleResourceManager
: public ResourceManager
{
26 using HandleRemoveFunction
= unique_function
<Error(ResourceKey
)>;
28 using HandleTransferFunction
=
29 unique_function
<void(ResourceKey
, ResourceKey
)>;
31 using RecordedResourcesMap
= DenseMap
<ResourceKey
, ResourceT
>;
33 SimpleResourceManager(ExecutionSession
&ES
) : ES(ES
) {
34 HandleRemove
= [&](ResourceKey K
) -> Error
{
35 ES
.runSessionLocked([&] { removeResource(K
); });
36 return Error::success();
39 HandleTransfer
= [this](ResourceKey DstKey
, ResourceKey SrcKey
) {
40 transferResources(DstKey
, SrcKey
);
43 ES
.registerResourceManager(*this);
46 SimpleResourceManager(const SimpleResourceManager
&) = delete;
47 SimpleResourceManager
&operator=(const SimpleResourceManager
&) = delete;
48 SimpleResourceManager(SimpleResourceManager
&&) = delete;
49 SimpleResourceManager
&operator=(SimpleResourceManager
&&) = delete;
51 ~SimpleResourceManager() { ES
.deregisterResourceManager(*this); }
53 /// Set the HandleRemove function object.
54 void setHandleRemove(HandleRemoveFunction HandleRemove
) {
55 this->HandleRemove
= std::move(HandleRemove
);
58 /// Set the HandleTransfer function object.
59 void setHandleTransfer(HandleTransferFunction HandleTransfer
) {
60 this->HandleTransfer
= std::move(HandleTransfer
);
63 /// Create an association between the given key and resource.
64 template <typename MergeOp
= std::plus
<ResourceT
>>
65 void recordResource(ResourceKey K
, ResourceT Val
= ResourceT(),
66 MergeOp Merge
= MergeOp()) {
67 auto Tmp
= std::move(Resources
[K
]);
68 Resources
[K
] = Merge(std::move(Tmp
), std::move(Val
));
71 /// Remove the resource associated with K from the map if present.
72 void removeResource(ResourceKey K
) { Resources
.erase(K
); }
74 /// Transfer resources from DstKey to SrcKey.
75 template <typename MergeOp
= std::plus
<ResourceT
>>
76 void transferResources(ResourceKey DstKey
, ResourceKey SrcKey
,
77 MergeOp Merge
= MergeOp()) {
78 auto &DstResourceRef
= Resources
[DstKey
];
79 ResourceT DstResources
;
80 std::swap(DstResourceRef
, DstResources
);
82 auto SI
= Resources
.find(SrcKey
);
83 assert(SI
!= Resources
.end() && "No resource associated with SrcKey");
85 DstResourceRef
= Merge(std::move(DstResources
), std::move(SI
->second
));
89 /// Return a reference to the Resources map.
90 RecordedResourcesMap
&getRecordedResources() { return Resources
; }
91 const RecordedResourcesMap
&getRecordedResources() const { return Resources
; }
93 Error
handleRemoveResources(ResourceKey K
) override
{
94 return HandleRemove(K
);
97 void handleTransferResources(ResourceKey DstKey
,
98 ResourceKey SrcKey
) override
{
99 HandleTransfer(DstKey
, SrcKey
);
102 static void transferNotAllowed(ResourceKey DstKey
, ResourceKey SrcKey
) {
103 llvm_unreachable("Resource transfer not allowed");
107 ExecutionSession
&ES
;
108 HandleRemoveFunction HandleRemove
;
109 HandleTransferFunction HandleTransfer
;
110 RecordedResourcesMap Resources
;
113 TEST_F(ResourceTrackerStandardTest
,
114 BasicDefineAndRemoveAllBeforeMaterializing
) {
116 bool ResourceManagerGotRemove
= false;
117 SimpleResourceManager
<> SRM(ES
);
118 SRM
.setHandleRemove([&](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();
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");
132 nullptr, SimpleMaterializationUnit::DiscardFunction(),
133 [&]() { MaterializationUnitDestroyed
= true; });
135 auto RT
= JD
.createResourceTracker();
136 cantFail(JD
.define(std::move(MU
), RT
));
137 cantFail(RT
->remove());
138 auto SymFlags
= cantFail(ES
.lookupFlags(
140 {{&JD
, JITDylibLookupFlags::MatchExportedSymbolsOnly
}},
141 SymbolLookupSet(Foo
, SymbolLookupFlags::WeaklyReferencedSymbol
)));
143 EXPECT_EQ(SymFlags
.size(), 0U)
144 << "Symbols should have been removed from the symbol table";
145 EXPECT_TRUE(ResourceManagerGotRemove
)
146 << "ResourceManager did not receive handleRemoveResources";
147 EXPECT_TRUE(MaterializationUnitDestroyed
)
148 << "MaterializationUnit not destroyed in response to removal";
151 TEST_F(ResourceTrackerStandardTest
, BasicDefineAndRemoveAllAfterMaterializing
) {
153 bool ResourceManagerGotRemove
= false;
154 SimpleResourceManager
<> SRM(ES
);
155 SRM
.setHandleRemove([&](ResourceKey K
) -> Error
{
156 ResourceManagerGotRemove
= true;
157 EXPECT_EQ(SRM
.getRecordedResources().size(), 1U)
158 << "Unexpected number of resources recorded";
159 EXPECT_EQ(SRM
.getRecordedResources().count(K
), 1U)
160 << "Unexpected recorded resource";
161 SRM
.removeResource(K
);
162 return Error::success();
165 auto MU
= std::make_unique
<SimpleMaterializationUnit
>(
166 SymbolFlagsMap({{Foo
, FooSym
.getFlags()}}),
167 [&](std::unique_ptr
<MaterializationResponsibility
> R
) {
168 cantFail(R
->withResourceKeyDo(
169 [&](ResourceKey K
) { SRM
.recordResource(K
); }));
170 cantFail(R
->notifyResolved({{Foo
, FooSym
}}));
171 cantFail(R
->notifyEmitted());
174 auto RT
= JD
.createResourceTracker();
175 cantFail(JD
.define(std::move(MU
), RT
));
176 cantFail(ES
.lookup({&JD
}, Foo
));
177 cantFail(RT
->remove());
178 auto SymFlags
= cantFail(ES
.lookupFlags(
180 {{&JD
, JITDylibLookupFlags::MatchExportedSymbolsOnly
}},
181 SymbolLookupSet(Foo
, SymbolLookupFlags::WeaklyReferencedSymbol
)));
183 EXPECT_EQ(SymFlags
.size(), 0U)
184 << "Symbols should have been removed from the symbol table";
185 EXPECT_TRUE(ResourceManagerGotRemove
)
186 << "ResourceManager did not receive handleRemoveResources";
189 TEST_F(ResourceTrackerStandardTest
, BasicDefineAndRemoveAllWhileMaterializing
) {
191 bool ResourceManagerGotRemove
= false;
192 SimpleResourceManager
<> SRM(ES
);
193 SRM
.setHandleRemove([&](ResourceKey K
) -> Error
{
194 ResourceManagerGotRemove
= true;
195 EXPECT_EQ(SRM
.getRecordedResources().size(), 0U)
196 << "Unexpected resources recorded";
197 SRM
.removeResource(K
);
198 return Error::success();
201 std::unique_ptr
<MaterializationResponsibility
> MR
;
202 auto MU
= std::make_unique
<SimpleMaterializationUnit
>(
203 SymbolFlagsMap({{Foo
, FooSym
.getFlags()}}),
204 [&](std::unique_ptr
<MaterializationResponsibility
> R
) {
208 auto RT
= JD
.createResourceTracker();
209 cantFail(JD
.define(std::move(MU
), RT
));
212 LookupKind::Static
, makeJITDylibSearchOrder(&JD
), SymbolLookupSet(Foo
),
214 [](Expected
<SymbolMap
> Result
) {
215 EXPECT_THAT_EXPECTED(Result
, Failed
<FailedToMaterialize
>())
216 << "Lookup failed unexpectedly";
218 NoDependenciesToRegister
);
220 cantFail(RT
->remove());
221 auto SymFlags
= cantFail(ES
.lookupFlags(
223 {{&JD
, JITDylibLookupFlags::MatchExportedSymbolsOnly
}},
224 SymbolLookupSet(Foo
, SymbolLookupFlags::WeaklyReferencedSymbol
)));
226 EXPECT_EQ(SymFlags
.size(), 0U)
227 << "Symbols should have been removed from the symbol table";
228 EXPECT_TRUE(ResourceManagerGotRemove
)
229 << "ResourceManager did not receive handleRemoveResources";
231 EXPECT_THAT_ERROR(MR
->withResourceKeyDo([](ResourceKey K
) {
232 ADD_FAILURE() << "Should not reach withResourceKeyDo body for removed key";
234 Failed
<ResourceTrackerDefunct
>())
235 << "withResourceKeyDo on MR with removed tracker should have failed";
236 EXPECT_THAT_ERROR(MR
->notifyResolved({{Foo
, FooSym
}}),
237 Failed
<ResourceTrackerDefunct
>())
238 << "notifyResolved on MR with removed tracker should have failed";
240 MR
->failMaterialization();
243 TEST_F(ResourceTrackerStandardTest
, JITDylibClear
) {
244 SimpleResourceManager
<> SRM(ES
);
246 // Add materializer for Foo.
247 cantFail(JD
.define(std::make_unique
<SimpleMaterializationUnit
>(
248 SymbolFlagsMap({{Foo
, FooSym
.getFlags()}}),
249 [&](std::unique_ptr
<MaterializationResponsibility
> R
) {
250 cantFail(R
->withResourceKeyDo(
251 [&](ResourceKey K
) { ++SRM
.getRecordedResources()[K
]; }));
252 cantFail(R
->notifyResolved({{Foo
, FooSym
}}));
253 cantFail(R
->notifyEmitted());
256 // Add materializer for Bar.
257 cantFail(JD
.define(std::make_unique
<SimpleMaterializationUnit
>(
258 SymbolFlagsMap({{Bar
, BarSym
.getFlags()}}),
259 [&](std::unique_ptr
<MaterializationResponsibility
> R
) {
260 cantFail(R
->withResourceKeyDo(
261 [&](ResourceKey K
) { ++SRM
.getRecordedResources()[K
]; }));
262 cantFail(R
->notifyResolved({{Bar
, BarSym
}}));
263 cantFail(R
->notifyEmitted());
266 EXPECT_TRUE(SRM
.getRecordedResources().empty())
267 << "Expected no resources recorded yet.";
270 ES
.lookup(makeJITDylibSearchOrder(&JD
), SymbolLookupSet({Foo
, Bar
})));
272 auto JDResourceKey
= JD
.getDefaultResourceTracker()->getKeyUnsafe();
273 EXPECT_EQ(SRM
.getRecordedResources().size(), 1U)
274 << "Expected exactly one entry (for JD's ResourceKey)";
275 EXPECT_EQ(SRM
.getRecordedResources().count(JDResourceKey
), 1U)
276 << "Expected an entry for JD's ResourceKey";
277 EXPECT_EQ(SRM
.getRecordedResources()[JDResourceKey
], 2U)
278 << "Expected value of 2 for JD's ResourceKey "
279 "(+1 for each of Foo and Bar)";
281 cantFail(JD
.clear());
283 EXPECT_TRUE(SRM
.getRecordedResources().empty())
284 << "Expected no resources recorded after clear";
287 TEST_F(ResourceTrackerStandardTest
,
288 BasicDefineAndExplicitTransferBeforeMaterializing
) {
290 bool ResourceManagerGotTransfer
= false;
291 SimpleResourceManager
<> SRM(ES
);
292 SRM
.setHandleTransfer([&](ResourceKey DstKey
, ResourceKey SrcKey
) {
293 ResourceManagerGotTransfer
= true;
294 auto &RR
= SRM
.getRecordedResources();
295 EXPECT_EQ(RR
.size(), 0U) << "Expected no resources recorded yet";
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());
309 auto FooRT
= JD
.createResourceTracker();
310 cantFail(JD
.define(MakeMU(Foo
, FooSym
), FooRT
));
312 auto BarRT
= JD
.createResourceTracker();
313 cantFail(JD
.define(MakeMU(Bar
, BarSym
), BarRT
));
315 BarRT
->transferTo(*FooRT
);
317 EXPECT_TRUE(ResourceManagerGotTransfer
)
318 << "ResourceManager did not receive transfer";
319 EXPECT_TRUE(BarRT
->isDefunct()) << "BarRT should now be defunct";
322 ES
.lookup(makeJITDylibSearchOrder({&JD
}), SymbolLookupSet({Foo
, Bar
})));
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";
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());
337 TEST_F(ResourceTrackerStandardTest
,
338 BasicDefineAndExplicitTransferAfterMaterializing
) {
340 bool ResourceManagerGotTransfer
= false;
341 SimpleResourceManager
<> SRM(ES
);
342 SRM
.setHandleTransfer([&](ResourceKey DstKey
, ResourceKey SrcKey
) {
343 ResourceManagerGotTransfer
= true;
344 SRM
.transferResources(DstKey
, SrcKey
);
347 auto MakeMU
= [&](SymbolStringPtr Name
, JITEvaluatedSymbol Sym
) {
348 return std::make_unique
<SimpleMaterializationUnit
>(
349 SymbolFlagsMap({{Name
, Sym
.getFlags()}}),
350 [=, &SRM
](std::unique_ptr
<MaterializationResponsibility
> R
) {
351 cantFail(R
->withResourceKeyDo(
352 [&](ResourceKey K
) { SRM
.recordResource(K
, 1); }));
353 cantFail(R
->notifyResolved({{Name
, Sym
}}));
354 cantFail(R
->notifyEmitted());
358 auto FooRT
= JD
.createResourceTracker();
359 cantFail(JD
.define(MakeMU(Foo
, FooSym
), FooRT
));
361 auto BarRT
= JD
.createResourceTracker();
362 cantFail(JD
.define(MakeMU(Bar
, BarSym
), BarRT
));
364 EXPECT_EQ(SRM
.getRecordedResources().size(), 0U)
365 << "Expected no recorded resources yet";
368 ES
.lookup(makeJITDylibSearchOrder({&JD
}), SymbolLookupSet({Foo
, Bar
})));
370 EXPECT_EQ(SRM
.getRecordedResources().size(), 2U)
371 << "Expected recorded resources for both Foo and Bar";
373 BarRT
->transferTo(*FooRT
);
375 EXPECT_TRUE(ResourceManagerGotTransfer
)
376 << "ResourceManager did not receive transfer";
377 EXPECT_TRUE(BarRT
->isDefunct()) << "BarRT should now be defunct";
379 EXPECT_EQ(SRM
.getRecordedResources().size(), 1U)
380 << "Expected recorded resources for Foo only";
381 EXPECT_EQ(SRM
.getRecordedResources().count(FooRT
->getKeyUnsafe()), 1U)
382 << "Expected recorded resources for Foo";
383 EXPECT_EQ(SRM
.getRecordedResources()[FooRT
->getKeyUnsafe()], 2U)
384 << "Expected resources value for for Foo to be '2'";
387 TEST_F(ResourceTrackerStandardTest
,
388 BasicDefineAndExplicitTransferWhileMaterializing
) {
390 bool ResourceManagerGotTransfer
= false;
391 SimpleResourceManager
<> SRM(ES
);
392 SRM
.setHandleTransfer([&](ResourceKey DstKey
, ResourceKey SrcKey
) {
393 ResourceManagerGotTransfer
= true;
394 SRM
.transferResources(DstKey
, SrcKey
);
397 auto FooRT
= JD
.createResourceTracker();
398 std::unique_ptr
<MaterializationResponsibility
> FooMR
;
399 cantFail(JD
.define(std::make_unique
<SimpleMaterializationUnit
>(
400 SymbolFlagsMap({{Foo
, FooSym
.getFlags()}}),
401 [&](std::unique_ptr
<MaterializationResponsibility
> R
) {
402 FooMR
= std::move(R
);
406 auto BarRT
= JD
.createResourceTracker();
409 LookupKind::Static
, makeJITDylibSearchOrder(&JD
), SymbolLookupSet(Foo
),
411 [](Expected
<SymbolMap
> Result
) { cantFail(Result
.takeError()); },
412 NoDependenciesToRegister
);
414 cantFail(FooMR
->withResourceKeyDo([&](ResourceKey K
) {
415 EXPECT_EQ(FooRT
->getKeyUnsafe(), K
)
416 << "Expected FooRT's ResourceKey for Foo here";
417 SRM
.recordResource(K
, 1);
420 EXPECT_EQ(SRM
.getRecordedResources().size(), 1U)
421 << "Expected one recorded resource here";
422 EXPECT_EQ(SRM
.getRecordedResources()[FooRT
->getKeyUnsafe()], 1U)
423 << "Expected Resource value for FooRT to be '1' here";
425 FooRT
->transferTo(*BarRT
);
427 EXPECT_TRUE(ResourceManagerGotTransfer
)
428 << "Expected resource manager to receive handleTransferResources call";
430 cantFail(FooMR
->withResourceKeyDo([&](ResourceKey K
) {
431 EXPECT_EQ(BarRT
->getKeyUnsafe(), K
)
432 << "Expected BarRT's ResourceKey for Foo here";
433 SRM
.recordResource(K
, 1);
436 EXPECT_EQ(SRM
.getRecordedResources().size(), 1U)
437 << "Expected one recorded resource here";
438 EXPECT_EQ(SRM
.getRecordedResources().count(BarRT
->getKeyUnsafe()), 1U)
439 << "Expected RecordedResources to contain an entry for BarRT";
440 EXPECT_EQ(SRM
.getRecordedResources()[BarRT
->getKeyUnsafe()], 2U)
441 << "Expected Resource value for BarRT to be '2' here";
443 cantFail(FooMR
->notifyResolved({{Foo
, FooSym
}}));
444 cantFail(FooMR
->notifyEmitted());