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
=
27 unique_function
<Error(JITDylib
&JD
, ResourceKey
)>;
29 using HandleTransferFunction
=
30 unique_function
<void(JITDylib
&JD
, ResourceKey
, ResourceKey
)>;
32 using RecordedResourcesMap
= DenseMap
<ResourceKey
, ResourceT
>;
34 SimpleResourceManager(ExecutionSession
&ES
) : ES(ES
) {
35 HandleRemove
= [&](JITDylib
&JD
, ResourceKey K
) -> Error
{
36 ES
.runSessionLocked([&] { removeResource(JD
, K
); });
37 return Error::success();
40 HandleTransfer
= [this](JITDylib
&JD
, ResourceKey DstKey
,
42 transferResources(JD
, DstKey
, SrcKey
);
45 ES
.registerResourceManager(*this);
48 SimpleResourceManager(const SimpleResourceManager
&) = delete;
49 SimpleResourceManager
&operator=(const SimpleResourceManager
&) = delete;
50 SimpleResourceManager(SimpleResourceManager
&&) = delete;
51 SimpleResourceManager
&operator=(SimpleResourceManager
&&) = delete;
53 ~SimpleResourceManager() { ES
.deregisterResourceManager(*this); }
55 /// Set the HandleRemove function object.
56 void setHandleRemove(HandleRemoveFunction HandleRemove
) {
57 this->HandleRemove
= std::move(HandleRemove
);
60 /// Set the HandleTransfer function object.
61 void setHandleTransfer(HandleTransferFunction HandleTransfer
) {
62 this->HandleTransfer
= std::move(HandleTransfer
);
65 /// Create an association between the given key and resource.
66 template <typename MergeOp
= std::plus
<ResourceT
>>
67 void recordResource(ResourceKey K
, ResourceT Val
= ResourceT(),
68 MergeOp Merge
= MergeOp()) {
69 auto Tmp
= std::move(Resources
[K
]);
70 Resources
[K
] = Merge(std::move(Tmp
), std::move(Val
));
73 /// Remove the resource associated with K from the map if present.
74 void removeResource(JITDylib
&JD
, ResourceKey K
) { Resources
.erase(K
); }
76 /// Transfer resources from DstKey to SrcKey.
77 template <typename MergeOp
= std::plus
<ResourceT
>>
78 void transferResources(JITDylib
&JD
, ResourceKey DstKey
, ResourceKey SrcKey
,
79 MergeOp Merge
= MergeOp()) {
80 auto &DstResourceRef
= Resources
[DstKey
];
81 ResourceT DstResources
;
82 std::swap(DstResourceRef
, DstResources
);
84 auto SI
= Resources
.find(SrcKey
);
85 assert(SI
!= Resources
.end() && "No resource associated with SrcKey");
87 DstResourceRef
= Merge(std::move(DstResources
), std::move(SI
->second
));
91 /// Return a reference to the Resources map.
92 RecordedResourcesMap
&getRecordedResources() { return Resources
; }
93 const RecordedResourcesMap
&getRecordedResources() const { return Resources
; }
95 Error
handleRemoveResources(JITDylib
&JD
, ResourceKey K
) override
{
96 return HandleRemove(JD
, K
);
99 void handleTransferResources(JITDylib
&JD
, ResourceKey DstKey
,
100 ResourceKey SrcKey
) override
{
101 HandleTransfer(JD
, DstKey
, SrcKey
);
104 static void transferNotAllowed(ResourceKey DstKey
, ResourceKey SrcKey
) {
105 llvm_unreachable("Resource transfer not allowed");
109 ExecutionSession
&ES
;
110 HandleRemoveFunction HandleRemove
;
111 HandleTransferFunction HandleTransfer
;
112 RecordedResourcesMap Resources
;
115 TEST_F(ResourceTrackerStandardTest
,
116 BasicDefineAndRemoveAllBeforeMaterializing
) {
118 bool ResourceManagerGotRemove
= false;
119 SimpleResourceManager
<> SRM(ES
);
120 SRM
.setHandleRemove([&](JITDylib
&JD
, ResourceKey K
) -> Error
{
121 ResourceManagerGotRemove
= true;
122 EXPECT_EQ(SRM
.getRecordedResources().size(), 0U)
123 << "Unexpected resources recorded";
124 SRM
.removeResource(JD
, K
);
125 return Error::success();
128 bool MaterializationUnitDestroyed
= false;
129 auto MU
= std::make_unique
<SimpleMaterializationUnit
>(
130 SymbolFlagsMap({{Foo
, FooSym
.getFlags()}}),
131 [&](std::unique_ptr
<MaterializationResponsibility
> R
) {
132 llvm_unreachable("Never called");
134 nullptr, SimpleMaterializationUnit::DiscardFunction(),
135 [&]() { MaterializationUnitDestroyed
= true; });
137 auto RT
= JD
.createResourceTracker();
138 cantFail(JD
.define(std::move(MU
), RT
));
139 cantFail(RT
->remove());
140 auto SymFlags
= cantFail(ES
.lookupFlags(
142 {{&JD
, JITDylibLookupFlags::MatchExportedSymbolsOnly
}},
143 SymbolLookupSet(Foo
, SymbolLookupFlags::WeaklyReferencedSymbol
)));
145 EXPECT_EQ(SymFlags
.size(), 0U)
146 << "Symbols should have been removed from the symbol table";
147 EXPECT_TRUE(ResourceManagerGotRemove
)
148 << "ResourceManager did not receive handleRemoveResources";
149 EXPECT_TRUE(MaterializationUnitDestroyed
)
150 << "MaterializationUnit not destroyed in response to removal";
153 TEST_F(ResourceTrackerStandardTest
, BasicDefineAndRemoveAllAfterMaterializing
) {
155 bool ResourceManagerGotRemove
= false;
156 SimpleResourceManager
<> SRM(ES
);
157 SRM
.setHandleRemove([&](JITDylib
&JD
, ResourceKey K
) -> Error
{
158 ResourceManagerGotRemove
= true;
159 EXPECT_EQ(SRM
.getRecordedResources().size(), 1U)
160 << "Unexpected number of resources recorded";
161 EXPECT_EQ(SRM
.getRecordedResources().count(K
), 1U)
162 << "Unexpected recorded resource";
163 SRM
.removeResource(JD
, K
);
164 return Error::success();
167 auto MU
= std::make_unique
<SimpleMaterializationUnit
>(
168 SymbolFlagsMap({{Foo
, FooSym
.getFlags()}}),
169 [&](std::unique_ptr
<MaterializationResponsibility
> R
) {
170 cantFail(R
->withResourceKeyDo(
171 [&](ResourceKey K
) { SRM
.recordResource(K
); }));
172 cantFail(R
->notifyResolved({{Foo
, FooSym
}}));
173 cantFail(R
->notifyEmitted({}));
176 auto RT
= JD
.createResourceTracker();
177 cantFail(JD
.define(std::move(MU
), RT
));
178 cantFail(ES
.lookup({&JD
}, Foo
));
179 cantFail(RT
->remove());
180 auto SymFlags
= cantFail(ES
.lookupFlags(
182 {{&JD
, JITDylibLookupFlags::MatchExportedSymbolsOnly
}},
183 SymbolLookupSet(Foo
, SymbolLookupFlags::WeaklyReferencedSymbol
)));
185 EXPECT_EQ(SymFlags
.size(), 0U)
186 << "Symbols should have been removed from the symbol table";
187 EXPECT_TRUE(ResourceManagerGotRemove
)
188 << "ResourceManager did not receive handleRemoveResources";
191 TEST_F(ResourceTrackerStandardTest
, BasicDefineAndRemoveAllWhileMaterializing
) {
193 bool ResourceManagerGotRemove
= false;
194 SimpleResourceManager
<> SRM(ES
);
195 SRM
.setHandleRemove([&](JITDylib
&JD
, ResourceKey K
) -> Error
{
196 ResourceManagerGotRemove
= true;
197 EXPECT_EQ(SRM
.getRecordedResources().size(), 0U)
198 << "Unexpected resources recorded";
199 SRM
.removeResource(JD
, K
);
200 return Error::success();
203 std::unique_ptr
<MaterializationResponsibility
> MR
;
204 auto MU
= std::make_unique
<SimpleMaterializationUnit
>(
205 SymbolFlagsMap({{Foo
, FooSym
.getFlags()}}),
206 [&](std::unique_ptr
<MaterializationResponsibility
> R
) {
210 auto RT
= JD
.createResourceTracker();
211 cantFail(JD
.define(std::move(MU
), RT
));
214 LookupKind::Static
, makeJITDylibSearchOrder(&JD
), SymbolLookupSet(Foo
),
216 [](Expected
<SymbolMap
> Result
) {
217 EXPECT_THAT_EXPECTED(Result
, Failed
<FailedToMaterialize
>())
218 << "Lookup failed unexpectedly";
220 NoDependenciesToRegister
);
222 cantFail(RT
->remove());
223 auto SymFlags
= cantFail(ES
.lookupFlags(
225 {{&JD
, JITDylibLookupFlags::MatchExportedSymbolsOnly
}},
226 SymbolLookupSet(Foo
, SymbolLookupFlags::WeaklyReferencedSymbol
)));
228 EXPECT_EQ(SymFlags
.size(), 0U)
229 << "Symbols should have been removed from the symbol table";
230 EXPECT_TRUE(ResourceManagerGotRemove
)
231 << "ResourceManager did not receive handleRemoveResources";
233 EXPECT_THAT_ERROR(MR
->withResourceKeyDo([](ResourceKey K
) {
234 ADD_FAILURE() << "Should not reach withResourceKeyDo body for removed key";
236 Failed
<ResourceTrackerDefunct
>())
237 << "withResourceKeyDo on MR with removed tracker should have failed";
238 EXPECT_THAT_ERROR(MR
->notifyResolved({{Foo
, FooSym
}}),
239 Failed
<ResourceTrackerDefunct
>())
240 << "notifyResolved on MR with removed tracker should have failed";
242 MR
->failMaterialization();
245 TEST_F(ResourceTrackerStandardTest
, JITDylibClear
) {
246 SimpleResourceManager
<> SRM(ES
);
248 // Add materializer for Foo.
249 cantFail(JD
.define(std::make_unique
<SimpleMaterializationUnit
>(
250 SymbolFlagsMap({{Foo
, FooSym
.getFlags()}}),
251 [&](std::unique_ptr
<MaterializationResponsibility
> R
) {
252 cantFail(R
->withResourceKeyDo(
253 [&](ResourceKey K
) { ++SRM
.getRecordedResources()[K
]; }));
254 cantFail(R
->notifyResolved({{Foo
, FooSym
}}));
255 cantFail(R
->notifyEmitted({}));
258 // Add materializer for Bar.
259 cantFail(JD
.define(std::make_unique
<SimpleMaterializationUnit
>(
260 SymbolFlagsMap({{Bar
, BarSym
.getFlags()}}),
261 [&](std::unique_ptr
<MaterializationResponsibility
> R
) {
262 cantFail(R
->withResourceKeyDo(
263 [&](ResourceKey K
) { ++SRM
.getRecordedResources()[K
]; }));
264 cantFail(R
->notifyResolved({{Bar
, BarSym
}}));
265 cantFail(R
->notifyEmitted({}));
268 EXPECT_TRUE(SRM
.getRecordedResources().empty())
269 << "Expected no resources recorded yet.";
272 ES
.lookup(makeJITDylibSearchOrder(&JD
), SymbolLookupSet({Foo
, Bar
})));
274 auto JDResourceKey
= JD
.getDefaultResourceTracker()->getKeyUnsafe();
275 EXPECT_EQ(SRM
.getRecordedResources().size(), 1U)
276 << "Expected exactly one entry (for JD's ResourceKey)";
277 EXPECT_EQ(SRM
.getRecordedResources().count(JDResourceKey
), 1U)
278 << "Expected an entry for JD's ResourceKey";
279 EXPECT_EQ(SRM
.getRecordedResources()[JDResourceKey
], 2U)
280 << "Expected value of 2 for JD's ResourceKey "
281 "(+1 for each of Foo and Bar)";
283 cantFail(JD
.clear());
285 EXPECT_TRUE(SRM
.getRecordedResources().empty())
286 << "Expected no resources recorded after clear";
289 TEST_F(ResourceTrackerStandardTest
,
290 BasicDefineAndExplicitTransferBeforeMaterializing
) {
292 bool ResourceManagerGotTransfer
= false;
293 SimpleResourceManager
<> SRM(ES
);
294 SRM
.setHandleTransfer(
295 [&](JITDylib
&JD
, ResourceKey DstKey
, ResourceKey SrcKey
) {
296 ResourceManagerGotTransfer
= true;
297 auto &RR
= SRM
.getRecordedResources();
298 EXPECT_EQ(RR
.size(), 0U) << "Expected no resources recorded yet";
301 auto MakeMU
= [&](SymbolStringPtr Name
, ExecutorSymbolDef Sym
) {
302 return std::make_unique
<SimpleMaterializationUnit
>(
303 SymbolFlagsMap({{Name
, Sym
.getFlags()}}),
304 [=, &SRM
](std::unique_ptr
<MaterializationResponsibility
> R
) {
305 cantFail(R
->withResourceKeyDo(
306 [&](ResourceKey K
) { SRM
.recordResource(K
); }));
307 cantFail(R
->notifyResolved({{Name
, Sym
}}));
308 cantFail(R
->notifyEmitted({}));
312 auto FooRT
= JD
.createResourceTracker();
313 cantFail(JD
.define(MakeMU(Foo
, FooSym
), FooRT
));
315 auto BarRT
= JD
.createResourceTracker();
316 cantFail(JD
.define(MakeMU(Bar
, BarSym
), BarRT
));
318 BarRT
->transferTo(*FooRT
);
320 EXPECT_TRUE(ResourceManagerGotTransfer
)
321 << "ResourceManager did not receive transfer";
322 EXPECT_TRUE(BarRT
->isDefunct()) << "BarRT should now be defunct";
325 ES
.lookup(makeJITDylibSearchOrder({&JD
}), SymbolLookupSet({Foo
, Bar
})));
327 EXPECT_EQ(SRM
.getRecordedResources().size(), 1U)
328 << "Expected exactly one entry (for FooRT's Key)";
329 EXPECT_EQ(SRM
.getRecordedResources().count(FooRT
->getKeyUnsafe()), 1U)
330 << "Expected an entry for FooRT's ResourceKey";
331 EXPECT_EQ(SRM
.getRecordedResources().count(BarRT
->getKeyUnsafe()), 0U)
332 << "Expected no entry for BarRT's ResourceKey";
334 // We need to explicitly destroy FooRT or its resources will be implicitly
335 // transferred to the default tracker triggering a second call to our
336 // transfer function above (which expects only one call).
337 cantFail(FooRT
->remove());
340 TEST_F(ResourceTrackerStandardTest
,
341 BasicDefineAndExplicitTransferAfterMaterializing
) {
343 bool ResourceManagerGotTransfer
= false;
344 SimpleResourceManager
<> SRM(ES
);
345 SRM
.setHandleTransfer(
346 [&](JITDylib
&JD
, ResourceKey DstKey
, ResourceKey SrcKey
) {
347 ResourceManagerGotTransfer
= true;
348 SRM
.transferResources(JD
, DstKey
, SrcKey
);
351 auto MakeMU
= [&](SymbolStringPtr Name
, ExecutorSymbolDef Sym
) {
352 return std::make_unique
<SimpleMaterializationUnit
>(
353 SymbolFlagsMap({{Name
, Sym
.getFlags()}}),
354 [=, &SRM
](std::unique_ptr
<MaterializationResponsibility
> R
) {
355 cantFail(R
->withResourceKeyDo(
356 [&](ResourceKey K
) { SRM
.recordResource(K
, 1); }));
357 cantFail(R
->notifyResolved({{Name
, Sym
}}));
358 cantFail(R
->notifyEmitted({}));
362 auto FooRT
= JD
.createResourceTracker();
363 cantFail(JD
.define(MakeMU(Foo
, FooSym
), FooRT
));
365 auto BarRT
= JD
.createResourceTracker();
366 cantFail(JD
.define(MakeMU(Bar
, BarSym
), BarRT
));
368 EXPECT_EQ(SRM
.getRecordedResources().size(), 0U)
369 << "Expected no recorded resources yet";
372 ES
.lookup(makeJITDylibSearchOrder({&JD
}), SymbolLookupSet({Foo
, Bar
})));
374 EXPECT_EQ(SRM
.getRecordedResources().size(), 2U)
375 << "Expected recorded resources for both Foo and Bar";
377 BarRT
->transferTo(*FooRT
);
379 EXPECT_TRUE(ResourceManagerGotTransfer
)
380 << "ResourceManager did not receive transfer";
381 EXPECT_TRUE(BarRT
->isDefunct()) << "BarRT should now be defunct";
383 EXPECT_EQ(SRM
.getRecordedResources().size(), 1U)
384 << "Expected recorded resources for Foo only";
385 EXPECT_EQ(SRM
.getRecordedResources().count(FooRT
->getKeyUnsafe()), 1U)
386 << "Expected recorded resources for Foo";
387 EXPECT_EQ(SRM
.getRecordedResources()[FooRT
->getKeyUnsafe()], 2U)
388 << "Expected resources value for for Foo to be '2'";
391 TEST_F(ResourceTrackerStandardTest
,
392 BasicDefineAndExplicitTransferWhileMaterializing
) {
394 bool ResourceManagerGotTransfer
= false;
395 SimpleResourceManager
<> SRM(ES
);
396 SRM
.setHandleTransfer(
397 [&](JITDylib
&JD
, ResourceKey DstKey
, ResourceKey SrcKey
) {
398 ResourceManagerGotTransfer
= true;
399 SRM
.transferResources(JD
, DstKey
, SrcKey
);
402 auto FooRT
= JD
.createResourceTracker();
403 std::unique_ptr
<MaterializationResponsibility
> FooMR
;
404 cantFail(JD
.define(std::make_unique
<SimpleMaterializationUnit
>(
405 SymbolFlagsMap({{Foo
, FooSym
.getFlags()}}),
406 [&](std::unique_ptr
<MaterializationResponsibility
> R
) {
407 FooMR
= std::move(R
);
411 auto BarRT
= JD
.createResourceTracker();
414 LookupKind::Static
, makeJITDylibSearchOrder(&JD
), SymbolLookupSet(Foo
),
416 [](Expected
<SymbolMap
> Result
) { cantFail(Result
.takeError()); },
417 NoDependenciesToRegister
);
419 cantFail(FooMR
->withResourceKeyDo([&](ResourceKey K
) {
420 EXPECT_EQ(FooRT
->getKeyUnsafe(), K
)
421 << "Expected FooRT's ResourceKey for Foo here";
422 SRM
.recordResource(K
, 1);
425 EXPECT_EQ(SRM
.getRecordedResources().size(), 1U)
426 << "Expected one recorded resource here";
427 EXPECT_EQ(SRM
.getRecordedResources()[FooRT
->getKeyUnsafe()], 1U)
428 << "Expected Resource value for FooRT to be '1' here";
430 FooRT
->transferTo(*BarRT
);
432 EXPECT_TRUE(ResourceManagerGotTransfer
)
433 << "Expected resource manager to receive handleTransferResources call";
435 cantFail(FooMR
->withResourceKeyDo([&](ResourceKey K
) {
436 EXPECT_EQ(BarRT
->getKeyUnsafe(), K
)
437 << "Expected BarRT's ResourceKey for Foo here";
438 SRM
.recordResource(K
, 1);
441 EXPECT_EQ(SRM
.getRecordedResources().size(), 1U)
442 << "Expected one recorded resource here";
443 EXPECT_EQ(SRM
.getRecordedResources().count(BarRT
->getKeyUnsafe()), 1U)
444 << "Expected RecordedResources to contain an entry for BarRT";
445 EXPECT_EQ(SRM
.getRecordedResources()[BarRT
->getKeyUnsafe()], 2U)
446 << "Expected Resource value for BarRT to be '2' here";
448 cantFail(FooMR
->notifyResolved({{Foo
, FooSym
}}));
449 cantFail(FooMR
->notifyEmitted({}));