##// END OF EJS Templates
rust: Move PyBytesWithData out of copy-tracing code...
Simon Sapin -
r48765:8f031a27 default
parent child Browse files
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 self::pybytes_with_data::PyBytesWithData;
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<PyBytesWithData>>(1000);
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.data()),
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| PyBytesWithData::new(py, b));
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:
@@ -36,6 +36,7 b' pub mod dirstate;'
36 pub mod discovery;
36 pub mod discovery;
37 pub mod exceptions;
37 pub mod exceptions;
38 pub mod parsers;
38 pub mod parsers;
39 mod pybytes_deref;
39 pub mod revlog;
40 pub mod revlog;
40 pub mod utils;
41 pub mod utils;
41
42
General Comments 0
You need to be logged in to leave comments. Login now