OpenCL: Implement clSetContextDestructorCallback

Adding support for clSetContextDestructorCallback.
This will register a destructor callback function with a context.
When context destructor called, it will run the callbacks functions.

Bug: angleproject:491161377
Tests-Passing: ocl_cts.test_api.context_destructor_callback
Change-Id: I33605f12450e23fc86043eb257d54ec6369d098e
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/7657410
Commit-Queue: Austin Annestrand <a.annestrand@samsung.com>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
diff --git a/include/angle_cl.h b/include/angle_cl.h
index 06c1f69..50302d8 100644
--- a/include/angle_cl.h
+++ b/include/angle_cl.h
@@ -30,6 +30,7 @@
                                            size_t cb,
                                            void *user_data);
 
+using ContextCB = void(CL_CALLBACK *)(cl_context context, void *user_data);
 using MemoryCB  = void(CL_CALLBACK *)(cl_mem memobj, void *user_data);
 using ProgramCB = void(CL_CALLBACK *)(cl_program program, void *user_data);
 using EventCB   = void(CL_CALLBACK *)(cl_event event, cl_int event_command_status, void *user_data);
diff --git a/src/libANGLE/CLContext.cpp b/src/libANGLE/CLContext.cpp
index 3b3900c..08c9683 100644
--- a/src/libANGLE/CLContext.cpp
+++ b/src/libANGLE/CLContext.cpp
@@ -85,6 +85,12 @@
     return angle::Result::Continue;
 }
 
+angle::Result Context::setDestructorCallback(ContextCB pfnNotify, void *userData)
+{
+    mDestructorCallbacks->emplace(pfnNotify, userData);
+    return angle::Result::Continue;
+}
+
 cl_command_queue Context::createCommandQueueWithProperties(cl_device_id device,
                                                            const cl_queue_properties *properties)
 {
@@ -365,7 +371,20 @@
     return mImpl->waitForEvents(Event::Cast(numEvents, eventList));
 }
 
-Context::~Context() = default;
+Context::~Context()
+{
+    // TODO(aannestrand): make dtor callback handling a "helper" util and reuse for CLMemory dtor
+    // http://anglebug.com/496408119
+    std::stack<CallbackData> callbacks;
+    mDestructorCallbacks->swap(callbacks);
+    while (!callbacks.empty())
+    {
+        const ContextCB callback = callbacks.top().first;
+        void *const userData     = callbacks.top().second;
+        callbacks.pop();
+        callback(this, userData);
+    }
+}
 
 void Context::ErrorCallback(const char *errinfo, const void *privateInfo, size_t cb, void *userData)
 {
diff --git a/src/libANGLE/CLContext.h b/src/libANGLE/CLContext.h
index 2d7cbf4..ca4e1ba 100644
--- a/src/libANGLE/CLContext.h
+++ b/src/libANGLE/CLContext.h
@@ -14,6 +14,8 @@
 #include "libANGLE/CLPlatform.h"
 #include "libANGLE/renderer/CLContextImpl.h"
 
+#include <stack>
+
 namespace cl
 {
 
@@ -29,6 +31,8 @@
                           void *value,
                           size_t *valueSizeRet) const;
 
+    angle::Result setDestructorCallback(ContextCB pfnNotify, void *userData);
+
     cl_command_queue createCommandQueueWithProperties(cl_device_id device,
                                                       const cl_queue_properties *properties);
 
@@ -125,6 +129,8 @@
                                                         const void *handle);
 
   private:
+    using CallbackData = std::pair<ContextCB, void *>;
+
     Context(Platform &platform,
             PropArray &&properties,
             DevicePtrs &&devices,
@@ -146,6 +152,8 @@
     rx::CLContextImpl::Ptr mImpl;
     DevicePtrs mDevices;
 
+    angle::SynchronizedValue<std::stack<CallbackData>> mDestructorCallbacks;
+
     friend class Object;
 };
 
diff --git a/src/libGLESv2/cl_stubs.cpp b/src/libGLESv2/cl_stubs.cpp
index d5754ab..be666c0 100644
--- a/src/libGLESv2/cl_stubs.cpp
+++ b/src/libGLESv2/cl_stubs.cpp
@@ -233,8 +233,7 @@
                                                                   void *user_data),
                                     void *user_data)
 {
-    WARN_NOT_SUPPORTED(SetContextDestructorCallback);
-    return CL_INVALID_OPERATION;
+    CL_RETURN_ERROR(context->cast<Context>().setDestructorCallback(pfn_notify, user_data));
 }
 
 cl_command_queue CreateCommandQueueWithProperties(cl_context context,