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> void CapsuleDestructor(void *ptr)
45 {
46  Array<OneD, T> *tmp = (Array<OneD, T> *)ptr;
47  delete tmp;
48 }
49 #else
50 template <typename T> void CapsuleDestructor(PyObject *ptr)
51 {
52  Array<OneD, T> *tmp = (Array<OneD, T> *)PyCapsule_GetPointer(ptr, 0);
53  delete tmp;
54 }
55 #endif
56 
57 template <typename T> struct OneDArrayToPython
58 {
59  static PyObject *convert(Array<OneD, T> const &arr)
60  {
61  // Create a Python capsule to hold a pointer that contains a lightweight
62  // copy of arr. That way we guarantee Python will still have access to
63  // the memory allocated inside arr even if arr is deallocated in C++.
64 #if PY_MAJOR_VERSION == 2
65  py::object capsule(py::handle<>(PyCObject_FromVoidPtr(
66  new Array<OneD, T>(arr), CapsuleDestructor<T>)));
67 #else
68  py::object capsule(py::handle<>(
69  PyCapsule_New(new Array<OneD, T>(arr), 0,
70  (PyCapsule_Destructor)&CapsuleDestructor<T>)));
71 #endif
72  PyObject *tmp =
73  py::incref(np::from_data(arr.data(), np::dtype::get_builtin<T>(),
74  py::make_tuple(arr.size()),
75  py::make_tuple(sizeof(T)), capsule)
76  .ptr());
77 
78  return tmp;
79  }
80 };
81 
82 template <typename T> struct PythonToOneDArray
83 {
85  {
86  py::converter::registry::push_back(&convertible, &construct,
87  py::type_id<Array<OneD, T>>());
88  }
89  static void *convertible(PyObject *objPtr)
90  {
91  try
92  {
93  py::object obj((py::handle<>(py::borrowed(objPtr))));
94  np::ndarray array = py::extract<np::ndarray>(obj);
95 
96  // Check data types match
97  np::dtype dtype =
98  np::dtype::get_builtin<typename boost::remove_const<T>::type>();
99  if (dtype != array.get_dtype())
100  {
101  return 0;
102  }
103 
104  // Check shape is 1D
105  if (array.get_nd() != 1)
106  {
107  return 0;
108  }
109  }
110  catch (boost::python::error_already_set &)
111  {
112  py::handle_exception();
113  PyErr_Clear();
114  return 0;
115  }
116 
117  return objPtr;
118  }
119 
120  static void decrement(void *objPtr)
121  {
122  if (!Py_IsInitialized())
123  {
124  // In deinitialisation phase, reference counters are not terribly
125  // robust; decremementing counters here can lead to segfaults during
126  // program exit in some cases.
127  return;
128  }
129 
130  // Otherwise decrement reference counter.
131  py::decref((PyObject *)objPtr);
132  }
133 
134  static void construct(PyObject *objPtr,
135  py::converter::rvalue_from_python_stage1_data *data)
136  {
137  // This has to be a _borrowed_ reference, otherwise at the end of this
138  // scope it seems memory gets deallocated
139  py::object obj((py::handle<>(py::borrowed(objPtr))));
140  np::ndarray array = py::extract<np::ndarray>(obj);
141 
142  // If this array came from C++, extract the C++ array from PyCObject or
143  // PyCapsule and ensure that we set up the C++ array to have a reference
144  // to that object, so that it can be decremented as appropriate.
145  py::object base = array.get_base();
146  Array<OneD, T> *ptr = nullptr;
147 
148 #if PY_MAJOR_VERSION == 2
149  if (PyCObject_Check(base.ptr()))
150  {
151  ptr = reinterpret_cast<Array<OneD, T> *>(
152  PyCObject_AsVoidPtr(base.ptr()));
153  }
154 #else
155  if (PyCapsule_CheckExact(base.ptr()))
156  {
157  ptr = reinterpret_cast<Array<OneD, T> *>(
158  PyCapsule_GetPointer(base.ptr(), 0));
159  }
160 #endif
161 
162  void *storage =
163  ((py::converter::rvalue_from_python_storage<Array<OneD, T>> *)data)
164  ->storage.bytes;
165  data->convertible = storage;
166 
167  // If array originated in C++, then we need to be careful to avoid
168  // circular references. We therefore take a step to 'convert' this to a
169  // Python array so that essentially the reference counting and memory
170  // cleanup is done from the Python side.
171  //
172  // 1) Calling ToPythonArray to point to this numpy array. This ensures
173  // that any C++ arrays that share this memory also know to call the
174  // appropriate decrement function.
175  // 2) Creating a new Array from ptr, since ptr will shortly be deleted.
176  // 3) We call set_base to let the numpy array own its own data. This
177  // will, at the end of the function and after `base` goes out of
178  // scope, lead to ptr being deleted.
179  //
180  // After all of this references should be consistent between the C++
181  // side and the Python side.
182  if (ptr != nullptr)
183  {
184  ptr->ToPythonArray((void *)objPtr, &decrement);
185  new (storage) Array<OneD, T>(*ptr);
186  array.set_base(py::object());
187  }
188  else
189  {
190  // Otherwise, construct OneD array from numpy array
191  using nonconst_t = typename std::remove_const<T>::type;
192  new (storage)
193  Array<OneD, T>(array.shape(0), (nonconst_t *)array.get_data(),
194  (void *)objPtr, &decrement);
195  }
196 
197  py::incref(objPtr);
198  }
199 };
200 
201 template <typename T> void export_SharedArray()
202 {
203  py::to_python_converter<Array<OneD, const T>, OneDArrayToPython<const T>>();
204  py::to_python_converter<Array<OneD, T>, OneDArrayToPython<T>>();
205 
208 }
209 
template void export_SharedArray< double >()
void CapsuleDestructor(PyObject *ptr)
Definition: SharedArray.cpp:50
void export_SharedArray()
The above copyright notice and this permission notice shall be included.
Definition: CoupledSolver.h:2
static PyObject * convert(Array< OneD, T > const &arr)
Definition: SharedArray.cpp:59
static void * convertible(PyObject *objPtr)
Definition: SharedArray.cpp:89
static void construct(PyObject *objPtr, py::converter::rvalue_from_python_stage1_data *data)
static void decrement(void *objPtr)