Skip to content
Snippets Groups Projects
wrap_cl.hpp 134 KiB
Newer Older
        PYOPENCL_CALL_GUARDED(clCreateSubDevices,
Andreas Klöckner's avatar
Andreas Klöckner committed
            (m_device, props_ptr, 0, nullptr, &num_entries));

        std::vector<cl_device_id> result;
        result.resize(num_entries);

        PYOPENCL_CALL_GUARDED(clCreateSubDevices,
Andreas Klöckner's avatar
Andreas Klöckner committed
            (m_device, props_ptr, num_entries, &result.front(), nullptr));

        py::list py_result;
        for (cl_device_id did: result)
          py_result.append(handle_from_new_ptr(
                new pyopencl::device(did, /*retain*/true,
                  device::REF_CL_1_2)));
        return py_result;
      }
#endif

  };




  inline py::list platform::get_devices(cl_device_type devtype)
  {
    cl_uint num_devices = 0;
    PYOPENCL_PRINT_CALL_TRACE("clGetDeviceIDs");
    {
      cl_int status_code;
      status_code = clGetDeviceIDs(m_platform, devtype, 0, 0, &num_devices);
      if (status_code == CL_DEVICE_NOT_FOUND)
        num_devices = 0;
      else if (status_code != CL_SUCCESS) \
        throw pyopencl::error("clGetDeviceIDs", status_code);
    }

    if (num_devices == 0)
      return py::list();

    std::vector<cl_device_id> devices(num_devices);
    PYOPENCL_CALL_GUARDED(clGetDeviceIDs,
        (m_platform, devtype,
Andreas Klöckner's avatar
Andreas Klöckner committed
         num_devices, devices.empty( ) ? nullptr : &devices.front(), &num_devices));
    for (cl_device_id did: devices)
      result.append(handle_from_new_ptr(
            new device(did)));

    return result;
  }

  // }}}

  // {{{ context

  class context : public noncopyable
  {
    private:
      cl_context m_context;

    public:
      context(cl_context ctx, bool retain)
        : m_context(ctx)
      {
        if (retain)
          PYOPENCL_CALL_GUARDED(clRetainContext, (ctx));
      }

      ~context()
      {
        PYOPENCL_CALL_GUARDED_CLEANUP(clReleaseContext,
            (m_context));
      }

      cl_context data() const
      {
        return m_context;
      }

      PYOPENCL_EQUALITY_TESTS(context);

      py::object get_info(cl_context_info param_name) const
      {
        switch (param_name)
        {
          case CL_CONTEXT_REFERENCE_COUNT:
            PYOPENCL_GET_TYPED_INFO(
                Context, m_context, param_name, cl_uint);

          case CL_CONTEXT_DEVICES:
            {
              std::vector<cl_device_id> result;
              PYOPENCL_GET_VEC_INFO(Context, m_context, param_name, result);

              py::list py_result;
              for (cl_device_id did: result)
                py_result.append(handle_from_new_ptr(
                      new pyopencl::device(did)));
              return py_result;
            }

          case CL_CONTEXT_PROPERTIES:
            {
              std::vector<cl_context_properties> result;
              PYOPENCL_GET_VEC_INFO(Context, m_context, param_name, result);

              py::list py_result;
              for (size_t i = 0; i < result.size(); i+=2)
              {
                cl_context_properties key = result[i];
                py::object value;
                switch (key)
                {
                  case CL_CONTEXT_PLATFORM:
                    {
                      value = py::object(
                          handle_from_new_ptr(new platform(
                            reinterpret_cast<cl_platform_id>(result[i+1]))));
                      break;
                    }

#if defined(PYOPENCL_GL_SHARING_VERSION) && (PYOPENCL_GL_SHARING_VERSION >= 1)
#if defined(__APPLE__) && defined(HAVE_GL)
                  case CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE:
#else
                  case CL_GL_CONTEXT_KHR:
                  case CL_EGL_DISPLAY_KHR:
                  case CL_GLX_DISPLAY_KHR:
                  case CL_WGL_HDC_KHR:
                  case CL_CGL_SHAREGROUP_KHR:
#endif
                    value = py::cast(result[i+1]);
                    break;

#endif
                  case 0:
                    break;

                  default:
                    throw error("Context.get_info", CL_INVALID_VALUE,
                        "unknown context_property key encountered");
                }

                py_result.append(py::make_tuple(result[i], value));
              }
              return py_result;
            }

#if PYOPENCL_CL_VERSION >= 0x1010
          case CL_CONTEXT_NUM_DEVICES:
            PYOPENCL_GET_TYPED_INFO(
                Context, m_context, param_name, cl_uint);
#endif

          default:
            throw error("Context.get_info", CL_INVALID_VALUE);
        }
      }


      // not exposed to python
      int get_hex_platform_version() const
      {
        std::vector<cl_device_id> devices;
        PYOPENCL_GET_VEC_INFO(Context, m_context, CL_CONTEXT_DEVICES, devices);

        if (devices.size() == 0)
          throw error("Context._get_hex_version", CL_INVALID_VALUE,
              "platform has no devices");

        cl_platform_id plat;

        PYOPENCL_CALL_GUARDED(clGetDeviceInfo,
            (devices[0], CL_DEVICE_PLATFORM, sizeof(plat), &plat, nullptr));

        std::string plat_version;
        {
          size_t param_value_size;
          PYOPENCL_CALL_GUARDED(clGetPlatformInfo,
              (plat, CL_PLATFORM_VERSION, 0, 0, &param_value_size));

          std::vector<char> param_value(param_value_size);
          PYOPENCL_CALL_GUARDED(clGetPlatformInfo,
              (plat, CL_PLATFORM_VERSION, param_value_size,
               param_value.empty( ) ? nullptr : &param_value.front(), &param_value_size));

          plat_version =
              param_value.empty( ) ? "" : std::string(&param_value.front(), param_value_size-1);
        }

        int major_ver, minor_ver;
        errno = 0;
        int match_count = sscanf(plat_version.c_str(), "OpenCL %d.%d ", &major_ver, &minor_ver);
        if (errno || match_count != 2)
          throw error("Context._get_hex_version", CL_INVALID_VALUE,
              "Platform version string did not have expected format");

        return major_ver << 12 | minor_ver << 4;
      }
  };


  inline
  std::vector<cl_context_properties> parse_context_properties(
      py::object py_properties)
  {
    std::vector<cl_context_properties> props;

    if (py_properties.ptr() != Py_None)
    {
      for (py::handle prop_tuple_py: py_properties)
        py::tuple prop_tuple(prop_tuple_py.cast<py::tuple>());

        if (len(prop_tuple) != 2)
          throw error("Context", CL_INVALID_VALUE, "property tuple must have length 2");
        cl_context_properties prop = prop_tuple[0].cast<cl_context_properties>();
        props.push_back(prop);

        if (prop == CL_CONTEXT_PLATFORM)
        {
          props.push_back(
              reinterpret_cast<cl_context_properties>(
                prop_tuple[1].cast<const platform &>().data()));
        }
#if defined(PYOPENCL_GL_SHARING_VERSION) && (PYOPENCL_GL_SHARING_VERSION >= 1)
#if defined(_WIN32)
       else if (prop == CL_WGL_HDC_KHR)
       {
         // size_t is a stand-in for HANDLE, hopefully has the same size.
         size_t hnd = (prop_tuple[1]).cast<size_t>();
         props.push_back(hnd);
       }
#endif
       else if (
#if defined(__APPLE__) && defined(HAVE_GL)
            prop == CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE
#else
            prop == CL_GL_CONTEXT_KHR
            || prop == CL_EGL_DISPLAY_KHR
            || prop == CL_GLX_DISPLAY_KHR
            || prop == CL_CGL_SHAREGROUP_KHR
#endif
           )
       {
          py::object ctypes = py::module::import("ctypes");
          py::object prop = prop_tuple[1], c_void_p = ctypes.attr("c_void_p");
          py::object ptr = ctypes.attr("cast")(prop, c_void_p);
          props.push_back(ptr.attr("value").cast<cl_context_properties>());
       }
#endif
        else
          throw error("Context", CL_INVALID_VALUE, "invalid context property");
      }
      props.push_back(0);
    }

    return props;
  }


  inline
  context *create_context_inner(py::object py_devices, py::object py_properties,
      py::object py_dev_type)
  {
    std::vector<cl_context_properties> props
      = parse_context_properties(py_properties);

    cl_context_properties *props_ptr
Andreas Klöckner's avatar
Andreas Klöckner committed
      = props.empty( ) ? nullptr : &props.front();

    cl_int status_code;

    cl_context ctx;

    // from device list
    if (py_devices.ptr() != Py_None)
    {
      if (py_dev_type.ptr() != Py_None)
        throw error("Context", CL_INVALID_VALUE,
            "one of 'devices' or 'dev_type' must be None");

      std::vector<cl_device_id> devices;
Andreas Klöckner's avatar
Andreas Klöckner committed
      for (py::handle py_dev: py_devices)
        devices.push_back(py_dev.cast<const device &>().data());

      PYOPENCL_PRINT_CALL_TRACE("clCreateContext");
      ctx = clCreateContext(
          props_ptr,
          devices.size(),
Andreas Klöckner's avatar
Andreas Klöckner committed
          devices.empty( ) ? nullptr : &devices.front(),
          0, 0, &status_code);
    }
    // from dev_type
    else
    {
      cl_device_type dev_type = CL_DEVICE_TYPE_DEFAULT;
      if (py_dev_type.ptr() != Py_None)
        dev_type = py_dev_type.cast<cl_device_type>();

      PYOPENCL_PRINT_CALL_TRACE("clCreateContextFromType");
      ctx = clCreateContextFromType(props_ptr, dev_type, 0, 0, &status_code);
    }

    if (status_code != CL_SUCCESS)
      throw pyopencl::error("Context", status_code);

    try
    {
      return new context(ctx, false);
    }
    catch (...)
    {
      PYOPENCL_CALL_GUARDED(clReleaseContext, (ctx));
      throw;
    }
  }


  inline
  context *create_context(py::object py_devices, py::object py_properties,
      py::object py_dev_type)
  {
    PYOPENCL_RETRY_RETURN_IF_MEM_ERROR(
      return create_context_inner(py_devices, py_properties, py_dev_type);
    )
  }

  // }}}

  // {{{ command_queue
  class command_queue
  {
    private:
      cl_command_queue m_queue;

    public:
      command_queue(cl_command_queue q, bool retain)
        : m_queue(q)
      {
        if (retain)
          PYOPENCL_CALL_GUARDED(clRetainCommandQueue, (q));
      }

      command_queue(command_queue const &src)
        : m_queue(src.m_queue)
      {
        PYOPENCL_CALL_GUARDED(clRetainCommandQueue, (m_queue));
      }

      command_queue(
          const context &ctx,
          const device *py_dev=nullptr,
          py::object py_props=py::none())
      {
        cl_device_id dev;
        if (py_dev)
          dev = py_dev->data();
        else
        {
          std::vector<cl_device_id> devs;
          PYOPENCL_GET_VEC_INFO(Context, ctx.data(), CL_CONTEXT_DEVICES, devs);
          if (devs.size() == 0)
            throw pyopencl::error("CommandQueue", CL_INVALID_VALUE,
                "context doesn't have any devices? -- don't know which one to default to");
          dev = devs[0];
        }

        int hex_plat_version = ctx.get_hex_platform_version();

        bool props_given_as_numeric;
        cl_command_queue_properties num_props;
        if (py_props.is_none())
        {
          num_props = 0;
          props_given_as_numeric = true;
        }
        else
        {
          try
          {
            num_props = py::cast<cl_command_queue_properties>(py_props);
            props_given_as_numeric = true;
          }
          catch (py::cast_error &)
          {
            props_given_as_numeric = false;
          }
        }

        if (props_given_as_numeric)
        {
#if PYOPENCL_CL_VERSION >= 0x2000
          if (hex_plat_version  >= 0x2000)
          {
            cl_queue_properties props_list[] = { CL_QUEUE_PROPERTIES, num_props, 0 };

            cl_int status_code;

            PYOPENCL_PRINT_CALL_TRACE("clCreateCommandQueueWithProperties");
            m_queue = clCreateCommandQueueWithProperties(
                ctx.data(), dev, props_list, &status_code);

            if (status_code != CL_SUCCESS)
              throw pyopencl::error("CommandQueue", status_code);
          }
          else
#endif
          {
            cl_int status_code;

            PYOPENCL_PRINT_CALL_TRACE("clCreateCommandQueue");
#if defined(__GNUG__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
            m_queue = clCreateCommandQueue(
                ctx.data(), dev, num_props, &status_code);
#if defined(__GNUG__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
            if (status_code != CL_SUCCESS)
              throw pyopencl::error("CommandQueue", status_code);
          }
        }
        else
        {
#if PYOPENCL_CL_VERSION < 0x2000
            throw error("CommandQueue", CL_INVALID_VALUE,
                "queue properties given as an iterable, "
                "which is only allowed when PyOpenCL was built "
                "against an OpenCL 2+ header");
          if (hex_plat_version  < 0x2000)
          {
            std::cerr <<
                "queue properties given as an iterable, "
                "which uses an OpenCL 2+-only interface, "
                "but the context's platform does not "
                "declare OpenCL 2 support. Proceeding "
                "as requested, but the next thing you see "
          PYOPENCL_STACK_CONTAINER(cl_queue_properties, props, py::len(py_props) + 1);
          {
            size_t i = 0;
            for (auto prop: py_props)
              props[i++] = py::cast<cl_queue_properties>(prop);
            props[i++] = 0;
          }

          cl_int status_code;
          PYOPENCL_PRINT_CALL_TRACE("clCreateCommandQueueWithProperties");
          m_queue = clCreateCommandQueueWithProperties(
              ctx.data(), dev, PYOPENCL_STACK_CONTAINER_GET_PTR(props), &status_code);
          if (status_code != CL_SUCCESS)
            throw pyopencl::error("CommandQueue", status_code);
      }

      ~command_queue()
      {
        PYOPENCL_CALL_GUARDED_CLEANUP(clReleaseCommandQueue,
            (m_queue));
      }

      const cl_command_queue data() const
      { return m_queue; }

      PYOPENCL_EQUALITY_TESTS(command_queue);

      py::object get_info(cl_command_queue_info param_name) const
      {
        switch (param_name)
        {
          case CL_QUEUE_CONTEXT:
            PYOPENCL_GET_OPAQUE_INFO(CommandQueue, m_queue, param_name,
                cl_context, context);
          case CL_QUEUE_DEVICE:
            PYOPENCL_GET_OPAQUE_INFO(CommandQueue, m_queue, param_name,
                cl_device_id, device);
          case CL_QUEUE_REFERENCE_COUNT:
            PYOPENCL_GET_TYPED_INFO(CommandQueue, m_queue, param_name,
                cl_uint);
          case CL_QUEUE_PROPERTIES:
            PYOPENCL_GET_TYPED_INFO(CommandQueue, m_queue, param_name,
                cl_command_queue_properties);

          default:
            throw error("CommandQueue.get_info", CL_INVALID_VALUE);
        }
      }

      std::unique_ptr<context> get_context() const
      {
        cl_context param_value;
        PYOPENCL_CALL_GUARDED(clGetCommandQueueInfo,
            (m_queue, CL_QUEUE_CONTEXT, sizeof(param_value), &param_value, 0));
        return std::unique_ptr<context>(
            new context(param_value, /*retain*/ true));
      }

#if PYOPENCL_CL_VERSION < 0x1010
      cl_command_queue_properties set_property(
          cl_command_queue_properties prop,
          bool enable)
      {
        cl_command_queue_properties old_prop;
        PYOPENCL_CALL_GUARDED(clSetCommandQueueProperty,
            (m_queue, prop, PYOPENCL_CAST_BOOL(enable), &old_prop));
        return old_prop;
      }
#endif

      void flush()
      { PYOPENCL_CALL_GUARDED(clFlush, (m_queue)); }
      void finish()
      { PYOPENCL_CALL_GUARDED_THREADED(clFinish, (m_queue)); }
  };

  // }}}

  // {{{ event/synchronization

  class event : noncopyable
  {
    private:
      cl_event m_event;

    public:
      event(cl_event event, bool retain)
        : m_event(event)
      {
        if (retain)
          PYOPENCL_CALL_GUARDED(clRetainEvent, (event));
      }

      event(event const &src)
        : m_event(src.m_event)
      { PYOPENCL_CALL_GUARDED(clRetainEvent, (m_event)); }

      virtual ~event()
      {
        PYOPENCL_CALL_GUARDED_CLEANUP(clReleaseEvent,
            (m_event));
      }

      const cl_event data() const
      { return m_event; }

      PYOPENCL_EQUALITY_TESTS(event);

      py::object get_info(cl_event_info param_name) const
      {
        switch (param_name)
        {
          case CL_EVENT_COMMAND_QUEUE:
            PYOPENCL_GET_OPAQUE_INFO(Event, m_event, param_name,
                cl_command_queue, command_queue);
          case CL_EVENT_COMMAND_TYPE:
            PYOPENCL_GET_TYPED_INFO(Event, m_event, param_name,
                cl_command_type);
          case CL_EVENT_COMMAND_EXECUTION_STATUS:
            PYOPENCL_GET_TYPED_INFO(Event, m_event, param_name,
                cl_int);
          case CL_EVENT_REFERENCE_COUNT:
            PYOPENCL_GET_TYPED_INFO(Event, m_event, param_name,
                cl_uint);
#if PYOPENCL_CL_VERSION >= 0x1010
          case CL_EVENT_CONTEXT:
            PYOPENCL_GET_OPAQUE_INFO(Event, m_event, param_name,
                cl_context, context);
#endif

          default:
            throw error("Event.get_info", CL_INVALID_VALUE);
        }
      }

      py::object get_profiling_info(cl_profiling_info param_name) const
      {
        switch (param_name)
        {
          case CL_PROFILING_COMMAND_QUEUED:
          case CL_PROFILING_COMMAND_SUBMIT:
          case CL_PROFILING_COMMAND_START:
          case CL_PROFILING_COMMAND_END:
Andreas Klöckner's avatar
Andreas Klöckner committed
#if PYOPENCL_CL_VERSION >= 0x2000
          case CL_PROFILING_COMMAND_COMPLETE:
#endif
            PYOPENCL_GET_TYPED_INFO(EventProfiling, m_event, param_name,
                cl_ulong);
          default:
            throw error("Event.get_profiling_info", CL_INVALID_VALUE);
        }
      }

      virtual void wait()
      {
        PYOPENCL_CALL_GUARDED_THREADED(clWaitForEvents, (1, &m_event));
      }
      // Called from a destructor context below:
      // - Should not release the GIL
      // - Should fail gracefully in the face of errors
      virtual void wait_during_cleanup_without_releasing_the_gil()
      {
        PYOPENCL_CALL_GUARDED_CLEANUP(clWaitForEvents, (1, &m_event));
      }

#if PYOPENCL_CL_VERSION >= 0x1010
    // {{{ set_callback, by way of a a thread-based construction

    private:
      struct event_callback_info_t
      {
        std::mutex m_mutex;
        std::condition_variable m_condvar;

        py::object m_py_event;
        py::object m_py_callback;

        bool m_set_callback_suceeded;

        cl_event m_event;
        cl_int m_command_exec_status;

        event_callback_info_t(py::object py_event, py::object py_callback)
        : m_py_event(py_event), m_py_callback(py_callback), m_set_callback_suceeded(true),
        m_notify_thread_wakeup_is_genuine(false)
      static void CL_CALLBACK evt_callback(cl_event evt, cl_int command_exec_status, void *user_data)
      {
        event_callback_info_t *cb_info = reinterpret_cast<event_callback_info_t *>(user_data);
        {
          std::lock_guard<std::mutex> lg(cb_info->m_mutex);
          cb_info->m_event = evt;
          cb_info->m_command_exec_status = command_exec_status;
        cb_info->m_condvar.notify_one();
      }

    public:
      void set_callback(cl_int command_exec_callback_type, py::object pfn_event_notify)
      {
        // The reason for doing this via a thread is that we're able to wait on
        // acquiring the GIL. (which we can't in the callback)

        std::unique_ptr<event_callback_info_t> cb_info_holder(
            new event_callback_info_t(
              handle_from_new_ptr(new event(*this)),
              pfn_event_notify));
        event_callback_info_t *cb_info = cb_info_holder.get();

        std::thread notif_thread([cb_info]()
            {
              {
                std::unique_lock<std::mutex> ulk(cb_info->m_mutex);
                cb_info->m_condvar.wait(
                    ulk,
                    [&](){ return cb_info->m_notify_thread_wakeup_is_genuine; });

                // ulk no longer held here, cb_info ready for deletion
              }

              {
                py::gil_scoped_acquire acquire;

                if (cb_info->m_set_callback_suceeded)
                {
                  try {
                    cb_info->m_py_callback(
                        // cb_info->m_py_event,
                        cb_info->m_command_exec_status);
                  }
                  catch (std::exception &exc)
                  {
                    std::cerr
                    << "[pyopencl] event callback handler threw an exception, ignoring: "
                    << exc.what()
                    << std::endl;
                  }
                }

                // Need to hold GIL to delete py::object instances in
                // event_callback_info_t
                delete cb_info;
              }
            });
        // Thread is away--it is now its responsibility to free cb_info.
        cb_info_holder.release();

        // notif_thread should no longer be coupled to the lifetime of the thread.
        notif_thread.detach();

        try
        {
          PYOPENCL_CALL_GUARDED(clSetEventCallback, (
                data(), command_exec_callback_type, &event::evt_callback, cb_info));
        }
        catch (...) {
          // Setting the callback did not succeed. The thread would never
          // be woken up. Wake it up to let it know that it can stop.
          {
            std::lock_guard<std::mutex> lg(cb_info->m_mutex);
            cb_info->m_set_callback_suceeded = false;
          }
          cb_info->m_condvar.notify_one();
          throw;
        }
      }
      // }}}
#endif
  };

  class nanny_event : public event
  {
    // In addition to everything an event does, the nanny event holds a reference
    // to a Python object and waits for its own completion upon destruction.

    protected:
      std::unique_ptr<py_buffer_wrapper> m_ward;
      nanny_event(cl_event evt, bool retain, std::unique_ptr<py_buffer_wrapper> &ward)
        : event(evt, retain), m_ward(std::move(ward))
      {
        // It appears that Pybind can get very confused if we release the GIL here:
        // https://github.com/inducer/pyopencl/issues/296
        wait_during_cleanup_without_releasing_the_gil();
      }

      py::object get_ward() const
      {
        if (m_ward.get())
        {
          return py::reinterpret_borrow<py::object>(m_ward->m_buf.obj);
          return py::none();
      }

      virtual void wait()
      {
        event::wait();
        m_ward.reset();
      }

      virtual void wait_during_cleanup_without_releasing_the_gil()
      {
        event::wait_during_cleanup_without_releasing_the_gil();
        m_ward.reset();
      }
  };




  inline
  void wait_for_events(py::object events)
  {
    cl_uint num_events_in_wait_list = 0;
    std::vector<cl_event> event_wait_list(len(events));

    for (py::handle evt: events)
      event_wait_list[num_events_in_wait_list++] =
        evt.cast<event &>().data();

    PYOPENCL_CALL_GUARDED_THREADED(clWaitForEvents, (
          PYOPENCL_WAITLIST_ARGS));
  }




#if PYOPENCL_CL_VERSION >= 0x1020
  inline
  event *enqueue_marker_with_wait_list(command_queue &cq,
      py::object py_wait_for)
  {
    PYOPENCL_PARSE_WAIT_FOR;
    cl_event evt;

    PYOPENCL_CALL_GUARDED(clEnqueueMarkerWithWaitList, (
          cq.data(), PYOPENCL_WAITLIST_ARGS, &evt));

    PYOPENCL_RETURN_NEW_EVENT(evt);
  }

  inline
  event *enqueue_barrier_with_wait_list(command_queue &cq,
      py::object py_wait_for)
  {
    PYOPENCL_PARSE_WAIT_FOR;
    cl_event evt;

    PYOPENCL_CALL_GUARDED(clEnqueueBarrierWithWaitList,
        (cq.data(), PYOPENCL_WAITLIST_ARGS, &evt));

    PYOPENCL_RETURN_NEW_EVENT(evt);
  }
#endif


  // {{{ used internally for pre-OpenCL-1.2 contexts

  inline
  event *enqueue_marker(command_queue &cq)
  {
    cl_event evt;

    PYOPENCL_CALL_GUARDED(clEnqueueMarker, (
          cq.data(), &evt));

    PYOPENCL_RETURN_NEW_EVENT(evt);
  }

  inline
  void enqueue_wait_for_events(command_queue &cq, py::object py_events)
  {
    cl_uint num_events = 0;
    std::vector<cl_event> event_list(len(py_events));

    for (py::handle py_evt: py_events)
      event_list[num_events++] = py_evt.cast<event &>().data();

    PYOPENCL_CALL_GUARDED(clEnqueueWaitForEvents, (
Andreas Klöckner's avatar
Andreas Klöckner committed
          cq.data(), num_events, event_list.empty( ) ? nullptr : &event_list.front()));
  }

  inline
  void enqueue_barrier(command_queue &cq)
  {
    PYOPENCL_CALL_GUARDED(clEnqueueBarrier, (cq.data()));
  }

  // }}}


#if PYOPENCL_CL_VERSION >= 0x1010
  class user_event : public event
  {
    public:
      user_event(cl_event evt, bool retain)
        : event(evt, retain)
      { }

      void set_status(cl_int execution_status)
      {
        PYOPENCL_CALL_GUARDED(clSetUserEventStatus, (data(), execution_status));
      }
  };




  inline
  user_event *create_user_event(context &ctx)
  {
    cl_int status_code;
    PYOPENCL_PRINT_CALL_TRACE("clCreateUserEvent");
    cl_event evt = clCreateUserEvent(ctx.data(), &status_code);

    if (status_code != CL_SUCCESS)
      throw pyopencl::error("UserEvent", status_code);

    try
    {
      return new user_event(evt, false);
    }
    catch (...)
    {
      clReleaseEvent(evt);
      throw;
    }
  }

#endif

  // }}}

  // {{{ memory_object

  py::object create_mem_object_wrapper(cl_mem mem, bool retain);

  class memory_object_holder
  {
    public:
      virtual const cl_mem data() const = 0;

      PYOPENCL_EQUALITY_TESTS(memory_object_holder);

      size_t size() const
      {
        size_t param_value;
        PYOPENCL_CALL_GUARDED(clGetMemObjectInfo,
            (data(), CL_MEM_SIZE, sizeof(param_value), &param_value, 0));
        return param_value;
      }

      py::object get_info(cl_mem_info param_name) const;
  };




  class memory_object : noncopyable, public memory_object_holder
      typedef std::unique_ptr<py_buffer_wrapper> hostbuf_t;

    private:
      bool m_valid;
      cl_mem m_mem;
      hostbuf_t m_hostbuf;

    public:
      memory_object(cl_mem mem, bool retain, hostbuf_t hostbuf=hostbuf_t())
        : m_valid(true), m_mem(mem)
      {
        if (retain)
          PYOPENCL_CALL_GUARDED(clRetainMemObject, (mem));

        m_hostbuf = PYOPENCL_STD_MOVE_IF_NEW_BUF_INTF(hostbuf);
      }

      memory_object(memory_object &src)
        : m_valid(true), m_mem(src.m_mem),
        m_hostbuf(PYOPENCL_STD_MOVE_IF_NEW_BUF_INTF(src.m_hostbuf))
      {
        PYOPENCL_CALL_GUARDED(clRetainMemObject, (m_mem));
      }

      memory_object(memory_object_holder const &src)
        : m_valid(true), m_mem(src.data())
      {
        PYOPENCL_CALL_GUARDED(clRetainMemObject, (m_mem));
      }

      void release()
      {
        if (!m_valid)
            throw error("MemoryObject.free", CL_INVALID_VALUE,
                "trying to double-unref mem object");
        PYOPENCL_CALL_GUARDED_CLEANUP(clReleaseMemObject, (m_mem));
        m_valid = false;
      }

      virtual ~memory_object()
      {
        if (m_valid)
          release();
      }

      py::object hostbuf()
      {
        if (m_hostbuf.get())
          return py::reinterpret_borrow<py::object>(m_hostbuf->m_buf.obj);
          return py::none();
      }

      const cl_mem data() const
      { return m_mem; }

  };

#if PYOPENCL_CL_VERSION >= 0x1020
  inline
  event *enqueue_migrate_mem_objects(
      command_queue &cq,
      py::object py_mem_objects,
      cl_mem_migration_flags flags,
      py::object py_wait_for)
  {
    PYOPENCL_PARSE_WAIT_FOR;

    std::vector<cl_mem> mem_objects;
    for (py::handle mo: py_mem_objects)
      mem_objects.push_back(mo.cast<const memory_object &>().data());