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
40using namespace Nektar;
41using namespace Nektar::LibUtilities;
42
43#if PY_MAJOR_VERSION == 2
44template <typename T> void CapsuleDestructor(void *ptr)
45{
46 Array<OneD, T> *tmp = (Array<OneD, T> *)ptr;
47 delete tmp;
48}
49#else
50template <typename T> void CapsuleDestructor(PyObject *ptr)
51{
52 Array<OneD, T> *tmp = (Array<OneD, T> *)PyCapsule_GetPointer(ptr, nullptr);
53 delete tmp;
54}
55#endif
56
57template <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), nullptr,
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
82template <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 nullptr;
102 }
103
104 // Check shape is 1D
105 if (array.get_nd() != 1)
106 {
107 return nullptr;
108 }
109 }
110 catch (boost::python::error_already_set &)
111 {
112 py::handle_exception();
113 PyErr_Clear();
114 return nullptr;
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(), nullptr));
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
201template <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()
static PyObject * convert(Array< OneD, T > const &arr)
Definition: SharedArray.cpp:59
static void construct(PyObject *objPtr, py::converter::rvalue_from_python_stage1_data *data)
static void decrement(void *objPtr)
static void * convertible(PyObject *objPtr)
Definition: SharedArray.cpp:89