Skip to content

Commit ccefc93

Browse files
[SYCL] Fix unreleased kernels obtained with program::get_kernel() (#1804)
Avoid unconditional retaining of internally created native kernels in kernel_impl constructors, retain them in the caller when needed instead. Signed-off-by: Sergey Semenov <[email protected]>
1 parent 9fd6850 commit ccefc93

File tree

5 files changed

+111
-3
lines changed

5 files changed

+111
-3
lines changed

sycl/source/detail/kernel_impl.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,12 @@ namespace detail {
2222
kernel_impl::kernel_impl(RT::PiKernel Kernel, ContextImplPtr Context)
2323
: kernel_impl(Kernel, Context,
2424
std::make_shared<program_impl>(Context, Kernel),
25-
/*IsCreatedFromSource*/ true) {}
25+
/*IsCreatedFromSource*/ true) {
26+
// This constructor is only called in the interoperability kernel constructor.
27+
// Let the runtime caller handle native kernel retaining in other cases if
28+
// it's needed.
29+
getPlugin().call<PiApiKind::piKernelRetain>(MKernel);
30+
}
2631

2732
kernel_impl::kernel_impl(RT::PiKernel Kernel, ContextImplPtr ContextImpl,
2833
ProgramImplPtr ProgramImpl,
@@ -39,7 +44,6 @@ kernel_impl::kernel_impl(RT::PiKernel Kernel, ContextImplPtr ContextImpl,
3944
throw cl::sycl::invalid_parameter_error(
4045
"Input context must be the same as the context of cl_kernel",
4146
PI_INVALID_CONTEXT);
42-
getPlugin().call<PiApiKind::piKernelRetain>(MKernel);
4347
}
4448

4549
kernel_impl::kernel_impl(ContextImplPtr Context,

sycl/source/detail/program_impl.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ RT::PiKernel program_impl::get_pi_kernel(const string_class &KernelName) const {
398398
if (is_cacheable()) {
399399
Kernel = ProgramManager::getInstance().getOrCreateKernel(
400400
MProgramModuleHandle, get_context(), KernelName, this);
401+
getPlugin().call<PiApiKind::piKernelRetain>(Kernel);
401402
} else {
402403
const detail::plugin &Plugin = getPlugin();
403404
RT::PiResult Err = Plugin.call_nocheck<PiApiKind::piKernelCreate>(

sycl/unittests/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,6 @@ endfunction()
5656

5757
add_subdirectory(misc)
5858
add_subdirectory(pi)
59-
add_subdirectory(thread_safety)
59+
add_subdirectory(program)
6060
add_subdirectory(scheduler)
61+
add_subdirectory(thread_safety)

sycl/unittests/program/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
add_sycl_unittest(ProgramTests OBJECT
2+
KernelRelease.cpp
3+
)
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
//==----------- KernelRelease.cpp --- kernel release unit test -------------==//
2+
//
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
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include <CL/sycl.hpp>
10+
#include <detail/context_impl.hpp>
11+
#include <gtest/gtest.h>
12+
#include <helpers/PiMock.hpp>
13+
14+
#include <iostream>
15+
#include <memory>
16+
17+
using namespace cl::sycl;
18+
19+
struct TestCtx {
20+
TestCtx(context &Ctx) : Ctx{Ctx} {};
21+
22+
context &Ctx;
23+
int KernelReferenceCount = 0;
24+
};
25+
26+
std::unique_ptr<TestCtx> TestContext;
27+
28+
pi_result redefinedProgramCreateWithSource(pi_context context, pi_uint32 count,
29+
const char **strings,
30+
const size_t *lengths,
31+
pi_program *ret_program) {
32+
return PI_SUCCESS;
33+
}
34+
35+
pi_result
36+
redefinedProgramBuild(pi_program program, pi_uint32 num_devices,
37+
const pi_device *device_list, const char *options,
38+
void (*pfn_notify)(pi_program program, void *user_data),
39+
void *user_data) {
40+
return PI_SUCCESS;
41+
}
42+
43+
pi_result redefinedKernelCreate(pi_program program, const char *kernel_name,
44+
pi_kernel *ret_kernel) {
45+
TestContext->KernelReferenceCount = 1;
46+
return PI_SUCCESS;
47+
}
48+
49+
pi_result redefinedKernelRetain(pi_kernel kernel) {
50+
++TestContext->KernelReferenceCount;
51+
return PI_SUCCESS;
52+
}
53+
54+
pi_result redefinedKernelRelease(pi_kernel kernel) {
55+
--TestContext->KernelReferenceCount;
56+
return PI_SUCCESS;
57+
}
58+
59+
pi_result redefinedKernelGetInfo(pi_kernel kernel, pi_kernel_info param_name,
60+
size_t param_value_size, void *param_value,
61+
size_t *param_value_size_ret) {
62+
EXPECT_EQ(param_name, PI_KERNEL_INFO_CONTEXT)
63+
<< "Unexpected kernel info requested";
64+
auto *Result = reinterpret_cast<RT::PiContext *>(param_value);
65+
RT::PiContext PiCtx =
66+
detail::getSyclObjImpl(TestContext->Ctx)->getHandleRef();
67+
*Result = PiCtx;
68+
return PI_SUCCESS;
69+
}
70+
71+
TEST(KernelReleaseTest, GetKernelRelease) {
72+
unittest::PiMock Mock;
73+
platform Plt = Mock.getPlatform();
74+
if (Plt.is_host()) {
75+
std::cerr << "The program/kernel methods are mostly no-op on the host "
76+
"device, the test is not run."
77+
<< std::endl;
78+
return;
79+
}
80+
81+
Mock.redefine<detail::PiApiKind::piclProgramCreateWithSource>(
82+
redefinedProgramCreateWithSource);
83+
Mock.redefine<detail::PiApiKind::piProgramBuild>(redefinedProgramBuild);
84+
Mock.redefine<detail::PiApiKind::piKernelCreate>(redefinedKernelCreate);
85+
Mock.redefine<detail::PiApiKind::piKernelRetain>(redefinedKernelRetain);
86+
Mock.redefine<detail::PiApiKind::piKernelRelease>(redefinedKernelRelease);
87+
Mock.redefine<detail::PiApiKind::piKernelGetInfo>(redefinedKernelGetInfo);
88+
89+
context Ctx{Plt};
90+
TestContext.reset(new TestCtx(Ctx));
91+
92+
program Prg{Ctx};
93+
Prg.build_with_source("");
94+
95+
{ kernel Krnl = Prg.get_kernel(""); }
96+
97+
ASSERT_EQ(TestContext->KernelReferenceCount, 0)
98+
<< "Reference count not equal to 0 after kernel destruction";
99+
}

0 commit comments

Comments
 (0)