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 | use hg::copy_tracing::CombineChangesetCopies; |
|
13 | use hg::copy_tracing::CombineChangesetCopies; | |
14 | use hg::Revision; |
|
14 | use hg::Revision; | |
15 |
|
15 | |||
16 |
use |
|
16 | use crate::pybytes_deref::PyBytesDeref; | |
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 | } |
|
|||
68 |
|
17 | |||
69 | /// Combines copies information contained into revision `revs` to build a copy |
|
18 | /// Combines copies information contained into revision `revs` to build a copy | |
70 | /// map. |
|
19 | /// map. | |
@@ -123,7 +72,7 b' pub fn combine_changeset_copies_wrapper(' | |||||
123 | // |
|
72 | // | |
124 | // TODO: tweak the bound? |
|
73 | // TODO: tweak the bound? | |
125 | let (rev_info_sender, rev_info_receiver) = |
|
74 | let (rev_info_sender, rev_info_receiver) = | |
126 |
crossbeam_channel::bounded::<RevInfo<PyBytes |
|
75 | crossbeam_channel::bounded::<RevInfo<PyBytesDeref>>(1000); | |
127 |
|
76 | |||
128 | // This channel (going the other way around) however is unbounded. |
|
77 | // This channel (going the other way around) however is unbounded. | |
129 | // If they were both bounded, there might potentially be deadlocks |
|
78 | // If they were both bounded, there might potentially be deadlocks | |
@@ -143,7 +92,7 b' pub fn combine_changeset_copies_wrapper(' | |||||
143 | CombineChangesetCopies::new(children_count); |
|
92 | CombineChangesetCopies::new(children_count); | |
144 | for (rev, p1, p2, opt_bytes) in rev_info_receiver { |
|
93 | for (rev, p1, p2, opt_bytes) in rev_info_receiver { | |
145 | let files = match &opt_bytes { |
|
94 | let files = match &opt_bytes { | |
146 |
Some(raw) => ChangedFiles::new(raw. |
|
95 | Some(raw) => ChangedFiles::new(raw.as_ref()), | |
147 | // Python None was extracted to Option::None, |
|
96 | // Python None was extracted to Option::None, | |
148 | // meaning there was no copy data. |
|
97 | // meaning there was no copy data. | |
149 | None => ChangedFiles::new_empty(), |
|
98 | None => ChangedFiles::new_empty(), | |
@@ -169,7 +118,7 b' pub fn combine_changeset_copies_wrapper(' | |||||
169 |
|
118 | |||
170 | for rev_info in revs_info { |
|
119 | for rev_info in revs_info { | |
171 | let (rev, p1, p2, opt_bytes) = rev_info?; |
|
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 | // We’d prefer to avoid the child thread calling into Python code, |
|
123 | // We’d prefer to avoid the child thread calling into Python code, | |
175 | // but this avoids a potential deadlock on the GIL if it does: |
|
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