I’m not sure if this is a bug in the OpenCL C++ bindinsg or in the Apple implementation I’m using it on. Currently, this code sometimes crashes (race condition) at program shutdown:
That is, the copy construction of a cl::Context is properly calling clRetainContext to increment the reference count, but the queue.getInfo<CL_QUEUE_CONTEXT>() call is not. This causes an extra clReleaseContext call to occur, which crashes the Apple implementation sometimes (which it shouldn’t, but that’s a separate issue).
So should a clGetInfo call that returns another cl_ type automatically increment that reference. I haven’t found anything in the standard stating one way or the other.
I’ll start working on a fix then to submit. It doesn’t look simple since the GetInfoHelper is used on both POD and the cl_* types. Also, retain and release are protected methods.
FYI, I added the following two more printfs about the reference counts. It appears creating the CommandQueue increments the reference count of the context. Does the AMD implementation have an alternative mechanism to keep the cl_context around if there’s a command queue that needs it?
I think the issue is the use of the vector assign operator in get*Info helper function. One possible fix is to replace this with a for loop and construct from the wrapper, forcing a increment in the ref count. I have not had time to try it yet.
The problem isn’t limited to the getInfo overload for vector returns. As the problem occurred in my example for with the queue.getInfo<CL_QUEUE_CONTEXT>() template method which returns a straight cl::Context. This method eventually drops into this template helper struct:
What is needed is a specialization of this for anything that inherits from detail::Wrapper, something like the following:
// Specialized for any of the Wrapper classes
template <typename Func, typename T>
struct GetInfoHelper<Func, Wrapper<T> >
{
static cl_int
get(Func f, cl_uint name, Wrapper<T>* param)
{
cl_uint err = f(name, sizeof(Wrapper<T>), param, NULL);
if (err != CL_SUCCESS) {
return err;
}
return param->retain();
}
};
Two problems with this though:
[ul]
I can’t get the compiler to recognize that it should fall into that specialization when getting passed a cl::Context object.
It calls the retain method of detail::Wrapper, which is currently protected, and probably should remain so.
[/ul]
I don’t like this for two reasons. It will require a lot of code duplication, though I can refactor the GetInfoHelper specialization into a macro. The tricky part was that the specialization has to be declared after cl::Context was fully specified because of the sizeof operator being used.
The next reason I don’t like this is I had to make the object_ member of detail::Wrapper public. This seemed slightly less egregious then making the retain method public. Though what would you think about making a cl::Context (and friends) constructor that simply takes the cl_type it is associated with and calls retain on it? This adds a constructor to the public interface, though theoretically this could have utility outside of the C++ bindings. For example, if using another third party library returns cl_* types, then the C++ bindings could be used to wrap around those returned values (properly calling the clRetain* function).