diff --git a/doc/source/driver.rst b/doc/source/driver.rst index b371526e51f1d97d63dedd660fa0d66921ad9258..3edbdc5acbb37c324612725304c1f81371de9864 100644 --- a/doc/source/driver.rst +++ b/doc/source/driver.rst @@ -657,6 +657,16 @@ Devices and Contexts Also make the newly-created context the current context. + .. method:: retain_primary_context() + + Return the :class:`Context` obtained by retaining the device's + primary context, which is the one used by the CUDA runtime API. + Unlike :meth:`Context.make_context`, the newly-created context is not made current. + + CUDA 7.0 and newer. + + .. versionadded:: 2020.1 + .. method:: can_access_peer(dev) CUDA 4.0 and newer. diff --git a/src/cpp/cuda.hpp b/src/cpp/cuda.hpp index da60bd5756508e599cb2c54d643e4c0f60d17036..78f49a7a96213f9bf2f21512a4245c92c6de7094 100644 --- a/src/cpp/cuda.hpp +++ b/src/cpp/cuda.hpp @@ -407,6 +407,7 @@ namespace pycuda // {{{ device class context; + class primary_context; class device { @@ -479,6 +480,9 @@ namespace pycuda } boost::shared_ptr make_context(unsigned int flags); +#if CUDAPP_CUDA_VERSION >= 7000 + boost::shared_ptr retain_primary_context(); +#endif CUdevice handle() const { return m_device; } @@ -559,7 +563,14 @@ namespace pycuda { return m_stack.top(); } void pop() - { m_stack.pop(); } + { + if (m_stack.empty()) + { + throw error("m_stack::pop", CUDA_ERROR_INVALID_CONTEXT, + "cannot pop context from empty stack"); + } + m_stack.pop(); + } void push(value_type v) { m_stack.push(v); } @@ -575,7 +586,7 @@ namespace pycuda class context : boost::noncopyable { - private: + protected: CUcontext m_context; bool m_valid; unsigned m_use_count; @@ -587,7 +598,7 @@ namespace pycuda m_thread(boost::this_thread::get_id()) { } - ~context() + virtual ~context() { if (m_valid) { @@ -637,21 +648,28 @@ namespace pycuda return result; } - void detach() + protected: + virtual void detach_internal() + { + CUDAPP_CALL_GUARDED_CLEANUP(cuCtxDetach, (m_context)); + } + + public: + virtual void detach() { if (m_valid) { bool active_before_destruction = current_context().get() == this; if (active_before_destruction) { - CUDAPP_CALL_GUARDED_CLEANUP(cuCtxDetach, (m_context)); + detach_internal(); } else { if (m_thread == boost::this_thread::get_id()) { CUDAPP_CALL_GUARDED_CLEANUP(cuCtxPushCurrent, (m_context)); - CUDAPP_CALL_GUARDED_CLEANUP(cuCtxDetach, (m_context)); + detach_internal(); /* pop is implicit in detach */ } else @@ -811,10 +829,26 @@ namespace pycuda friend void context_push(boost::shared_ptr ctx); friend boost::shared_ptr gl::make_gl_context(device const &dev, unsigned int flags); + friend class primary_context; }; + class primary_context : public context + { + protected: + CUdevice m_device; + public: + primary_context(CUcontext ctx, CUdevice dev) + : context (ctx), m_device(dev) + { } + protected: + virtual void detach_internal() + { + // Primary context comes from retainPrimaryContext. + CUDAPP_CALL_GUARDED_CLEANUP(cuDevicePrimaryCtxRelease, (m_device)); + } + }; inline boost::shared_ptr device::make_context(unsigned int flags) @@ -829,10 +863,15 @@ namespace pycuda } - - - - +#if CUDAPP_CUDA_VERSION >= 7000 + inline boost::shared_ptr device::retain_primary_context() + { + CUcontext ctx; + CUDAPP_CALL_GUARDED(cuDevicePrimaryCtxRetain, (&ctx, m_device)); + boost::shared_ptr result(new primary_context(ctx, m_device)); + return result; + } +#endif #if CUDAPP_CUDA_VERSION >= 2000 diff --git a/src/wrapper/wrap_cudadrv.cpp b/src/wrapper/wrap_cudadrv.cpp index dfa3d1cda929f86a186fb6788bd0e438128a0f6f..a27b836e3e7071777690303b1736e41f173d8f81 100644 --- a/src/wrapper/wrap_cudadrv.cpp +++ b/src/wrapper/wrap_cudadrv.cpp @@ -1099,6 +1099,10 @@ BOOST_PYTHON_MODULE(_driver) (py::args("self"), py::args("flags")=0)) #if CUDAPP_CUDA_VERSION >= 4000 .DEF_SIMPLE_METHOD(can_access_peer) +#endif +#if CUDAPP_CUDA_VERSION >= 7000 + .def("retain_primary_context", &cl::retain_primary_context, + (py::args("self"))) #endif ; } diff --git a/test/test_driver.py b/test/test_driver.py index b440eff09e9791ea7977afd1cba550f0cebe173f..9ff010e6e647d8e87f122c504c0f7282d531c1bb 100644 --- a/test/test_driver.py +++ b/test/test_driver.py @@ -644,6 +644,23 @@ class TestDriver: del mem_b ctx2.detach() + @mark_cuda_test + def test_additional_primary_context(self): + if drv.get_version() < (2, 0, 0): + return + if drv.get_version() >= (2, 2, 0) and drv.get_version() < (8,): + if drv.Context.get_device().compute_mode == drv.compute_mode.EXCLUSIVE: + return + + mem_a = drv.mem_alloc(50) + pctx = drv.Context.get_device().retain_primary_context() + pctx.push() + mem_b = drv.mem_alloc(60) + + del mem_a + del mem_b + pctx.detach() + @mark_cuda_test def test_3d_texture(self): # adapted from code by Nicolas Pinto