Nektar++
SharedArray.cpp
Go to the documentation of this file.
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 // File: SharedArray.cpp
4 //
5 // For more information, please see: http://www.nektar.info
6 //
7 // The MIT License
8 //
9 // Copyright (c) 2006 Scientific Computing and Imaging Institute,
10 // University of Utah (USA) and Department of Aeronautics, Imperial
11 // College London (UK).
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining a
14 // copy of this software and associated documentation files (the "Software"),
15 // to deal in the Software without restriction, including without limitation
16 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
17 // and/or sell copies of the Software, and to permit persons to whom the
18 // Software is furnished to do so, subject to the following conditions:
19 //
20 // The above copyright notice and this permission notice shall be included
21 // in all copies or substantial portions of the Software.
22 //
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 // DEALINGS IN THE SOFTWARE.
30 //
31 // Description: Python wrapper for ShareArray.
32 //
33 ///////////////////////////////////////////////////////////////////////////////
34 
37 
38 #include <type_traits>
39 
40 using namespace Nektar;
41 using namespace Nektar::LibUtilities;
42 
43 #if PY_MAJOR_VERSION == 2
44 template<typename T>
45 void CapsuleDestructor(void *ptr)
46 {
47  Array<OneD, T> *tmp = (Array<OneD, T> *)ptr;
48  delete tmp;
49 }
50 #else
51 template<typename T>
52 void CapsuleDestructor(PyObject *ptr)
53 {
54  Array<OneD, T> *tmp = (Array<OneD, T> *)PyCapsule_GetPointer(ptr, 0);
55  delete tmp;
56 }
57 #endif
58 
59 template<typename T>
61 {
62  static PyObject *convert(Array<OneD, T> const &arr)
63  {
64  // Create a Python capsule to hold a pointer that contains a lightweight
65  // copy of arr. That way we guarantee Python will still have access to
66  // the memory allocated inside arr even if arr is deallocated in C++.
67 #if PY_MAJOR_VERSION == 2
68  py::object capsule(
69  py::handle<>(PyCObject_FromVoidPtr(
70  new Array<OneD, T>(arr), CapsuleDestructor<T>)));
71 #else
72  py::object capsule(
73  py::handle<>(PyCapsule_New(
74  new Array<OneD, T>(arr), 0,
75  (PyCapsule_Destructor)&CapsuleDestructor<T>)));
76 #endif
77  PyObject *tmp = py::incref(
78  np::from_data(
79  arr.data(), np::dtype::get_builtin<T>(),
80  py::make_tuple(arr.num_elements()), py::make_tuple(sizeof(T)),
81  capsule).ptr());
82 
83  return tmp;
84  }
85 };
86 
87 template <typename T>
89 {
91  {
92  py::converter::registry::push_back(
93  &convertible, &construct, py::type_id<Array<OneD, T> >());
94  }
95  static void *convertible(PyObject *objPtr)
96  {
97  try
98  {
99  py::object obj((py::handle<>(py::borrowed(objPtr))));
100  np::ndarray array = py::extract<np::ndarray>(obj);
101 
102  // Check data types match
103  np::dtype dtype = np::dtype::get_builtin<
104  typename boost::remove_const<T>::type>();
105  if (dtype != array.get_dtype())
106  {
107  return 0;
108  }
109 
110  // Check shape is 1D
111  if (array.get_nd() != 1)
112  {
113  return 0;
114  }
115  }
116  catch (boost::python::error_already_set&)
117  {
118  py::handle_exception();
119  PyErr_Clear();
120  return 0;
121  }
122 
123  return objPtr;
124  }
125 
126  static void decrement(void *objPtr)
127  {
128  if (!Py_IsInitialized())
129  {
130  // In deinitialisation phase, reference counters are not terribly
131  // robust; decremementing counters here can lead to segfaults during
132  // program exit in some cases.
133  return;
134  }
135 
136  // Otherwise decrement reference counter.
137  py::decref((PyObject *)objPtr);
138  }
139 
140  static void construct(
141  PyObject *objPtr,
142  py::converter::rvalue_from_python_stage1_data* data)
143  {
144  // This has to be a _borrowed_ reference, otherwise at the end of this
145  // scope it seems memory gets deallocated
146  py::object obj((py::handle<>(py::borrowed(objPtr))));
147  np::ndarray array = py::extract<np::ndarray>(obj);
148 
149  // If this array came from C++, extract the C++ array from PyCObject or
150  // PyCapsule and ensure that we set up the C++ array to have a reference
151  // to that object, so that it can be decremented as appropriate.
152  py::object base = array.get_base();
153  Array<OneD, T> *ptr = nullptr;
154 
155 #if PY_MAJOR_VERSION == 2
156  if (PyCObject_Check(base.ptr()))
157  {
158  ptr = reinterpret_cast<Array<OneD, T> *>(
159  PyCObject_AsVoidPtr(base.ptr()));
160  }
161 #else
162  if (PyCapsule_CheckExact(base.ptr()))
163  {
164  ptr = reinterpret_cast<Array<OneD, T> *>(
165  PyCapsule_GetPointer(base.ptr(), 0));
166  }
167 #endif
168 
169  void *storage = (
170  (py::converter::rvalue_from_python_storage<Array<OneD, T> >*)
171  data)->storage.bytes;
172  data->convertible = storage;
173 
174  // If array originated in C++, then we need to be careful to avoid
175  // circular references. We therefore take a step to 'convert' this to a
176  // Python array so that essentially the reference counting and memory
177  // cleanup is done from the Python side.
178  //
179  // 1) Calling ToPythonArray to point to this numpy array. This ensures
180  // that any C++ arrays that share this memory also know to call the
181  // appropriate decrement function.
182  // 2) Creating a new Array from ptr, since ptr will shortly be deleted.
183  // 3) We call set_base to let the numpy array own its own data. This
184  // will, at the end of the function and after `base` goes out of
185  // scope, lead to ptr being deleted.
186  //
187  // After all of this references should be consistent between the C++
188  // side and the Python side.
189  if (ptr != nullptr)
190  {
191  ptr->ToPythonArray((void *)objPtr, &decrement);
192  new (storage) Array<OneD, T>(*ptr);
193  array.set_base(py::object());
194  }
195  else
196  {
197  // Otherwise, construct OneD array from numpy array
198  using nonconst_t = typename std::remove_const<T>::type;
199  new (storage) Array<OneD, T>(
200  array.shape(0), (nonconst_t *)array.get_data(),
201  (void *)objPtr, &decrement);
202  }
203 
204  py::incref(objPtr);
205  }
206 };
207 
208 template<typename T>
210 {
211  py::to_python_converter<Array<OneD, const T>, OneDArrayToPython<const T> >();
212  py::to_python_converter<Array<OneD, T>, OneDArrayToPython<T> >();
213 
216 }
217 
218 template void export_SharedArray<double>();
static PyObject * convert(Array< OneD, T > const &arr)
Definition: SharedArray.cpp:62
void export_SharedArray()
static void decrement(void *objPtr)
void CapsuleDestructor(PyObject *ptr)
Definition: SharedArray.cpp:52
static void construct(PyObject *objPtr, py::converter::rvalue_from_python_stage1_data *data)
static void * convertible(PyObject *objPtr)
Definition: SharedArray.cpp:95
template void export_SharedArray< double >()