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