Show More
@@ -0,0 +1,53 b'' | |||
|
1 | use cpython::{PyBytes, Python}; | |
|
2 | ||
|
3 | /// Safe abstraction over a `PyBytes` together with the `&[u8]` slice | |
|
4 | /// that borrows it. Implements `Deref<Target = [u8]>`. | |
|
5 | /// | |
|
6 | /// Calling `PyBytes::data` requires a GIL marker but we want to access the | |
|
7 | /// data in a thread that (ideally) does not need to acquire the GIL. | |
|
8 | /// This type allows separating the call an the use. | |
|
9 | /// | |
|
10 | /// It also enables using a (wrapped) `PyBytes` in GIL-unaware generic code. | |
|
11 | pub struct PyBytesDeref { | |
|
12 | #[allow(unused)] | |
|
13 | keep_alive: PyBytes, | |
|
14 | ||
|
15 | /// Borrows the buffer inside `self.keep_alive`, | |
|
16 | /// but the borrow-checker cannot express self-referential structs. | |
|
17 | data: *const [u8], | |
|
18 | } | |
|
19 | ||
|
20 | impl PyBytesDeref { | |
|
21 | pub fn new(py: Python, bytes: PyBytes) -> Self { | |
|
22 | Self { | |
|
23 | data: bytes.data(py), | |
|
24 | keep_alive: bytes, | |
|
25 | } | |
|
26 | } | |
|
27 | ||
|
28 | pub fn unwrap(self) -> PyBytes { | |
|
29 | self.keep_alive | |
|
30 | } | |
|
31 | } | |
|
32 | ||
|
33 | impl std::ops::Deref for PyBytesDeref { | |
|
34 | type Target = [u8]; | |
|
35 | ||
|
36 | fn deref(&self) -> &[u8] { | |
|
37 | // Safety: the raw pointer is valid as long as the PyBytes is still | |
|
38 | // alive, and the returned slice borrows `self`. | |
|
39 | unsafe { &*self.data } | |
|
40 | } | |
|
41 | } | |
|
42 | ||
|
43 | fn require_send<T: Send>() {} | |
|
44 | ||
|
45 | #[allow(unused)] | |
|
46 | fn static_assert_pybytes_is_send() { | |
|
47 | require_send::<PyBytes>; | |
|
48 | } | |
|
49 | ||
|
50 | // Safety: PyBytes is Send. Raw pointers are not by default, | |
|
51 | // but here sending one to another thread is fine since we ensure it stays | |
|
52 | // valid. | |
|
53 | unsafe impl Send for PyBytesDeref {} |
@@ -13,58 +13,7 b' use hg::copy_tracing::ChangedFiles;' | |||
|
13 | 13 | use hg::copy_tracing::CombineChangesetCopies; |
|
14 | 14 | use hg::Revision; |
|
15 | 15 | |
|
16 |
use |
|
|
17 | ||
|
18 | // Module to encapsulate private fields | |
|
19 | mod pybytes_with_data { | |
|
20 | use cpython::{PyBytes, Python}; | |
|
21 | ||
|
22 | /// Safe abstraction over a `PyBytes` together with the `&[u8]` slice | |
|
23 | /// that borrows it. | |
|
24 | /// | |
|
25 | /// Calling `PyBytes::data` requires a GIL marker but we want to access the | |
|
26 | /// data in a thread that (ideally) does not need to acquire the GIL. | |
|
27 | /// This type allows separating the call an the use. | |
|
28 | pub(super) struct PyBytesWithData { | |
|
29 | #[allow(unused)] | |
|
30 | keep_alive: PyBytes, | |
|
31 | ||
|
32 | /// Borrows the buffer inside `self.keep_alive`, | |
|
33 | /// but the borrow-checker cannot express self-referential structs. | |
|
34 | data: *const [u8], | |
|
35 | } | |
|
36 | ||
|
37 | fn require_send<T: Send>() {} | |
|
38 | ||
|
39 | #[allow(unused)] | |
|
40 | fn static_assert_pybytes_is_send() { | |
|
41 | require_send::<PyBytes>; | |
|
42 | } | |
|
43 | ||
|
44 | // Safety: PyBytes is Send. Raw pointers are not by default, | |
|
45 | // but here sending one to another thread is fine since we ensure it stays | |
|
46 | // valid. | |
|
47 | unsafe impl Send for PyBytesWithData {} | |
|
48 | ||
|
49 | impl PyBytesWithData { | |
|
50 | pub fn new(py: Python, bytes: PyBytes) -> Self { | |
|
51 | Self { | |
|
52 | data: bytes.data(py), | |
|
53 | keep_alive: bytes, | |
|
54 | } | |
|
55 | } | |
|
56 | ||
|
57 | pub fn data(&self) -> &[u8] { | |
|
58 | // Safety: the raw pointer is valid as long as the PyBytes is still | |
|
59 | // alive, and the returned slice borrows `self`. | |
|
60 | unsafe { &*self.data } | |
|
61 | } | |
|
62 | ||
|
63 | pub fn unwrap(self) -> PyBytes { | |
|
64 | self.keep_alive | |
|
65 | } | |
|
66 | } | |
|
67 | } | |
|
16 | use crate::pybytes_deref::PyBytesDeref; | |
|
68 | 17 | |
|
69 | 18 | /// Combines copies information contained into revision `revs` to build a copy |
|
70 | 19 | /// map. |
@@ -123,7 +72,7 b' pub fn combine_changeset_copies_wrapper(' | |||
|
123 | 72 | // |
|
124 | 73 | // TODO: tweak the bound? |
|
125 | 74 | let (rev_info_sender, rev_info_receiver) = |
|
126 |
crossbeam_channel::bounded::<RevInfo<PyBytes |
|
|
75 | crossbeam_channel::bounded::<RevInfo<PyBytesDeref>>(1000); | |
|
127 | 76 | |
|
128 | 77 | // This channel (going the other way around) however is unbounded. |
|
129 | 78 | // If they were both bounded, there might potentially be deadlocks |
@@ -143,7 +92,7 b' pub fn combine_changeset_copies_wrapper(' | |||
|
143 | 92 | CombineChangesetCopies::new(children_count); |
|
144 | 93 | for (rev, p1, p2, opt_bytes) in rev_info_receiver { |
|
145 | 94 | let files = match &opt_bytes { |
|
146 |
Some(raw) => ChangedFiles::new(raw. |
|
|
95 | Some(raw) => ChangedFiles::new(raw.as_ref()), | |
|
147 | 96 | // Python None was extracted to Option::None, |
|
148 | 97 | // meaning there was no copy data. |
|
149 | 98 | None => ChangedFiles::new_empty(), |
@@ -169,7 +118,7 b' pub fn combine_changeset_copies_wrapper(' | |||
|
169 | 118 | |
|
170 | 119 | for rev_info in revs_info { |
|
171 | 120 | let (rev, p1, p2, opt_bytes) = rev_info?; |
|
172 |
let opt_bytes = opt_bytes.map(|b| PyBytes |
|
|
121 | let opt_bytes = opt_bytes.map(|b| PyBytesDeref::new(py, b)); | |
|
173 | 122 | |
|
174 | 123 | // We’d prefer to avoid the child thread calling into Python code, |
|
175 | 124 | // but this avoids a potential deadlock on the GIL if it does: |
General Comments 0
You need to be logged in to leave comments.
Login now