diff --git a/test_conformance/extensions/cl_khr_semaphore/CMakeLists.txt b/test_conformance/extensions/cl_khr_semaphore/CMakeLists.txt index 824784a135..5618ebd640 100644 --- a/test_conformance/extensions/cl_khr_semaphore/CMakeLists.txt +++ b/test_conformance/extensions/cl_khr_semaphore/CMakeLists.txt @@ -3,6 +3,8 @@ set(MODULE_NAME CL_KHR_SEMAPHORE) set(${MODULE_NAME}_SOURCES main.cpp test_semaphores.cpp + test_semaphores_negative_wait.cpp + semaphore_base.h ) include(../../CMakeCommon.txt) diff --git a/test_conformance/extensions/cl_khr_semaphore/main.cpp b/test_conformance/extensions/cl_khr_semaphore/main.cpp index 0ae7206a0d..dc360ab6b9 100644 --- a/test_conformance/extensions/cl_khr_semaphore/main.cpp +++ b/test_conformance/extensions/cl_khr_semaphore/main.cpp @@ -35,6 +35,15 @@ test_definition test_list[] = { ADD_TEST_VERSION(semaphores_multi_wait, Version(1, 2)), ADD_TEST_VERSION(semaphores_queries, Version(1, 2)), ADD_TEST_VERSION(semaphores_import_export_fd, Version(1, 2)), + ADD_TEST_VERSION(semaphores_negative_wait_invalid_command_queue, + Version(1, 2)), + ADD_TEST_VERSION(semaphores_negative_wait_invalid_value, Version(1, 2)), + ADD_TEST_VERSION(semaphores_negative_wait_invalid_semaphore, Version(1, 2)), + ADD_TEST_VERSION(semaphores_negative_wait_invalid_context, Version(1, 2)), + ADD_TEST_VERSION(semaphores_negative_wait_invalid_event_wait_list, + Version(1, 2)), + ADD_TEST_VERSION(semaphores_negative_wait_invalid_event_status, + Version(1, 2)), }; const int test_num = ARRAY_SIZE(test_list); diff --git a/test_conformance/extensions/cl_khr_semaphore/procs.h b/test_conformance/extensions/cl_khr_semaphore/procs.h index f7c1aaa301..9fb174583a 100644 --- a/test_conformance/extensions/cl_khr_semaphore/procs.h +++ b/test_conformance/extensions/cl_khr_semaphore/procs.h @@ -45,3 +45,23 @@ extern int test_semaphores_import_export_fd(cl_device_id deviceID, cl_context context, cl_command_queue queue, int num_elements); +extern int test_semaphores_negative_wait_invalid_command_queue( + cl_device_id device, cl_context context, cl_command_queue queue, + int num_elements); +extern int test_semaphores_negative_wait_invalid_value(cl_device_id device, + cl_context context, + cl_command_queue queue, + int num_elements); +extern int test_semaphores_negative_wait_invalid_semaphore( + cl_device_id device, cl_context context, cl_command_queue queue, + int num_elements); +extern int test_semaphores_negative_wait_invalid_context(cl_device_id device, + cl_context context, + cl_command_queue queue, + int num_elements); +extern int test_semaphores_negative_wait_invalid_event_wait_list( + cl_device_id device, cl_context context, cl_command_queue queue, + int num_elements); +extern int test_semaphores_negative_wait_invalid_event_status( + cl_device_id device, cl_context context, cl_command_queue queue, + int num_elements); diff --git a/test_conformance/extensions/cl_khr_semaphore/semaphore_base.h b/test_conformance/extensions/cl_khr_semaphore/semaphore_base.h new file mode 100644 index 0000000000..e50f33aedd --- /dev/null +++ b/test_conformance/extensions/cl_khr_semaphore/semaphore_base.h @@ -0,0 +1,202 @@ +// +// Copyright (c) 2024 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef CL_KHR_SEMAPHORE_BASE_H +#define CL_KHR_SEMAPHORE_BASE_H + +#include +#include "harness/deviceInfo.h" +#include "harness/testHarness.h" + +#include "harness/typeWrappers.h" + +struct SemaphoreBase +{ + SemaphoreBase(cl_device_id device): device(device) {} + + cl_int init_extension_functions() + { + cl_platform_id platform; + cl_int error = + clGetDeviceInfo(device, CL_DEVICE_PLATFORM, sizeof(cl_platform_id), + &platform, nullptr); + test_error(error, "clGetDeviceInfo for CL_DEVICE_PLATFORM failed"); + + // If it is supported get the addresses of all the APIs here. + // clang-format off +#define GET_EXTENSION_ADDRESS(FUNC) \ + FUNC = reinterpret_cast( \ + clGetExtensionFunctionAddressForPlatform(platform, #FUNC)); \ + if (FUNC == nullptr) \ + { \ + log_error("ERROR: clGetExtensionFunctionAddressForPlatform failed" \ + " with " #FUNC "\n"); \ + return TEST_FAIL; \ + } + // clang-format on + + GET_EXTENSION_ADDRESS(clCreateSemaphoreWithPropertiesKHR); + GET_EXTENSION_ADDRESS(clEnqueueSignalSemaphoresKHR); + GET_EXTENSION_ADDRESS(clEnqueueWaitSemaphoresKHR); + GET_EXTENSION_ADDRESS(clReleaseSemaphoreKHR); + GET_EXTENSION_ADDRESS(clGetSemaphoreInfoKHR); + GET_EXTENSION_ADDRESS(clRetainSemaphoreKHR); + GET_EXTENSION_ADDRESS(clGetSemaphoreHandleForTypeKHR); + +#undef GET_EXTENSION_ADDRESS + return CL_SUCCESS; + } + + clCreateSemaphoreWithPropertiesKHR_fn clCreateSemaphoreWithPropertiesKHR = + nullptr; + clEnqueueSignalSemaphoresKHR_fn clEnqueueSignalSemaphoresKHR = nullptr; + clEnqueueWaitSemaphoresKHR_fn clEnqueueWaitSemaphoresKHR = nullptr; + clReleaseSemaphoreKHR_fn clReleaseSemaphoreKHR = nullptr; + clGetSemaphoreInfoKHR_fn clGetSemaphoreInfoKHR = nullptr; + clRetainSemaphoreKHR_fn clRetainSemaphoreKHR = nullptr; + clGetSemaphoreHandleForTypeKHR_fn clGetSemaphoreHandleForTypeKHR = nullptr; + + cl_device_id device = nullptr; +}; + +// Wrapper class based off generic typeWrappers.h wrappers. However, because +// the release/retain functions are queried at runtime from the platform, +// rather than known at compile time we cannot link the instantiated template. +// Instead, pass an instance of `SemaphoreTestBase` on wrapper construction +// to access the release/retain functions. +class clSemaphoreWrapper { + cl_semaphore_khr object = nullptr; + + void retain() + { + if (!object) return; + + auto err = base->clRetainSemaphoreKHR(object); + if (err != CL_SUCCESS) + { + print_error(err, "clRetainCommandBufferKHR() failed"); + std::abort(); + } + } + + void release() + { + if (!object) return; + + auto err = base->clReleaseSemaphoreKHR(object); + if (err != CL_SUCCESS) + { + print_error(err, "clReleaseCommandBufferKHR() failed"); + std::abort(); + } + } + + // Used to access release/retain functions + SemaphoreBase *base; + +public: + // We always want to have base available to dereference + clSemaphoreWrapper() = delete; + + clSemaphoreWrapper(SemaphoreBase *base): base(base) {} + + // On assignment, assume the object has a refcount of one. + clSemaphoreWrapper &operator=(cl_semaphore_khr rhs) + { + reset(rhs); + return *this; + } + + // Copy semantics, increase retain count. + clSemaphoreWrapper(clSemaphoreWrapper const &w) { *this = w; } + clSemaphoreWrapper &operator=(clSemaphoreWrapper const &w) + { + reset(w.object); + retain(); + return *this; + } + + // Move semantics, directly take ownership. + clSemaphoreWrapper(clSemaphoreWrapper &&w) { *this = std::move(w); } + clSemaphoreWrapper &operator=(clSemaphoreWrapper &&w) + { + reset(w.object); + w.object = nullptr; + return *this; + } + + ~clSemaphoreWrapper() { reset(); } + + // Release the existing object, if any, and own the new one, if any. + void reset(cl_semaphore_khr new_object = nullptr) + { + release(); + object = new_object; + } + + operator cl_semaphore_khr() const { return object; } + operator const cl_semaphore_khr *() { return &object; } +}; + +struct SemaphoreTestBase : public SemaphoreBase +{ + SemaphoreTestBase(cl_device_id device, cl_context context, + cl_command_queue queue) + : SemaphoreBase(device), context(context), semaphore(this) + { + cl_int error = init_extension_functions(); + if (error != CL_SUCCESS) + throw std::runtime_error("init_extension_functions failed\n"); + + error = clRetainCommandQueue(queue); + if (error != CL_SUCCESS) + throw std::runtime_error("clRetainCommandQueue failed\n"); + this->queue = queue; + } + + virtual cl_int Run() = 0; + +protected: + cl_context context = nullptr; + clCommandQueueWrapper queue = nullptr; + clSemaphoreWrapper semaphore = nullptr; +}; + +template +int MakeAndRunTest(cl_device_id device, cl_context context, + cl_command_queue queue) +{ + if (!is_extension_available(device, "cl_khr_semaphore")) + { + log_info( + "Device does not support 'cl_khr_semaphore'. Skipping the test.\n"); + return TEST_SKIPPED_ITSELF; + } + + cl_int status = TEST_PASS; + try + { + auto test_fixture = T(device, context, queue); + status = test_fixture.Run(); + } catch (const std::runtime_error &e) + { + log_error("%s", e.what()); + return TEST_FAIL; + } + + return status; +} + +#endif // CL_KHR_SEMAPHORE_BASE_H diff --git a/test_conformance/extensions/cl_khr_semaphore/test_semaphores_negative_wait.cpp b/test_conformance/extensions/cl_khr_semaphore/test_semaphores_negative_wait.cpp new file mode 100644 index 0000000000..7fc429b76c --- /dev/null +++ b/test_conformance/extensions/cl_khr_semaphore/test_semaphores_negative_wait.cpp @@ -0,0 +1,395 @@ +// +// Copyright (c) 2024 The Khronos Group Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "semaphore_base.h" + +#include "harness/errorHelpers.h" +#include +#include +#include +#include + +namespace { + +// the device associated with command_queue is not same as one of the devices +// specified by CL_SEMAPHORE_DEVICE_HANDLE_LIST_KHR at the time of creating one +// or more of sema_objects. + +struct WaitInvalidCommandQueue : public SemaphoreTestBase +{ + WaitInvalidCommandQueue(cl_device_id device, cl_context context, + cl_command_queue queue) + : SemaphoreTestBase(device, context, queue) + {} + + cl_int Run() override + { + // Create semaphore + cl_semaphore_properties_khr sema_props[] = { + static_cast(CL_SEMAPHORE_TYPE_KHR), + static_cast( + CL_SEMAPHORE_TYPE_BINARY_KHR), + static_cast( + CL_SEMAPHORE_DEVICE_HANDLE_LIST_KHR), + (cl_semaphore_properties_khr)device, + CL_SEMAPHORE_DEVICE_HANDLE_LIST_END_KHR, + 0 + }; + + cl_int err = CL_SUCCESS; + semaphore = + clCreateSemaphoreWithPropertiesKHR(context, sema_props, &err); + test_error(err, "Could not create semaphore"); + + // find other device + cl_platform_id platform_id = 0; + // find out what platform the harness is using. + err = clGetDeviceInfo(device, CL_DEVICE_PLATFORM, + sizeof(cl_platform_id), &platform_id, nullptr); + test_error(err, "clGetDeviceInfo failed"); + + cl_uint num_platforms = 0; + err = clGetPlatformIDs(16, nullptr, &num_platforms); + test_error(err, "clGetPlatformIDs failed"); + + std::vector platforms(num_platforms); + + err = clGetPlatformIDs(num_platforms, platforms.data(), &num_platforms); + test_error(err, "clGetPlatformIDs failed"); + + cl_device_id device_sec = nullptr; + cl_uint num_devices = 0; + for (int p = 0; p < (int)num_platforms; p++) + { + if (platform_id == platforms[p]) continue; + + err = clGetDeviceIDs(platforms[p], CL_DEVICE_TYPE_ALL, 0, nullptr, + &num_devices); + test_error(err, "clGetDeviceIDs failed"); + + std::vector devices(num_devices); + err = clGetDeviceIDs(platforms[p], CL_DEVICE_TYPE_ALL, num_devices, + devices.data(), nullptr); + test_error(err, "clGetDeviceIDs failed"); + + device_sec = devices.front(); + break; + } + + if (device_sec == nullptr) + { + log_info("Can't find needed resources. Skipping the test.\n"); + return TEST_SKIPPED_ITSELF; + } + + // Create secondary context + clContextWrapper context_sec = + clCreateContext(0, 1, &device_sec, nullptr, nullptr, &err); + test_error(err, "Failed to create context"); + + // Create secondary queue + clCommandQueueWrapper queue_sec = + clCreateCommandQueue(context_sec, device_sec, 0, &err); + test_error(err, "Could not create command queue"); + + // Signal semaphore + err = clEnqueueSignalSemaphoresKHR(queue, 1, semaphore, nullptr, 0, + nullptr, nullptr); + test_error(err, "Could not signal semaphore"); + + // Wait semaphore + err = clEnqueueWaitSemaphoresKHR(queue_sec, 1, semaphore, nullptr, 0, + nullptr, nullptr); + test_failure_error(err, CL_INVALID_COMMAND_QUEUE, + "Unexpected clEnqueueWaitSemaphoresKHR return"); + + return TEST_PASS; + } +}; + + +// num_sema_objects is 0. + +struct WaitInvalidValue : public SemaphoreTestBase +{ + WaitInvalidValue(cl_device_id device, cl_context context, + cl_command_queue queue) + : SemaphoreTestBase(device, context, queue) + {} + + cl_int Run() override + { + // Wait semaphore + cl_int err = CL_SUCCESS; + err = clEnqueueWaitSemaphoresKHR(queue, 0, semaphore, nullptr, 0, + nullptr, nullptr); + test_failure_error(err, CL_INVALID_VALUE, + "Unexpected clEnqueueWaitSemaphoresKHR return"); + + return CL_SUCCESS; + } +}; + +// any of the semaphore objects specified by sema_objects is not valid. + +struct WaitInvalidSemaphore : public SemaphoreTestBase +{ + WaitInvalidSemaphore(cl_device_id device, cl_context context, + cl_command_queue queue) + : SemaphoreTestBase(device, context, queue) + {} + + cl_int Run() override + { + // Wait semaphore + cl_semaphore_khr sema_objects[] = { nullptr, nullptr, nullptr }; + cl_int err = CL_SUCCESS; + err = clEnqueueWaitSemaphoresKHR( + queue, sizeof(sema_objects) / sizeof(sema_objects[0]), sema_objects, + nullptr, 0, nullptr, nullptr); + test_failure_error(err, CL_INVALID_SEMAPHORE_KHR, + "Unexpected clEnqueueWaitSemaphoresKHR return"); + + return CL_SUCCESS; + } +}; + +// 1) the context associated with command_queue and any of the semaphore objects +// in sema_objects are not the same, or +// 2) the context associated with command_queue and that associated with events +// in event_wait_list are not the same. + +struct WaitInvalidContext : public SemaphoreTestBase +{ + WaitInvalidContext(cl_device_id device, cl_context context, + cl_command_queue queue) + : SemaphoreTestBase(device, context, queue) + {} + + cl_int Run() override + { + // Create semaphore + cl_semaphore_properties_khr sema_props[] = { + static_cast(CL_SEMAPHORE_TYPE_KHR), + static_cast( + CL_SEMAPHORE_TYPE_BINARY_KHR), + 0 + }; + + cl_int err = CL_SUCCESS; + semaphore = + clCreateSemaphoreWithPropertiesKHR(context, sema_props, &err); + test_error(err, "Could not create semaphore"); + + // Create secondary context + clContextWrapper context_sec = + clCreateContext(0, 1, &device, nullptr, nullptr, &err); + test_error(err, "Failed to create context"); + + // Create secondary queue + clCommandQueueWrapper queue_sec = + clCreateCommandQueue(context_sec, device, 0, &err); + test_error(err, "Could not create command queue"); + + // Signal semaphore + err = clEnqueueSignalSemaphoresKHR(queue, 1, semaphore, nullptr, 0, + nullptr, nullptr); + test_error(err, "Could not signal semaphore"); + + // (1) Wait semaphore + err = clEnqueueWaitSemaphoresKHR(queue_sec, 1, semaphore, nullptr, 0, + nullptr, nullptr); + test_failure_error(err, CL_INVALID_CONTEXT, + "Unexpected clEnqueueWaitSemaphoresKHR return"); + + // Create user event + clEventWrapper user_event = clCreateUserEvent(context_sec, &err); + test_error(err, "Could not create user event"); + + // (2) Wait semaphore + err = clEnqueueWaitSemaphoresKHR(queue, 1, semaphore, nullptr, 1, + &user_event, nullptr); + + cl_int signal_error = clSetUserEventStatus(user_event, CL_COMPLETE); + test_error(signal_error, "clSetUserEventStatus failed"); + + test_failure_error(err, CL_INVALID_CONTEXT, + "Unexpected clEnqueueWaitSemaphoresKHR return"); + + return TEST_PASS; + } +}; + +// (1) event_wait_list is NULL and num_events_in_wait_list is not 0, or +// (2) event_wait_list is not NULL and num_events_in_wait_list is 0, or +// (3) event objects in event_wait_list are not valid events. + +struct WaitInvalidEventWaitList : public SemaphoreTestBase +{ + WaitInvalidEventWaitList(cl_device_id device, cl_context context, + cl_command_queue queue) + : SemaphoreTestBase(device, context, queue) + {} + + cl_int Run() override + { + // Create semaphore + cl_semaphore_properties_khr sema_props[] = { + static_cast(CL_SEMAPHORE_TYPE_KHR), + static_cast( + CL_SEMAPHORE_TYPE_BINARY_KHR), + 0 + }; + + cl_int err = CL_SUCCESS; + semaphore = + clCreateSemaphoreWithPropertiesKHR(context, sema_props, &err); + test_error(err, "Could not create semaphore"); + + + // Signal semaphore + err = clEnqueueSignalSemaphoresKHR(queue, 1, semaphore, nullptr, 0, + nullptr, nullptr); + test_error(err, "Could not signal semaphore"); + + // (1) Wait semaphore + err = clEnqueueWaitSemaphoresKHR(queue, 1, semaphore, nullptr, 1, + nullptr, nullptr); + test_failure_error(err, CL_INVALID_EVENT_WAIT_LIST, + "Unexpected clEnqueueWaitSemaphoresKHR return"); + + // Create user event + clEventWrapper user_event = clCreateUserEvent(context, &err); + test_error(err, "Could not create user event"); + + // (2) Wait semaphore + err = clEnqueueWaitSemaphoresKHR(queue, 1, semaphore, nullptr, 0, + &user_event, nullptr); + + cl_int signal_error = clSetUserEventStatus(user_event, CL_COMPLETE); + test_error(signal_error, "clSetUserEventStatus failed"); + + test_failure_error(err, CL_INVALID_EVENT_WAIT_LIST, + "Unexpected clEnqueueWaitSemaphoresKHR return"); + + // (3) Wait semaphore + cl_event wait_list[] = { nullptr, nullptr, nullptr }; + err = clEnqueueWaitSemaphoresKHR( + queue, 1, semaphore, nullptr, + sizeof(wait_list) / sizeof(wait_list[0]), wait_list, nullptr); + test_failure_error(err, CL_INVALID_EVENT_WAIT_LIST, + "Unexpected clEnqueueWaitSemaphoresKHR return"); + + return CL_SUCCESS; + } +}; + +// the execution status of any of the events in event_wait_list is a negative +// integer value. + +struct WaitInvalidEventStatus : public SemaphoreTestBase +{ + WaitInvalidEventStatus(cl_device_id device, cl_context context, + cl_command_queue queue) + : SemaphoreTestBase(device, context, queue) + {} + + cl_int Run() override + { + // Create semaphore + cl_semaphore_properties_khr sema_props[] = { + static_cast(CL_SEMAPHORE_TYPE_KHR), + static_cast( + CL_SEMAPHORE_TYPE_BINARY_KHR), + 0 + }; + + cl_int err = CL_SUCCESS; + semaphore = + clCreateSemaphoreWithPropertiesKHR(context, sema_props, &err); + test_error(err, "Could not create semaphore"); + + // Signal semaphore + err = clEnqueueSignalSemaphoresKHR(queue, 1, semaphore, nullptr, 0, + nullptr, nullptr); + test_error(err, "Could not signal semaphore"); + + // Create user event + clEventWrapper user_event = clCreateUserEvent(context, &err); + test_error(err, "Could not create user event"); + + // Now release the user event, which will allow our actual action to run + err = clSetUserEventStatus(user_event, -1); + test_error(err, "Unable to set event status"); + + // Wait semaphore + err = clEnqueueWaitSemaphoresKHR(queue, 1, semaphore, nullptr, 1, + &user_event, nullptr); + test_failure_error(err, CL_INVALID_EVENT_WAIT_LIST, + "Unexpected clEnqueueWaitSemaphoresKHR return"); + + return CL_SUCCESS; + } +}; + +} + +int test_semaphores_negative_wait_invalid_command_queue(cl_device_id device, + cl_context context, + cl_command_queue queue, + int num_elements) +{ + return MakeAndRunTest(device, context, queue); +} + +int test_semaphores_negative_wait_invalid_value(cl_device_id device, + cl_context context, + cl_command_queue queue, + int num_elements) +{ + return MakeAndRunTest(device, context, queue); +} + +int test_semaphores_negative_wait_invalid_semaphore(cl_device_id device, + cl_context context, + cl_command_queue queue, + int num_elements) +{ + return MakeAndRunTest(device, context, queue); +} + +int test_semaphores_negative_wait_invalid_context(cl_device_id device, + cl_context context, + cl_command_queue queue, + int num_elements) +{ + return MakeAndRunTest(device, context, queue); +} + +int test_semaphores_negative_wait_invalid_event_wait_list( + cl_device_id device, cl_context context, cl_command_queue queue, + int num_elements) +{ + return MakeAndRunTest(device, context, queue); +} + +int test_semaphores_negative_wait_invalid_event_status(cl_device_id device, + cl_context context, + cl_command_queue queue, + int num_elements) +{ + return MakeAndRunTest(device, context, queue); +}