Skip to content

Commit 804851e

Browse files
Merge pull request #1991 from aarongreig/aaron/clearExtFuncPtrCache
Clear OpenCL function pointer cache when context is released.
2 parents 0702e7b + 58ef967 commit 804851e

File tree

3 files changed

+74
-38
lines changed

3 files changed

+74
-38
lines changed

source/adapters/opencl/common.hpp

Lines changed: 24 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -319,45 +319,33 @@ cl_int(CL_API_CALL *)(cl_command_buffer_khr command_buffer,
319319
template <typename T> struct FuncPtrCache {
320320
std::map<cl_context, T> Map;
321321
std::mutex Mutex;
322+
323+
void clear(cl_context context) {
324+
std::lock_guard<std::mutex> CacheLock{Mutex};
325+
Map.erase(context);
326+
}
322327
};
323328

324-
// FIXME: There's currently no mechanism for cleaning up this cache, meaning
325-
// that it is invalidated whenever a context is destroyed. This could lead to
326-
// reusing an invalid function pointer if another context happens to have the
327-
// same native handle.
328329
struct ExtFuncPtrCacheT {
329-
FuncPtrCache<clHostMemAllocINTEL_fn> clHostMemAllocINTELCache;
330-
FuncPtrCache<clDeviceMemAllocINTEL_fn> clDeviceMemAllocINTELCache;
331-
FuncPtrCache<clSharedMemAllocINTEL_fn> clSharedMemAllocINTELCache;
332-
FuncPtrCache<clGetDeviceFunctionPointer_fn> clGetDeviceFunctionPointerCache;
333-
FuncPtrCache<clGetDeviceGlobalVariablePointer_fn>
334-
clGetDeviceGlobalVariablePointerCache;
335-
FuncPtrCache<clCreateBufferWithPropertiesINTEL_fn>
336-
clCreateBufferWithPropertiesINTELCache;
337-
FuncPtrCache<clMemBlockingFreeINTEL_fn> clMemBlockingFreeINTELCache;
338-
FuncPtrCache<clSetKernelArgMemPointerINTEL_fn>
339-
clSetKernelArgMemPointerINTELCache;
340-
FuncPtrCache<clEnqueueMemFillINTEL_fn> clEnqueueMemFillINTELCache;
341-
FuncPtrCache<clEnqueueMemcpyINTEL_fn> clEnqueueMemcpyINTELCache;
342-
FuncPtrCache<clGetMemAllocInfoINTEL_fn> clGetMemAllocInfoINTELCache;
343-
FuncPtrCache<clEnqueueWriteGlobalVariable_fn>
344-
clEnqueueWriteGlobalVariableCache;
345-
FuncPtrCache<clEnqueueReadGlobalVariable_fn> clEnqueueReadGlobalVariableCache;
346-
FuncPtrCache<clEnqueueReadHostPipeINTEL_fn> clEnqueueReadHostPipeINTELCache;
347-
FuncPtrCache<clEnqueueWriteHostPipeINTEL_fn> clEnqueueWriteHostPipeINTELCache;
348-
FuncPtrCache<clSetProgramSpecializationConstant_fn>
349-
clSetProgramSpecializationConstantCache;
350-
FuncPtrCache<clCreateCommandBufferKHR_fn> clCreateCommandBufferKHRCache;
351-
FuncPtrCache<clRetainCommandBufferKHR_fn> clRetainCommandBufferKHRCache;
352-
FuncPtrCache<clReleaseCommandBufferKHR_fn> clReleaseCommandBufferKHRCache;
353-
FuncPtrCache<clFinalizeCommandBufferKHR_fn> clFinalizeCommandBufferKHRCache;
354-
FuncPtrCache<clCommandNDRangeKernelKHR_fn> clCommandNDRangeKernelKHRCache;
355-
FuncPtrCache<clCommandCopyBufferKHR_fn> clCommandCopyBufferKHRCache;
356-
FuncPtrCache<clCommandCopyBufferRectKHR_fn> clCommandCopyBufferRectKHRCache;
357-
FuncPtrCache<clCommandFillBufferKHR_fn> clCommandFillBufferKHRCache;
358-
FuncPtrCache<clEnqueueCommandBufferKHR_fn> clEnqueueCommandBufferKHRCache;
359-
FuncPtrCache<clGetCommandBufferInfoKHR_fn> clGetCommandBufferInfoKHRCache;
360-
FuncPtrCache<clUpdateMutableCommandsKHR_fn> clUpdateMutableCommandsKHRCache;
330+
#define CL_EXTENSION_FUNC(func) FuncPtrCache<func##_fn> func##Cache;
331+
332+
#include "extension_functions.def"
333+
334+
#undef CL_EXTENSION_FUNC
335+
336+
// If a context stored in the current caching mechanism is destroyed by the
337+
// CL driver all of its function pointers are invalidated. This can lead to a
338+
// pathological case where a subsequently created context gets returned with
339+
// a coincidentally identical handle to the destroyed one and ends up being
340+
// used to retrieve bad function pointers. To avoid this we clear the cache
341+
// when contexts are released.
342+
void clearCache(cl_context context) {
343+
#define CL_EXTENSION_FUNC(func) func##Cache.clear(context);
344+
345+
#include "extension_functions.def"
346+
347+
#undef CL_EXTENSION_FUNC
348+
}
361349
};
362350
// A raw pointer is used here since the lifetime of this map has to be tied to
363351
// piTeardown to avoid issues with static destruction order (a user application

source/adapters/opencl/context.cpp

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,30 @@ urContextGetInfo(ur_context_handle_t hContext, ur_context_info_t propName,
113113

114114
UR_APIEXPORT ur_result_t UR_APICALL
115115
urContextRelease(ur_context_handle_t hContext) {
116+
// If we're reasonably sure this context is about to be detroyed we should
117+
// clear the ext function pointer cache. This isn't foolproof sadly but it
118+
// should drastically reduce the chances of the pathological case described
119+
// in the comments in common.hpp.
120+
static std::mutex contextReleaseMutex;
121+
auto clContext = cl_adapter::cast<cl_context>(hContext);
116122

117-
cl_int Ret = clReleaseContext(cl_adapter::cast<cl_context>(hContext));
118-
return mapCLErrorToUR(Ret);
123+
{
124+
std::lock_guard<std::mutex> lock(contextReleaseMutex);
125+
size_t refCount = 0;
126+
CL_RETURN_ON_FAILURE(clGetContextInfo(clContext, CL_CONTEXT_REFERENCE_COUNT,
127+
sizeof(size_t), &refCount, nullptr));
128+
129+
// ExtFuncPtrCache is destroyed in an atexit() callback, so it doesn't
130+
// necessarily outlive the adapter (or all the contexts).
131+
if (refCount == 1 && cl_ext::ExtFuncPtrCache) {
132+
cl_ext::ExtFuncPtrCache->clearCache(clContext);
133+
}
134+
}
135+
136+
CL_RETURN_ON_FAILURE(
137+
clReleaseContext(cl_adapter::cast<cl_context>(hContext)));
138+
139+
return UR_RESULT_SUCCESS;
119140
}
120141

121142
UR_APIEXPORT ur_result_t UR_APICALL
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
CL_EXTENSION_FUNC(clHostMemAllocINTEL)
2+
CL_EXTENSION_FUNC(clDeviceMemAllocINTEL)
3+
CL_EXTENSION_FUNC(clSharedMemAllocINTEL)
4+
CL_EXTENSION_FUNC(clGetDeviceFunctionPointer)
5+
CL_EXTENSION_FUNC(clGetDeviceGlobalVariablePointer)
6+
CL_EXTENSION_FUNC(clCreateBufferWithPropertiesINTEL)
7+
CL_EXTENSION_FUNC(clMemBlockingFreeINTEL)
8+
CL_EXTENSION_FUNC(clSetKernelArgMemPointerINTEL)
9+
CL_EXTENSION_FUNC(clEnqueueMemFillINTEL)
10+
CL_EXTENSION_FUNC(clEnqueueMemcpyINTEL)
11+
CL_EXTENSION_FUNC(clGetMemAllocInfoINTEL)
12+
CL_EXTENSION_FUNC(clEnqueueWriteGlobalVariable)
13+
CL_EXTENSION_FUNC(clEnqueueReadGlobalVariable)
14+
CL_EXTENSION_FUNC(clEnqueueReadHostPipeINTEL)
15+
CL_EXTENSION_FUNC(clEnqueueWriteHostPipeINTEL)
16+
CL_EXTENSION_FUNC(clSetProgramSpecializationConstant)
17+
CL_EXTENSION_FUNC(clCreateCommandBufferKHR)
18+
CL_EXTENSION_FUNC(clRetainCommandBufferKHR)
19+
CL_EXTENSION_FUNC(clReleaseCommandBufferKHR)
20+
CL_EXTENSION_FUNC(clFinalizeCommandBufferKHR)
21+
CL_EXTENSION_FUNC(clCommandNDRangeKernelKHR)
22+
CL_EXTENSION_FUNC(clCommandCopyBufferKHR)
23+
CL_EXTENSION_FUNC(clCommandCopyBufferRectKHR)
24+
CL_EXTENSION_FUNC(clCommandFillBufferKHR)
25+
CL_EXTENSION_FUNC(clEnqueueCommandBufferKHR)
26+
CL_EXTENSION_FUNC(clGetCommandBufferInfoKHR)
27+
CL_EXTENSION_FUNC(clUpdateMutableCommandsKHR)

0 commit comments

Comments
 (0)