##// END OF EJS Templates
rust-cpython: change license of ref_sharing.rs to MIT...
Yuya Nishihara -
r43352:fdfe5cfb default
parent child Browse files
Show More
@@ -1,396 +1,411 b''
1 // macros.rs
1 // ref_sharing.rs
2 2 //
3 3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 4 //
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to
7 // deal in the Software without restriction, including without limitation the
8 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 // sell copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 // IN THE SOFTWARE.
7 22
8 23 //! Macros for use in the `hg-cpython` bridge library.
9 24
10 25 use crate::exceptions::AlreadyBorrowed;
11 26 use cpython::{PyResult, Python};
12 27 use std::cell::{Cell, Ref, RefCell, RefMut};
13 28
14 29 /// Manages the shared state between Python and Rust
15 30 #[derive(Default)]
16 31 pub struct PySharedState {
17 32 leak_count: Cell<usize>,
18 33 mutably_borrowed: Cell<bool>,
19 34 }
20 35
21 36 impl PySharedState {
22 37 pub fn borrow_mut<'a, T>(
23 38 &'a self,
24 39 py: Python<'a>,
25 40 pyrefmut: RefMut<'a, T>,
26 41 ) -> PyResult<PyRefMut<'a, T>> {
27 42 if self.mutably_borrowed.get() {
28 43 return Err(AlreadyBorrowed::new(
29 44 py,
30 45 "Cannot borrow mutably while there exists another \
31 46 mutable reference in a Python object",
32 47 ));
33 48 }
34 49 match self.leak_count.get() {
35 50 0 => {
36 51 self.mutably_borrowed.replace(true);
37 52 Ok(PyRefMut::new(py, pyrefmut, self))
38 53 }
39 54 // TODO
40 55 // For now, this works differently than Python references
41 56 // in the case of iterators.
42 57 // Python does not complain when the data an iterator
43 58 // points to is modified if the iterator is never used
44 59 // afterwards.
45 60 // Here, we are stricter than this by refusing to give a
46 61 // mutable reference if it is already borrowed.
47 62 // While the additional safety might be argued for, it
48 63 // breaks valid programming patterns in Python and we need
49 64 // to fix this issue down the line.
50 65 _ => Err(AlreadyBorrowed::new(
51 66 py,
52 67 "Cannot borrow mutably while there are \
53 68 immutable references in Python objects",
54 69 )),
55 70 }
56 71 }
57 72
58 73 /// Return a reference to the wrapped data with an artificial static
59 74 /// lifetime.
60 75 /// We need to be protected by the GIL for thread-safety.
61 76 ///
62 77 /// # Safety
63 78 ///
64 79 /// This is highly unsafe since the lifetime of the given data can be
65 80 /// extended. Do not call this function directly.
66 81 pub unsafe fn leak_immutable<T>(
67 82 &self,
68 83 py: Python,
69 84 data: &PySharedRefCell<T>,
70 85 ) -> PyResult<&'static T> {
71 86 if self.mutably_borrowed.get() {
72 87 return Err(AlreadyBorrowed::new(
73 88 py,
74 89 "Cannot borrow immutably while there is a \
75 90 mutable reference in Python objects",
76 91 ));
77 92 }
78 93 let ptr = data.as_ptr();
79 94 self.leak_count.replace(self.leak_count.get() + 1);
80 95 Ok(&*ptr)
81 96 }
82 97
83 98 /// # Safety
84 99 ///
85 100 /// It's unsafe to update the reference count without knowing the
86 101 /// reference is deleted. Do not call this function directly.
87 102 pub unsafe fn decrease_leak_count(&self, _py: Python, mutable: bool) {
88 103 if mutable {
89 104 assert_eq!(self.leak_count.get(), 0);
90 105 assert!(self.mutably_borrowed.get());
91 106 self.mutably_borrowed.replace(false);
92 107 } else {
93 108 let count = self.leak_count.get();
94 109 assert!(count > 0);
95 110 self.leak_count.replace(count - 1);
96 111 }
97 112 }
98 113 }
99 114
100 115 /// `RefCell` wrapper to be safely used in conjunction with `PySharedState`.
101 116 ///
102 117 /// Only immutable operation is allowed through this interface.
103 118 #[derive(Debug)]
104 119 pub struct PySharedRefCell<T> {
105 120 inner: RefCell<T>,
106 121 }
107 122
108 123 impl<T> PySharedRefCell<T> {
109 124 pub const fn new(value: T) -> PySharedRefCell<T> {
110 125 Self {
111 126 inner: RefCell::new(value),
112 127 }
113 128 }
114 129
115 130 pub fn borrow(&self) -> Ref<T> {
116 131 // py_shared_state isn't involved since
117 132 // - inner.borrow() would fail if self is mutably borrowed,
118 133 // - and inner.borrow_mut() would fail while self is borrowed.
119 134 self.inner.borrow()
120 135 }
121 136
122 137 pub fn as_ptr(&self) -> *mut T {
123 138 self.inner.as_ptr()
124 139 }
125 140
126 141 pub unsafe fn borrow_mut(&self) -> RefMut<T> {
127 142 // must be borrowed by self.py_shared_state(py).borrow_mut().
128 143 self.inner.borrow_mut()
129 144 }
130 145 }
131 146
132 147 /// Holds a mutable reference to data shared between Python and Rust.
133 148 pub struct PyRefMut<'a, T> {
134 149 inner: RefMut<'a, T>,
135 150 py_shared_state: &'a PySharedState,
136 151 }
137 152
138 153 impl<'a, T> PyRefMut<'a, T> {
139 154 // Must be constructed by PySharedState after checking its leak_count.
140 155 // Otherwise, drop() would incorrectly update the state.
141 156 fn new(
142 157 _py: Python<'a>,
143 158 inner: RefMut<'a, T>,
144 159 py_shared_state: &'a PySharedState,
145 160 ) -> Self {
146 161 Self {
147 162 inner,
148 163 py_shared_state,
149 164 }
150 165 }
151 166 }
152 167
153 168 impl<'a, T> std::ops::Deref for PyRefMut<'a, T> {
154 169 type Target = RefMut<'a, T>;
155 170
156 171 fn deref(&self) -> &Self::Target {
157 172 &self.inner
158 173 }
159 174 }
160 175 impl<'a, T> std::ops::DerefMut for PyRefMut<'a, T> {
161 176 fn deref_mut(&mut self) -> &mut Self::Target {
162 177 &mut self.inner
163 178 }
164 179 }
165 180
166 181 impl<'a, T> Drop for PyRefMut<'a, T> {
167 182 fn drop(&mut self) {
168 183 let gil = Python::acquire_gil();
169 184 let py = gil.python();
170 185 unsafe {
171 186 self.py_shared_state.decrease_leak_count(py, true);
172 187 }
173 188 }
174 189 }
175 190
176 191 /// Allows a `py_class!` generated struct to share references to one of its
177 192 /// data members with Python.
178 193 ///
179 194 /// # Warning
180 195 ///
181 196 /// The targeted `py_class!` needs to have the
182 197 /// `data py_shared_state: PySharedState;` data attribute to compile.
183 198 /// A better, more complicated macro is needed to automatically insert it,
184 199 /// but this one is not yet really battle tested (what happens when
185 200 /// multiple references are needed?). See the example below.
186 201 ///
187 202 /// TODO allow Python container types: for now, integration with the garbage
188 203 /// collector does not extend to Rust structs holding references to Python
189 204 /// objects. Should the need surface, `__traverse__` and `__clear__` will
190 205 /// need to be written as per the `rust-cpython` docs on GC integration.
191 206 ///
192 207 /// # Parameters
193 208 ///
194 209 /// * `$name` is the same identifier used in for `py_class!` macro call.
195 210 /// * `$inner_struct` is the identifier of the underlying Rust struct
196 211 /// * `$data_member` is the identifier of the data member of `$inner_struct`
197 212 /// that will be shared.
198 213 /// * `$leaked` is the identifier to give to the struct that will manage
199 214 /// references to `$name`, to be used for example in other macros like
200 215 /// `py_shared_iterator`.
201 216 ///
202 217 /// # Example
203 218 ///
204 219 /// ```
205 220 /// struct MyStruct {
206 221 /// inner: Vec<u32>;
207 222 /// }
208 223 ///
209 224 /// py_class!(pub class MyType |py| {
210 225 /// data inner: PySharedRefCell<MyStruct>;
211 226 /// data py_shared_state: PySharedState;
212 227 /// });
213 228 ///
214 229 /// py_shared_ref!(MyType, MyStruct, inner, MyTypeLeakedRef);
215 230 /// ```
216 231 macro_rules! py_shared_ref {
217 232 (
218 233 $name: ident,
219 234 $inner_struct: ident,
220 235 $data_member: ident,
221 236 $leaked: ident,
222 237 ) => {
223 238 impl $name {
224 239 fn borrow_mut<'a>(
225 240 &'a self,
226 241 py: Python<'a>,
227 242 ) -> PyResult<crate::ref_sharing::PyRefMut<'a, $inner_struct>>
228 243 {
229 244 // assert $data_member type
230 245 use crate::ref_sharing::PySharedRefCell;
231 246 let data: &PySharedRefCell<_> = self.$data_member(py);
232 247 self.py_shared_state(py)
233 248 .borrow_mut(py, unsafe { data.borrow_mut() })
234 249 }
235 250
236 251 /// Returns a leaked reference and its management object.
237 252 ///
238 253 /// # Safety
239 254 ///
240 255 /// It's up to you to make sure that the management object lives
241 256 /// longer than the leaked reference. Otherwise, you'll get a
242 257 /// dangling reference.
243 258 unsafe fn leak_immutable<'a>(
244 259 &'a self,
245 260 py: Python<'a>,
246 261 ) -> PyResult<($leaked, &'static $inner_struct)> {
247 262 // assert $data_member type
248 263 use crate::ref_sharing::PySharedRefCell;
249 264 let data: &PySharedRefCell<_> = self.$data_member(py);
250 265 let static_ref =
251 266 self.py_shared_state(py).leak_immutable(py, data)?;
252 267 let leak_handle = $leaked::new(py, self);
253 268 Ok((leak_handle, static_ref))
254 269 }
255 270 }
256 271
257 272 /// Manage immutable references to `$name` leaked into Python
258 273 /// iterators.
259 274 ///
260 275 /// In truth, this does not represent leaked references themselves;
261 276 /// it is instead useful alongside them to manage them.
262 277 pub struct $leaked {
263 278 inner: $name,
264 279 }
265 280
266 281 impl $leaked {
267 282 // Marked as unsafe so client code wouldn't construct $leaked
268 283 // struct by mistake. Its drop() is unsafe.
269 284 unsafe fn new(py: Python, inner: &$name) -> Self {
270 285 Self {
271 286 inner: inner.clone_ref(py),
272 287 }
273 288 }
274 289 }
275 290
276 291 impl Drop for $leaked {
277 292 fn drop(&mut self) {
278 293 let gil = Python::acquire_gil();
279 294 let py = gil.python();
280 295 let state = self.inner.py_shared_state(py);
281 296 unsafe {
282 297 state.decrease_leak_count(py, false);
283 298 }
284 299 }
285 300 }
286 301 };
287 302 }
288 303
289 304 /// Defines a `py_class!` that acts as a Python iterator over a Rust iterator.
290 305 ///
291 306 /// TODO: this is a bit awkward to use, and a better (more complicated)
292 307 /// procedural macro would simplify the interface a lot.
293 308 ///
294 309 /// # Parameters
295 310 ///
296 311 /// * `$name` is the identifier to give to the resulting Rust struct.
297 312 /// * `$leaked` corresponds to `$leaked` in the matching `py_shared_ref!` call.
298 313 /// * `$iterator_type` is the type of the Rust iterator.
299 314 /// * `$success_func` is a function for processing the Rust `(key, value)`
300 315 /// tuple on iteration success, turning it into something Python understands.
301 316 /// * `$success_func` is the return type of `$success_func`
302 317 ///
303 318 /// # Example
304 319 ///
305 320 /// ```
306 321 /// struct MyStruct {
307 322 /// inner: HashMap<Vec<u8>, Vec<u8>>;
308 323 /// }
309 324 ///
310 325 /// py_class!(pub class MyType |py| {
311 326 /// data inner: PySharedRefCell<MyStruct>;
312 327 /// data py_shared_state: PySharedState;
313 328 ///
314 329 /// def __iter__(&self) -> PyResult<MyTypeItemsIterator> {
315 330 /// let (leak_handle, leaked_ref) = unsafe { self.leak_immutable(py)? };
316 331 /// MyTypeItemsIterator::from_inner(
317 332 /// py,
318 333 /// leak_handle,
319 334 /// leaked_ref.iter(),
320 335 /// )
321 336 /// }
322 337 /// });
323 338 ///
324 339 /// impl MyType {
325 340 /// fn translate_key_value(
326 341 /// py: Python,
327 342 /// res: (&Vec<u8>, &Vec<u8>),
328 343 /// ) -> PyResult<Option<(PyBytes, PyBytes)>> {
329 344 /// let (f, entry) = res;
330 345 /// Ok(Some((
331 346 /// PyBytes::new(py, f),
332 347 /// PyBytes::new(py, entry),
333 348 /// )))
334 349 /// }
335 350 /// }
336 351 ///
337 352 /// py_shared_ref!(MyType, MyStruct, inner, MyTypeLeakedRef);
338 353 ///
339 354 /// py_shared_iterator!(
340 355 /// MyTypeItemsIterator,
341 356 /// MyTypeLeakedRef,
342 357 /// HashMap<'static, Vec<u8>, Vec<u8>>,
343 358 /// MyType::translate_key_value,
344 359 /// Option<(PyBytes, PyBytes)>
345 360 /// );
346 361 /// ```
347 362 macro_rules! py_shared_iterator {
348 363 (
349 364 $name: ident,
350 365 $leaked: ident,
351 366 $iterator_type: ty,
352 367 $success_func: expr,
353 368 $success_type: ty
354 369 ) => {
355 370 py_class!(pub class $name |py| {
356 371 data inner: RefCell<Option<$leaked>>;
357 372 data it: RefCell<$iterator_type>;
358 373
359 374 def __next__(&self) -> PyResult<$success_type> {
360 375 let mut inner_opt = self.inner(py).borrow_mut();
361 376 if inner_opt.is_some() {
362 377 match self.it(py).borrow_mut().next() {
363 378 None => {
364 379 // replace Some(inner) by None, drop $leaked
365 380 inner_opt.take();
366 381 Ok(None)
367 382 }
368 383 Some(res) => {
369 384 $success_func(py, res)
370 385 }
371 386 }
372 387 } else {
373 388 Ok(None)
374 389 }
375 390 }
376 391
377 392 def __iter__(&self) -> PyResult<Self> {
378 393 Ok(self.clone_ref(py))
379 394 }
380 395 });
381 396
382 397 impl $name {
383 398 pub fn from_inner(
384 399 py: Python,
385 400 leaked: $leaked,
386 401 it: $iterator_type
387 402 ) -> PyResult<Self> {
388 403 Self::create_instance(
389 404 py,
390 405 RefCell::new(Some(leaked)),
391 406 RefCell::new(it)
392 407 )
393 408 }
394 409 }
395 410 };
396 411 }
General Comments 0
You need to be logged in to leave comments. Login now