/* * Copyright (c) Advanced Micro Devices, Inc., or its affiliates. * * SPDX-License-Identifier: MIT */ #include "cl_common.hpp" #include "platform/object.hpp" #include "platform/context.hpp" #include "platform/command.hpp" /*! \addtogroup API * @{ * \addtogroup CL_Events * * Event objects can be used to refer to a kernel execution command: * - clEnqueueNDRangeKernel * - clEnqueueTask * - clEnqueueNativeKernel * * or read, write, map and copy commands on memory objects: * - clEnqueue{Read|Write|Map}{Buffer|Image} * - clEnqueueCopy{Buffer|Image} * - clEnqueueCopyBufferToImage * - clEnqueueCopyImageToBuffer * * An event object can be used to track the execution status of a command. * The execution status of a command at any given point in time can be * CL_QUEUED (is currently in the command queue), * CL_RUNNING (device is currently executing this command), * CL_COMPLETE (command has successfully completed) or the appropriate error * code if the command was abnormally terminated (this may be caused by a bad * memory access etc.). The error code returned by a terminated command is * a negative integer value. A command is considered to be complete if its * execution status is CL_COMPLETE or is a negative integer value. * * If the execution of a command is terminated, the command-queue associated * with this terminated command, and the associated context (and all other * command-queues in this context) may no longer be available. The behavior of * OpenCL API calls that use this context (and command-queues associated with * this context) are now considered to be implementationdefined. The user * registered callback function specified when context is created can be used * to report appropriate error information. * * @{ */ /*! \brief Wait on the host thread for commands identified by event objects in * event_list to complete. * * A command is considered complete if its execution status is CL_COMPLETE or * a negative value. The events specified in event_list act as synchronization * points. * * \return One of the following values: * - CL_SUCCESS if the function was executed successfully. * - CL_INVALID_VALUE if \a num_events is zero * - CL_INVALID_CONTEXT if events specified in \a event_list do not belong to * the same context * - CL_INVALID_EVENT if event objects specified in \a event_list are not valid * event objects. * * \version 1.0r33 */ RUNTIME_ENTRY(cl_int, clWaitForEvents, (cl_uint num_events, const cl_event* event_list)) { if (num_events == 0 || event_list == NULL) { return CL_INVALID_VALUE; } const amd::Context* prevContext = NULL; const amd::HostQueue* prevQueue = NULL; for (cl_uint i = 0; i < num_events; ++i) { cl_event event = event_list[i]; if (!is_valid(event)) { return CL_INVALID_EVENT; } // Make sure all the events are associated with the same context amd::Event* amdEvent = as_amd(event); if (amdEvent->status() == CL_COMPLETE) continue; const amd::Context* context = &amdEvent->context(); if (prevContext != NULL && prevContext != context) { return CL_INVALID_CONTEXT; } prevContext = context; // Flush the command queues associated with event1...eventN amd::HostQueue* queue = amdEvent->command().queue(); if (queue != NULL && prevQueue != queue) { queue->flush(); } prevQueue = queue; } bool allSucceeded = true; while (num_events-- > 0) { allSucceeded &= as_amd(*event_list++)->awaitCompletion(); } return allSucceeded ? CL_SUCCESS : CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST; } RUNTIME_EXIT /*! \brief Return information about the event object. * * \param event specifies the event object being queried. * * \param param_name specifies the information to query. * * \param param_value is a pointer to memory where the appropriate result being * queried is returned. If \a param_value is NULL, it is ignored. * * \param param_value_size is used to specify the size in bytes of memory * pointed to by \a param_value. This size must be >= size of return type. * * \param param_value_size_ret returns the actual size in bytes of data copied * to \a param_value. If \a param_value_size_ret is NULL, it is ignored. * * Using clGetEventInfo to determine if a command identified by event has * finished execution (i.e. CL_EVENT_COMMAND_EXECUTION_STATUS returns * CL_COMPLETE) is not a synchronization point i.e. there are no guarantees * that the memory objects being modified by command associated with event will * be visible to other enqueued commands. * * \return One of the following values: * - CL_SUCCESS if the function is executed successfully * - CL_INVALID_VALUE if \a param_name is not valid, or if size in bytes * specified by \a param_value_size is < size of return type and * \a param_value is not NULL * - CL_INVALID_EVENT if \a event is a not a valid event object. * * \version 1.0r33 */ RUNTIME_ENTRY(cl_int, clGetEventInfo, (cl_event event, cl_event_info param_name, size_t param_value_size, void* param_value, size_t* param_value_size_ret)) { if (!is_valid(event)) { return CL_INVALID_EVENT; } switch (param_name) { case CL_EVENT_CONTEXT: { amd::Context& amdCtx = const_cast(as_amd(event)->context()); cl_context context = as_cl(&amdCtx); return amd::clGetInfo(context, param_value_size, param_value, param_value_size_ret); } case CL_EVENT_COMMAND_QUEUE: { amd::Command& command = as_amd(event)->command(); cl_command_queue queue = command.queue() == NULL ? NULL : const_cast(as_cl(command.queue()->asCommandQueue())); return amd::clGetInfo(queue, param_value_size, param_value, param_value_size_ret); } case CL_EVENT_COMMAND_TYPE: { cl_command_type type = as_amd(event)->command().type(); return amd::clGetInfo(type, param_value_size, param_value, param_value_size_ret); } case CL_EVENT_COMMAND_EXECUTION_STATUS: { as_amd(event)->notifyCmdQueue(); cl_int status = as_amd(event)->command().status(); return amd::clGetInfo(status, param_value_size, param_value, param_value_size_ret); } case CL_EVENT_REFERENCE_COUNT: { cl_uint count = as_amd(event)->referenceCount(); return amd::clGetInfo(count, param_value_size, param_value, param_value_size_ret); } default: break; } return CL_INVALID_VALUE; } RUNTIME_EXIT /*! \brief Increment the event reference count. * * \return CL_SUCCESS if the function is executed successfully. It returns * CL_INVALID_EVENT if \a event is not a valid event object. * * The OpenCL commands that return an event perform an implicit retain. * * \version 1.0r33 */ RUNTIME_ENTRY(cl_int, clRetainEvent, (cl_event event)) { if (!is_valid(event)) { return CL_INVALID_EVENT; } as_amd(event)->retain(); return CL_SUCCESS; } RUNTIME_EXIT /*! \brief Decrement the event reference count. * * \return CL_SUCCESS if the function is executed successfully. It returns * CL_INVALID_EVENT if \a event is not a valid event object. * * The event object is deleted once the reference count becomes zero, the * specific command identified by this event has completed (or terminated) and * there are no commands in the command-queues of a context that require a wait * for this event to complete. * * \version 1.0r33 */ RUNTIME_ENTRY(cl_int, clReleaseEvent, (cl_event event)) { if (!is_valid(event)) { return CL_INVALID_EVENT; } as_amd(event)->release(); return CL_SUCCESS; } RUNTIME_EXIT /*! \brief Creates a user event object. * * User events allow applications to enqueue commands that wait on a user event * to finish before the command is executed by the device. * * \return a valid non-zero event object and errcode_ret is set to CL_SUCCESS * if the user event object is created successfully. Otherwise, it returns * a NULL value with one of the following error values returned in errcode_ret: * - CL_INVALID_CONTEXT if context is not a valid context. * - CL_OUT_OF_HOST_MEMORY if there is a failure to allocate resources * required by the OpenCL implementation on the host. * * The execution status of the user event object created is set to CL_SUBMITTED. * * \version 1.1r15 */ RUNTIME_ENTRY_RET(cl_event, clCreateUserEvent, (cl_context context, cl_int* errcode_ret)) { if (!is_valid(context)) { *not_null(errcode_ret) = CL_INVALID_CONTEXT; return (cl_event)0; } auto event = new amd::UserEvent(*as_amd(context)); if (event == nullptr || !event->Create()) { delete event; *not_null(errcode_ret) = CL_OUT_OF_HOST_MEMORY; return (cl_event)0; } event->retain(); *not_null(errcode_ret) = CL_SUCCESS; return as_cl(reinterpret_cast(event)); } RUNTIME_EXIT /*! \brief Sets the execution status of a user event object. * * \a event is a user event object created using clCreateUserEvent. * \a execution_status specifies the new execution status to be set and can be * CL_COMPLETE or a negative integer value to indicate an error. * clSetUserEventStatus can only be called once to change the execution status * of event. * * \return CL_SUCCESS if the function was executed successfully. Otherwise, * it returns one of the following errors: * - CL_INVALID_EVENT if event is not a valid user event object. * - CL_INVALID_VALUE if the execution_status is not CL_COMPLETE or * a negative integer value. * - CL_INVALID_OPERATION if the execution_status for event has already been * changed by a previous call to clSetUserEventStatus. * * \version 1.1r15 */ RUNTIME_ENTRY(cl_int, clSetUserEventStatus, (cl_event event, cl_int execution_status)) { if (!is_valid(event)) { return CL_INVALID_EVENT; } if (execution_status > CL_COMPLETE) { return CL_INVALID_VALUE; } auto user_event = reinterpret_cast(as_amd(event)); if (!user_event->SetExecutionStatus(execution_status)) { return CL_INVALID_OPERATION; } return CL_SUCCESS; } RUNTIME_EXIT /*! \brief Registers a user callback function for a specific command execution * status. * * The registered callback function will be called when the execution status * of command associated with event changes to the execution status specified * by command_exec_status. * * Each call to clSetEventCallback registers the specified user callback * function on a callback stack associated with event. The order in which the * registered user callback functions are called is undefined. * * \a event is a valid event object. * \a command_exec_callback_type specifies the command execution status for * which the callback is registered. The command execution callback mask * values for which a callback can be registered are: CL_COMPLETE. * There is no guarantee that the callback functions registered for various * execution status values for an event will be called in the exact order * that the execution status of a command changes. * \a pfn_event_notify is the event callback function that can be registered * by the application. This callback function may be called asynchronously * by the OpenCL implementation. It is the application’s responsibility to * ensure that the callback function is thread-safe. The parameters to this * callback function are: * event is the event object for which the callback function is invoked. * event_command_exec_status represents the execution status of command * for which this callback function is invoked. If the callback is called * as the result of the command associated with event being abnormally * terminated, an appropriate error code for the error that caused the * termination will be passed to event_command_exec_status instead. * \a user_data is a pointer to user supplied data. user_data will be passed as * the user_data argument when pfn_notify is called. user_data can be NULL. * * All callbacks registered for an event object must be called. All enqueued * callbacks shall be called before the event object is destroyed. Callbacks * must return promptly. The behavior of calling expensive system routines, * OpenCL API calls to create contexts or command-queues, or blocking OpenCL * operations from the following list below, in a callback is undefined. * clFinish, clWaitForEvents, blocking calls to clEnqueueReadBuffer, * clEnqueueReadBufferRect, clEnqueueWriteBuffer, clEnqueueWriteBufferRect, * blocking calls to clEnqueueReadImage and clEnqueueWriteImage, blocking * calls to clEnqueueMapBuffer and clEnqueueMapImage, blocking calls to * clBuildProgram * * If an application needs to wait for completion of a routine from the above * list in a callback, please use the non-blocking form of the function, and * assign a completion callback to it to do the remainder of your work. * Note that when a callback (or other code) enqueues commands to a * command-queue, the commands are not required to begin execution until the * queue is flushed. In standard usage, blocking enqueue calls serve this role * by implicitly flushing the queue. Since blocking calls are not permitted in * callbacks, those callbacks that enqueue commands on a command queue should * either call clFlush on the queue before returning or arrange for clFlush * to be called later on another thread. * * \return CL_SUCCESS if the function is executed successfully. Otherwise, * it returns one of the following errors: * - CL_INVALID_EVENT if event is not a valid event object or is a user event * object created using clCreateUserEvent. * - CL_INVALID_VALUE if pfn_event_notify is NULL or if * command_exec_callback_type is not a valid command execution status. * - CL_OUT_OF_HOST_MEMORY if there is a failure to allocate resources * required by the OpenCL implementation on the host. * * \version 1.1r15 */ RUNTIME_ENTRY(cl_int, clSetEventCallback, (cl_event event, cl_int command_exec_callback_type, void(CL_CALLBACK* pfn_notify)(cl_event event, cl_int command_exec_status, void* user_data), void* user_data)) { if (!is_valid(event)) { return CL_INVALID_EVENT; } if (pfn_notify == NULL || command_exec_callback_type < CL_COMPLETE || command_exec_callback_type > CL_QUEUED) { return CL_INVALID_VALUE; } amd::Event* ev = as_amd(event); ev->retain(); if (!ev->setCallback(command_exec_callback_type, pfn_notify, user_data)) { ev->release(); return CL_OUT_OF_HOST_MEMORY; } if (ev->status() > CL_COMPLETE) { ev->notifyCmdQueue(); } ev->release(); return CL_SUCCESS; } RUNTIME_EXIT /*! @} * @} */