Skip to content

Commit e65de89

Browse files
[SYCL] ProgramManager: Remove invalidated entries in NativePrograms before insert (#15973)
Fixes #14972 This commit erases existing entries with ur handle that is just created/returned by backend. All existing entries in NativePrograms are known to be invalid in this case. Could not erase them on UrProgramRelease call since we have no tracking of program handle references on SYCL RT level and it is not feasible to add it. Obtaining ref count from ur is not thread safe and not a feature to base product on. --------- Signed-off-by: Tikhomirova, Kseniya <[email protected]> Co-authored-by: Sergey Semenov <[email protected]>
1 parent 48bcaf0 commit e65de89

File tree

3 files changed

+83
-17
lines changed

3 files changed

+83
-17
lines changed

sycl/source/detail/program_manager/program_manager.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ std::pair<ur_program_handle_t, bool> ProgramManager::getOrCreateURProgram(
509509
const std::vector<const RTDeviceBinaryImage *> &AllImages,
510510
const context &Context, const std::vector<device> &Devices,
511511
const std::string &CompileAndLinkOptions, SerializedObj SpecConsts) {
512-
ur_program_handle_t NativePrg; // TODO: Or native?
512+
ur_program_handle_t NativePrg;
513513

514514
// Get binaries for each device (1:1 correpsondence with input Devices).
515515
auto Binaries = PersistentDeviceCodeCache::getItemFromDisc(
@@ -768,7 +768,8 @@ setSpecializationConstants(const std::shared_ptr<device_image_impl> &InputImpl,
768768
}
769769
}
770770

771-
static inline void CheckAndDecompressImage([[maybe_unused]] RTDeviceBinaryImage *Img) {
771+
static inline void
772+
CheckAndDecompressImage([[maybe_unused]] RTDeviceBinaryImage *Img) {
772773
#ifndef SYCL_RT_ZSTD_NOT_AVAIABLE
773774
if (auto CompImg = dynamic_cast<CompressedRTDeviceBinaryImage *>(Img))
774775
if (CompImg->IsCompressed())
@@ -913,6 +914,11 @@ ur_program_handle_t ProgramManager::getBuiltURProgram(
913914

914915
{
915916
std::lock_guard<std::mutex> Lock(MNativeProgramsMutex);
917+
// NativePrograms map does not intend to keep reference to program handle,
918+
// so keys in the map can be invalid (reference count went to zero and the
919+
// underlying program disposed of). Protecting from incorrect values by
920+
// removal of map entries with same handle (obviously invalid entries).
921+
std::ignore = NativePrograms.erase(BuiltProgram.get());
916922
for (const RTDeviceBinaryImage *Img : ImgWithDeps) {
917923
NativePrograms.insert({BuiltProgram.get(), Img});
918924
}
@@ -2747,6 +2753,11 @@ ProgramManager::link(const DevImgPlainWithDeps &ImgWithDeps,
27472753

27482754
{
27492755
std::lock_guard<std::mutex> Lock(MNativeProgramsMutex);
2756+
// NativePrograms map does not intend to keep reference to program handle,
2757+
// so keys in the map can be invalid (reference count went to zero and the
2758+
// underlying program disposed of). Protecting from incorrect values by
2759+
// removal of map entries with same handle (obviously invalid entries).
2760+
std::ignore = NativePrograms.erase(LinkedProg);
27502761
for (const device_image_plain &Img : ImgWithDeps) {
27512762
NativePrograms.insert(
27522763
{LinkedProg, getSyclObjImpl(Img)->get_bin_image_ref()});

sycl/source/detail/program_manager/program_manager.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ extern "C" __SYCL_EXPORT void __sycl_unregister_lib(sycl_device_binaries desc);
4646

4747
// +++ }
4848

49+
// For testing purposes
50+
class ProgramManagerTest;
51+
4952
namespace sycl {
5053
inline namespace _V1 {
5154
class context;
@@ -494,6 +497,8 @@ class ProgramManager {
494497
using MaterializedEntries =
495498
std::map<std::vector<unsigned char>, ur_kernel_handle_t>;
496499
std::unordered_map<std::string, MaterializedEntries> m_MaterializedKernels;
500+
501+
friend class ::ProgramManagerTest;
497502
};
498503
} // namespace detail
499504
} // namespace _V1

sycl/unittests/program_manager/arg_mask/EliminatedArgMask.cpp

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,17 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9+
#include <detail/config.hpp>
910
#include <detail/handler_impl.hpp>
1011
#include <detail/kernel_bundle_impl.hpp>
12+
#include <detail/program_manager/program_manager.hpp>
1113
#include <detail/queue_impl.hpp>
1214
#include <detail/scheduler/commands.hpp>
1315
#include <sycl/sycl.hpp>
1416

1517
#include <helpers/MockDeviceImage.hpp>
1618
#include <helpers/MockKernelInfo.hpp>
19+
#include <helpers/ScopedEnvVar.hpp>
1720
#include <helpers/UrMock.hpp>
1821

1922
#include <gtest/gtest.h>
@@ -98,28 +101,17 @@ static sycl::unittest::MockDeviceImageArray<1> EAMImgArray{&EAMImg};
98101
static sycl::unittest::MockDeviceImageArray<1> EAM2ImgArray{&EAM2Img};
99102
static sycl::unittest::MockDeviceImageArray<1> EAM3ImgArray{&EAM3Img};
100103

101-
// ur_program_handle_t address is used as a key for ProgramManager::NativePrograms
102-
// storage. redefinedProgramLinkCommon makes ur_program_handle_t address equal to 0x1.
103-
// Make sure that size of Bin is different for device images used in these tests
104-
// and greater than 1.
104+
// ur_program_handle_t address is used as a key for
105+
// ProgramManager::NativePrograms storage. redefinedProgramLinkCommon makes
106+
// ur_program_handle_t address equal to 0x1. Make sure that size of Bin is
107+
// different for device images used in these tests and greater than 1.
105108
inline ur_result_t redefinedProgramCreateEAM(void *pParams) {
106109
auto params = *static_cast<ur_program_create_with_il_params_t *>(pParams);
107110
static size_t UrProgramAddr = 2;
108111
**params.pphProgram = reinterpret_cast<ur_program_handle_t>(UrProgramAddr++);
109112
return UR_RESULT_SUCCESS;
110113
}
111114

112-
mock::dummy_handle_t_ FixedHandle;
113-
inline ur_result_t setFixedProgramPtr(void *pParams) {
114-
auto params = *static_cast<ur_program_create_with_il_params_t *>(pParams);
115-
**params.pphProgram = reinterpret_cast<ur_program_handle_t>(&FixedHandle);
116-
return UR_RESULT_SUCCESS;
117-
}
118-
inline ur_result_t releaseFixedProgramPtr(void *pParams) {
119-
// Do nothing
120-
return UR_RESULT_SUCCESS;
121-
}
122-
123115
class MockHandler : public sycl::handler {
124116

125117
public:
@@ -218,6 +210,53 @@ TEST(EliminatedArgMask, KernelBundleWith2Kernels) {
218210
EXPECT_EQ(*EliminatedArgMask, ExpElimArgMask);
219211
}
220212

213+
std::vector<std::unique_ptr<mock::dummy_handle_t_>> UsedProgramHandles;
214+
std::vector<std::unique_ptr<mock::dummy_handle_t_>> ProgramHandlesToReuse;
215+
inline ur_result_t setFixedProgramPtr(void *pParams) {
216+
auto params = *static_cast<ur_program_create_with_il_params_t *>(pParams);
217+
if (!ProgramHandlesToReuse.empty()) {
218+
auto it = ProgramHandlesToReuse.begin() + 1;
219+
std::move(ProgramHandlesToReuse.begin(), it,
220+
std::back_inserter(UsedProgramHandles));
221+
ProgramHandlesToReuse.erase(ProgramHandlesToReuse.begin(), it);
222+
} else
223+
UsedProgramHandles.push_back(
224+
std::make_unique<mock::dummy_handle_t_>(sizeof(unsigned)));
225+
**params.pphProgram =
226+
reinterpret_cast<ur_program_handle_t>(UsedProgramHandles.back().get());
227+
return UR_RESULT_SUCCESS;
228+
}
229+
inline ur_result_t releaseFixedProgramPtr(void *pParams) {
230+
auto params = *static_cast<ur_program_release_params_t *>(pParams);
231+
{
232+
auto it = std::find_if(
233+
UsedProgramHandles.begin(), UsedProgramHandles.end(),
234+
[&params](const std::unique_ptr<mock::dummy_handle_t_> &item) {
235+
return reinterpret_cast<ur_program_handle_t>(item.get()) ==
236+
*params.phProgram;
237+
});
238+
if (it == UsedProgramHandles.end())
239+
return UR_RESULT_SUCCESS;
240+
std::move(it, it + 1, std::back_inserter(ProgramHandlesToReuse));
241+
UsedProgramHandles.erase(it, it + 1);
242+
}
243+
return UR_RESULT_SUCCESS;
244+
}
245+
246+
inline ur_result_t customProgramRetain(void *pParams) {
247+
// do nothing
248+
return UR_RESULT_SUCCESS;
249+
}
250+
251+
class ProgramManagerTest {
252+
public:
253+
static std::unordered_multimap<ur_program_handle_t,
254+
const sycl::detail::RTDeviceBinaryImage *> &
255+
getNativePrograms() {
256+
return sycl::detail::ProgramManager::getInstance().NativePrograms;
257+
}
258+
};
259+
221260
// It's possible for the same handle to be reused for multiple distinct programs
222261
// This can happen if a program is released (freeing underlying memory) and then
223262
// a new program happens to get given that same memory for its handle.
@@ -227,6 +266,7 @@ TEST(EliminatedArgMask, KernelBundleWith2Kernels) {
227266
TEST(EliminatedArgMask, ReuseOfHandleValues) {
228267
sycl::detail::ProgramManager &PM =
229268
sycl::detail::ProgramManager::getInstance();
269+
auto &NativePrograms = ProgramManagerTest::getNativePrograms();
230270

231271
ur_program_handle_t ProgBefore = nullptr;
232272
ur_program_handle_t ProgAfter = nullptr;
@@ -238,6 +278,8 @@ TEST(EliminatedArgMask, ReuseOfHandleValues) {
238278
&setFixedProgramPtr);
239279
mock::getCallbacks().set_replace_callback("urProgramRelease",
240280
&releaseFixedProgramPtr);
281+
mock::getCallbacks().set_replace_callback("urProgramRetain",
282+
&customProgramRetain);
241283

242284
const sycl::device Dev = Plt.get_devices()[0];
243285
sycl::queue Queue{Dev};
@@ -247,8 +289,12 @@ TEST(EliminatedArgMask, ReuseOfHandleValues) {
247289
auto Mask = PM.getEliminatedKernelArgMask(ProgBefore, Name);
248290
EXPECT_NE(Mask, nullptr);
249291
EXPECT_EQ(Mask->at(0), 1);
292+
EXPECT_EQ(UsedProgramHandles.size(), 1u);
293+
EXPECT_EQ(NativePrograms.count(ProgBefore), 1u);
250294
}
251295

296+
EXPECT_EQ(UsedProgramHandles.size(), 0u);
297+
252298
{
253299
auto Name = sycl::detail::KernelInfo<EAMTestKernel3>::getName();
254300
sycl::unittest::UrMock<> Mock;
@@ -257,6 +303,8 @@ TEST(EliminatedArgMask, ReuseOfHandleValues) {
257303
&setFixedProgramPtr);
258304
mock::getCallbacks().set_replace_callback("urProgramRelease",
259305
&releaseFixedProgramPtr);
306+
mock::getCallbacks().set_replace_callback("urProgramRetain",
307+
&customProgramRetain);
260308

261309
const sycl::device Dev = Plt.get_devices()[0];
262310
sycl::queue Queue{Dev};
@@ -266,6 +314,8 @@ TEST(EliminatedArgMask, ReuseOfHandleValues) {
266314
auto Mask = PM.getEliminatedKernelArgMask(ProgAfter, Name);
267315
EXPECT_NE(Mask, nullptr);
268316
EXPECT_EQ(Mask->at(0), 0);
317+
EXPECT_EQ(UsedProgramHandles.size(), 1u);
318+
EXPECT_EQ(NativePrograms.count(ProgBefore), 1u);
269319
}
270320

271321
// Verify that the test is behaving correctly and that the pointer is being

0 commit comments

Comments
 (0)