text: //
  // Copyright (c) 2008 Advanced Micro Devices, Inc. All rights reserved.
  //
  
  #include "cl_common.hpp"
  
  #include "platform/object.hpp"
  #include "platform/context.hpp"
  #include "platform/command.hpp"
  #include "platform/agent.hpp"
  
  /*! \addtogroup API
   *  @{
   *
   *  \addtogroup CL_Queues
   *
   *  OpenCL objects such as memory objects, program and kernel objects are
   *  created using a context. Operations on these objects are performed using
   *  a command-queue. The command-queue can be used to queue a set of operations
   *  (referred to as commands) in order. Having multiple command-queues allows
   *  applications to queue multiple independent commands without requiring
   *  synchronization. Note that this should work as long as these objects are
   *  not being shared. Sharing of objects across multiple command-queues will
   *  require the application to perform appropriate synchronization.
   *
   *  @{
   */
  
  /*! \brief Create a command-queue on a specific device.
   *
   *  \param context must be a valid OpenCL context.
   *
   *  \param device must be a device associated with context. It can either be
   *  in the list of devices specified when context is created using
   *  clCreateContext or have the same device type as device type specified wheni
   *  context is created using clCreateContextFromType.
   *
   *  \param properties specifies a list of properties for the command-queue.
   *
   *  \param errcode_ret will return an appropriate error code. If \a errcode_ret
   *  is NULL, no error code is returned.
   *
   *  \return A valid non-zero command-queue and \a errcode_ret is set to
   *  CL_SUCCESS if the command-queue is created successfully or a NULL value
   *  with one of the following error values returned \a in errcode_ret:
   *    - CL_INVALID_CONTEXT if context is not a valid.
   *    - CL_INVALID_DEVICE if device is not a valid device or is not associated
   *      with context
   *    - CL_INVALID_VALUE if values specified in properties are not valid.
   *    - CL_INVALID_QUEUE_PROPERTIES if values specified in properties are valid
   *      but are not supported by the device.
   *    - CL_OUT_OF_HOST_MEMORY if there is a failure to allocate resources
   *      required by the runtime.
   *
   *  \version 1.0r33
   */
  RUNTIME_ENTRY_RET(cl_command_queue, clCreateCommandQueueWithProperties,
                    (cl_context context, cl_device_id device,
                     const cl_queue_properties* queue_properties, cl_int* errcode_ret)) {
    if (!is_valid(context)) {
      *not_null(errcode_ret) = CL_INVALID_CONTEXT;
      return (cl_command_queue)0;
    }
  
    amd::Context& amdContext = *as_amd(context);
    amd::Device& amdDevice = *as_amd(device);
  
    if (!is_valid(device) || !amdContext.containsDevice(&amdDevice)) {
      *not_null(errcode_ret) = CL_INVALID_DEVICE;
      return (cl_command_queue)0;
    }
  
    cl_command_queue_properties properties = 0;
    const struct QueueProperty {
      cl_queue_properties name;
      union {
        cl_queue_properties raw;
        // FIXME_lmoriche: Check with Khronos. cl_queue_properties is an intptr,
        // but cl_command_queue_properties is a bitfield (truncate?).
        // cl_command_queue_properties properties;
        cl_uint size;
      } value;
    }* p = reinterpret_cast<const QueueProperty*>(queue_properties);
  
    uint queueSize = amdDevice.info().queueOnDevicePreferredSize_;
    uint queueRTCUs = amd::CommandQueue::RealTimeDisabled;
    amd::CommandQueue::Priority priority = amd::CommandQueue::Priority::Normal;
    if (p != NULL)
      while (p->name != 0) {
        switch (p->name) {
          case CL_QUEUE_PROPERTIES:
            // FIXME_lmoriche: See comment above.
            // properties = p->value.properties;
            properties = static_cast<cl_command_queue_properties>(p->value.raw);
            break;
          case CL_QUEUE_SIZE:
            queueSize = p->value.size;
            break;
  #define CL_QUEUE_REAL_TIME_COMPUTE_UNITS_AMD 0x404f
          case CL_QUEUE_REAL_TIME_COMPUTE_UNITS_AMD:
            queueRTCUs = p->value.size;
            break;
  #define CL_QUEUE_MEDIUM_PRIORITY_AMD 0x4050
          case CL_QUEUE_MEDIUM_PRIORITY_AMD:
            priority = amd::CommandQueue::Priority::Medium;
            break;
          default:
            *not_null(errcode_ret) = CL_INVALID_QUEUE_PROPERTIES;
            LogWarning("invalid property name");
            return (cl_command_queue)0;
        }
        ++p;
      }
  
    if (queueSize > amdDevice.info().queueOnDeviceMaxSize_) {
      *not_null(errcode_ret) = CL_INVALID_VALUE;
      return (cl_command_queue)0;
    }
  
    if ((queueRTCUs != amd::CommandQueue::RealTimeDisabled) &&
        ((queueRTCUs > amdDevice.info().numRTCUs_) || (queueRTCUs == 0))) {
      *not_null(errcode_ret) = CL_INVALID_VALUE;
      return (cl_command_queue)0;
    }
  
    amd::CommandQueue* queue = NULL;
    {
      amd::ScopedLock lock(amdContext.lock());
  
      // Check if the app creates a host queue
      if (!(properties & CL_QUEUE_ON_DEVICE)) {
        queue = new amd::HostQueue(amdContext, amdDevice, properties, queueRTCUs, priority);
      } else {
        // Is it a device default queue
        if (properties & CL_QUEUE_ON_DEVICE_DEFAULT) {
          queue = amdContext.defDeviceQueue(amdDevice);
          // If current context has one already then return it
          if (NULL != queue) {
            queue->retain();
            *not_null(errcode_ret) = CL_SUCCESS;
            return as_cl(queue);
          }
        }
        // Check if runtime can allocate a new device queue on this context
        if (amdContext.isDevQueuePossible(amdDevice)) {
          queue = new amd::DeviceQueue(amdContext, amdDevice, properties, queueSize);
        }
      }
  
      if (queue == NULL || !queue->create()) {
        *not_null(errcode_ret) = CL_OUT_OF_HOST_MEMORY;
        delete queue;
        return (cl_command_queue)0;
      }
    }
  
    if (amd::Agent::shouldPostCommandQueueEvents()) {
      amd::Agent::postCommandQueueCreate(as_cl(queue->asCommandQueue()));
    }
  
    *not_null(errcode_ret) = CL_SUCCESS;
    return as_cl(queue);
  }
  RUNTIME_EXIT
  
  RUNTIME_ENTRY_RET(cl_command_queue, clCreateCommandQueue,
                    (cl_context context, cl_device_id device, cl_command_queue_properties properties,
                     cl_int* errcode_ret)) {
    const cl_queue_properties cprops[] = {CL_QUEUE_PROPERTIES,
                                          static_cast<cl_queue_properties>(properties), 0};
    return clCreateCommandQueueWithProperties(context, device, properties ? cprops : NULL,
                                              errcode_ret);
  }
  RUNTIME_EXIT
  
  /*! \brief Increment the \a command_queue reference count.
   *
   *  \return One of the following values:
   *    - CL_SUCCESS if the function is executed successfully.
   *    - CL_INVALID_COMMAND_QUEUE if \a command_queue is not a valid
   *      command-queue.
   *
   *  clCreateCommandQueue performs an implicit retain. This is very helpful for
   *  3rd party libraries, which typically get a command-queue passed to them
   *  by the application. However, it is possible that the application may delete
   *  the command-queue without informing the library.  Allowing functions to
   *  attach to (i.e. retain) and release a command-queue solves the problem of a
   *  command-queue being used by a library no longer being valid.
   *
   *  \version 1.0r33
   */
  RUNTIME_ENTRY(cl_int, clRetainCommandQueue, (cl_command_queue command_queue)) {
    if (!is_valid(command_queue)) {
      return CL_INVALID_COMMAND_QUEUE;
    }
    as_amd(command_queue)->retain();
    return CL_SUCCESS;
  }
  RUNTIME_EXIT
  
  /*! \brief Decrement the \a command_queue reference count.
   *
   *  \return One of the following values:
   *    - CL_SUCCESS if the function is executed successfully.
   *    - CL_INVALID_COMMAND_QUEUE if \a command_queue is not a valid
   *      command-queue.
   *
   *  After the command_queue reference count becomes zero and all commands queued
   *  to \a command_queue have finished (eg. kernel executions, memory object
   *  updates etc.), the command-queue is deleted.
   *
   *  \version 1.0r33
   */
  RUNTIME_ENTRY(cl_int, clReleaseCommandQueue, (cl_command_queue command_queue)) {
    if (!is_valid(command_queue)) {
      return CL_INVALID_COMMAND_QUEUE;
    }
    as_amd(command_queue)->release();
    return CL_SUCCESS;
  }
  RUNTIME_EXIT
  
  /*! \brief Query information about a command-queue.
   *
   *  \param command_queue specifies the command-queue 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.
   *  If param_value is NULL, it is ignored.
   *
   *  \param param_value_size_ret returns the actual size in bytes of data being
   *  queried by \a param_value. If \a param_value_size_ret is NULL,
   *  it is ignored.
   *
   *  \return One of the following values:
   *    - CL_SUCCESS if the function is executed successfully.
   *    - CL_INVALID_COMMAND_QUEUE if \a command_queue is not a valid
   *      command-queue.
   *    - CL_INVALID_VALUE if \a param_name is not one of the supported
   *      values or if size in bytes specified by \a param_value_size is < size of
   *      return type and \a param_value is not a NULL value.
   *
   *  \version 1.0r33
   */
  RUNTIME_ENTRY(cl_int, clGetCommandQueueInfo,
                (cl_command_queue command_queue, cl_command_queue_info param_name,
                 size_t param_value_size, void* param_value, size_t* param_value_size_ret)) {
    if (!is_valid(command_queue)) {
      return CL_INVALID_COMMAND_QUEUE;
    }
  
    switch (param_name) {
      case CL_QUEUE_CONTEXT: {
        cl_context context = const_cast<cl_context>(as_cl(&as_amd(command_queue)->context()));
        return amd::clGetInfo(context, param_value_size, param_value, param_value_size_ret);
      }
      case CL_QUEUE_DEVICE: {
        cl_device_id device = const_cast<cl_device_id>(as_cl(&as_amd(command_queue)->device()));
        return amd::clGetInfo(device, param_value_size, param_value, param_value_size_ret);
      }
      case CL_QUEUE_PROPERTIES: {
        cl_command_queue_properties properties = as_amd(command_queue)->properties().value_;
        return amd::clGetInfo(properties, param_value_size, param_value, param_value_size_ret);
      }
      case CL_QUEUE_REFERENCE_COUNT: {
        cl_uint count = as_amd(command_queue)->referenceCount();
        return amd::clGetInfo(count, param_value_size, param_value, param_value_size_ret);
      }
      case CL_QUEUE_SIZE: {
        const amd::DeviceQueue* deviceQueue = as_amd(command_queue)->asDeviceQueue();
        if (NULL == deviceQueue) {
          return CL_INVALID_COMMAND_QUEUE;
        }
        cl_uint size = deviceQueue->size();
        return amd::clGetInfo(size, param_value_size, param_value, param_value_size_ret);
      }
      case CL_QUEUE_THREAD_HANDLE_AMD: {
        const amd::HostQueue* hostQueue = as_amd(command_queue)->asHostQueue();
        if (NULL == hostQueue) {
          return CL_INVALID_COMMAND_QUEUE;
        }
        const void* handle = hostQueue->thread().handle();
        return amd::clGetInfo(handle, param_value_size, param_value, param_value_size_ret);
      }
      default:
        break;
    }
  
    return CL_INVALID_VALUE;
  }
  RUNTIME_EXIT
  
  /*! \brief Enable or disable the properties of a command-queue.
   *
   *  \param command_queue specifies the command-queue being queried.
   *
   *  \param properties specifies the new command-queue properties to be applied
   *  to \a command_queue .
   *
   *  \param enable determines whether the values specified by properties are
   *  enabled (if enable is CL_TRUE) or disabled (if enable is CL_FALSE) for the
   *  command-queue .
   *
   *  \param old_properties returns the command-queue properties before they were
   *  changed by clSetCommandQueueProperty. If \a old_properties is NULL,
   *  it is ignored.
   *
   *  \return One of the following values:
   *  - CL_SUCCESS if the command-queue properties are successfully updated.
   *  - CL_INVALID_COMMAND_QUEUE if command_queue is not a valid command-queue.
   *  - CL_INVALID_VALUE if the values specified in properties are not valid.
   *  - CL_INVALID_QUEUE_PROPERTIES if values specified in properties are
   *    not supported by the device.
   *
   *  \version 1.0r33
   */
  RUNTIME_ENTRY(cl_int, clSetCommandQueueProperty,
                (cl_command_queue command_queue, cl_command_queue_properties properties,
                 cl_bool enable, cl_command_queue_properties* old_properties)) {
    if (!is_valid(command_queue)) {
      return CL_INVALID_COMMAND_QUEUE;
    }
  
    *not_null(old_properties) = as_amd(command_queue)->properties().value_;
  
    if (properties & CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE) {
      clFinish(command_queue);
    }
  
    bool success;
    if (enable == CL_TRUE) {
      success = as_amd(command_queue)->properties().set(properties);
    } else {
      success = as_amd(command_queue)->properties().clear(properties);
    }
  
    return success ? CL_SUCCESS : CL_INVALID_QUEUE_PROPERTIES;
  }
  RUNTIME_EXIT
  
  /*! @}
   *  @}
   */
  