Ensure OpenCL runtimes are initialized first

The OpenCL API Specification states:

> The behavior of OpenCL API functions called from global constructors
> or destructors is therefore implementation-defined.

This patch improves compatibility with OpenCL runtimes that use static
objects to hold their internal state.

Change-Id: I850be378e9c6f0b5aa8db926fe0c62833a936724
Signed-off-by: Marco Antognini <marco.antognini@arm.com>
Reviewed-on: https://review.mlplatform.org/c/ml/ComputeLibrary/+/5383
Reviewed-by: Georgios Pinitas <georgios.pinitas@arm.com>
Comments-Addressed: Sheri Zhang <sheri.zhang@arm.com>
Tested-by: Arm Jenkins <bsgcomp@arm.com>
diff --git a/src/core/CL/OpenCL.cpp b/src/core/CL/OpenCL.cpp
index aff6285..dd9960a 100644
--- a/src/core/CL/OpenCL.cpp
+++ b/src/core/CL/OpenCL.cpp
@@ -152,6 +152,23 @@
 bool opencl_is_available()
 {
     CLSymbols::get().load_default();
+
+    // Using static objects that rely on OpenCL in their constructor or
+    // destructor is implementation defined according to the OpenCL API
+    // Specification. These objects include CLScheduler.
+    //
+    // For compatibility with OpenCL runtimes that also use static objects to
+    // hold their state, we call a harmless OpenCL function (clGetPlatformIDs
+    // with invalid parameters must result in CL_INVALID_VALUE) to ensure the
+    // runtimes have a chance to initialize their static objects first. Thanks
+    // to C++11 rules about normal program termination (cf [basic.start]), this
+    // ensures their static objects are destroyed last, i.e. after the
+    // singleton CLScheduler is destroyed.
+    //
+    // When OpenCL is not available, this call results in CL_OUT_OF_RESOURCES,
+    // which is equally harmless.
+    (void)clGetPlatformIDs(0, nullptr, nullptr);
+
     return CLSymbols::get().clBuildProgram_ptr != nullptr;
 }
 } // namespace arm_compute