Repository: numba/numba

Issue:
CUDA: `mapped_array` accepts a stream but does nothing with it
`mapped_array` accepts a stream parameter:

```python
def mapped_array(shape, dtype=np.float, strides=None, order='C', stream=0,
                 portable=False, wc=False):
```

However, it only passes it to `mappedview.device_setup`:

```python
    managedview.device_setup(buffer, stream=stream)
```

which then does nothing with it:

```python
class MappedNDArray(DeviceNDArrayBase, np.ndarray):
    """ 
    A host array that uses CUDA mapped memory.
    """

    def device_setup(self, gpu_data, stream=0):
        self.gpu_data = gpu_data
```

I think this is misleading - it should either set its default stream to the provided stream (I think this is preferable, to facilitate the use of async operations for non-default streams) or not accept a stream parameter (if there's something preventing the use of async operations on these allocations anyway).

<!--

Thanks for opening an issue! To help the Numba team handle your information
efficiently, please first ensure that there is no other issue present that
already describes the issue you have
(search at https://github.com/numba/numba/issues?&q=is%3Aissue).

-->

## Reporting a bug

<!--

Before submitting a bug report please ensure that you can check off these boxes:

-->

- [ ] I have tried using the latest released version of Numba (most recent is
 visible in the change log (https://github.com/numba/numba/blob/master/CHANGE_LOG).
- [ ] I have included below a minimal working reproducer (if you are unsure how
 to write one see http://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports).

<!--

Please include details of the bug here, including, if applicable, what you
expected to happen!

-->



Discussion:
A concrete example of problematic code related to this, reported by @c200chromebook:

```python
import pickle
from numba import cuda
import numba as nb
import numpy as np
b = cuda.mapped_array(1, dtype=np.float)
v = b.view('u1')
pickle.dumps(b)
```

which fails due to the lack of stream.

Resolving patch:
diff --git a/numba/cuda/cudadrv/devicearray.py b/numba/cuda/cudadrv/devicearray.py
--- a/numba/cuda/cudadrv/devicearray.py
+++ b/numba/cuda/cudadrv/devicearray.py
@@ -681,6 +681,7 @@ class MappedNDArray(DeviceNDArrayBase, np.ndarray):
 
     def device_setup(self, gpu_data, stream=0):
         self.gpu_data = gpu_data
+        self.stream = stream
 
 
 class ManagedNDArray(DeviceNDArrayBase, np.ndarray):
@@ -690,6 +691,7 @@ class ManagedNDArray(DeviceNDArrayBase, np.ndarray):
 
     def device_setup(self, gpu_data, stream=0):
         self.gpu_data = gpu_data
+        self.stream = stream
 
 
 def from_array_like(ary, stream=0, gpu_data=None):


Test patch:
diff --git a/numba/cuda/tests/cudapy/test_cuda_array_interface.py b/numba/cuda/tests/cudapy/test_cuda_array_interface.py
--- a/numba/cuda/tests/cudapy/test_cuda_array_interface.py
+++ b/numba/cuda/tests/cudapy/test_cuda_array_interface.py
@@ -4,7 +4,7 @@
 from numba import cuda
 from numba.cuda.testing import unittest, ContextResettingTestCase, ForeignArray
 from numba.cuda.testing import skip_on_cudasim, skip_if_external_memmgr
-from numba.tests.support import override_config
+from numba.tests.support import linux_only, override_config
 from unittest.mock import call, patch
 
 
@@ -261,12 +261,32 @@ def test_produce_no_stream(self):
         c_arr = cuda.device_array(10)
         self.assertIsNone(c_arr.__cuda_array_interface__['stream'])
 
+        mapped_arr = cuda.mapped_array(10)
+        self.assertIsNone(mapped_arr.__cuda_array_interface__['stream'])
+
+    @linux_only
+    def test_produce_managed_no_stream(self):
+        managed_arr = cuda.managed_array(10)
+        self.assertIsNone(managed_arr.__cuda_array_interface__['stream'])
+
     def test_produce_stream(self):
         s = cuda.stream()
         c_arr = cuda.device_array(10, stream=s)
         cai_stream = c_arr.__cuda_array_interface__['stream']
         self.assertEqual(s.handle.value, cai_stream)
 
+        s = cuda.stream()
+        mapped_arr = cuda.mapped_array(10, stream=s)
+        cai_stream = mapped_arr.__cuda_array_interface__['stream']
+        self.assertEqual(s.handle.value, cai_stream)
+
+    @linux_only
+    def test_produce_managed_stream(self):
+        s = cuda.stream()
+        managed_arr = cuda.managed_array(10, stream=s)
+        cai_stream = managed_arr.__cuda_array_interface__['stream']
+        self.assertEqual(s.handle.value, cai_stream)
+
     def test_consume_no_stream(self):
         # Create a foreign array with no stream
         f_arr = ForeignArray(cuda.device_array(10))