Show More
@@ -0,0 +1,121 b'' | |||||
|
1 | // cindex.rs | |||
|
2 | // | |||
|
3 | // Copyright 2018 Georges Racinet <gracinet@anybox.fr> | |||
|
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. | |||
|
7 | ||||
|
8 | //! Bindings to use the Index defined by the parsers C extension | |||
|
9 | //! | |||
|
10 | //! Ideally, we should use an Index entirely implemented in Rust, | |||
|
11 | //! but this will take some time to get there. | |||
|
12 | #[cfg(feature = "python27")] | |||
|
13 | extern crate python27_sys as python_sys; | |||
|
14 | #[cfg(feature = "python3")] | |||
|
15 | extern crate python3_sys as python_sys; | |||
|
16 | ||||
|
17 | use self::python_sys::PyCapsule_Import; | |||
|
18 | use cpython::{PyErr, PyObject, PyResult, Python}; | |||
|
19 | use hg::{Graph, GraphError, Revision}; | |||
|
20 | use libc::c_int; | |||
|
21 | use std::ffi::CStr; | |||
|
22 | use std::mem::transmute; | |||
|
23 | ||||
|
24 | type IndexParentsFn = unsafe extern "C" fn( | |||
|
25 | index: *mut python_sys::PyObject, | |||
|
26 | rev: c_int, | |||
|
27 | ps: *mut [c_int; 2], | |||
|
28 | ) -> c_int; | |||
|
29 | ||||
|
30 | /// A `Graph` backed up by objects and functions from revlog.c | |||
|
31 | /// | |||
|
32 | /// This implementation of the `Graph` trait, relies on (pointers to) | |||
|
33 | /// - the C index object (`index` member) | |||
|
34 | /// - the `index_get_parents()` function (`parents` member) | |||
|
35 | /// | |||
|
36 | /// # Safety | |||
|
37 | /// | |||
|
38 | /// The C index itself is mutable, and this Rust exposition is **not | |||
|
39 | /// protected by the GIL**, meaning that this construct isn't safe with respect | |||
|
40 | /// to Python threads. | |||
|
41 | /// | |||
|
42 | /// All callers of this `Index` must acquire the GIL and must not release it | |||
|
43 | /// while working. | |||
|
44 | /// | |||
|
45 | /// # TODO find a solution to make it GIL safe again. | |||
|
46 | /// | |||
|
47 | /// This is non trivial, and can wait until we have a clearer picture with | |||
|
48 | /// more Rust Mercurial constructs. | |||
|
49 | /// | |||
|
50 | /// One possibility would be to a `GILProtectedIndex` wrapper enclosing | |||
|
51 | /// a `Python<'p>` marker and have it be the one implementing the | |||
|
52 | /// `Graph` trait, but this would mean the `Graph` implementor would become | |||
|
53 | /// likely to change between subsequent method invocations of the `hg-core` | |||
|
54 | /// objects (a serious change of the `hg-core` API): | |||
|
55 | /// either exposing ways to mutate the `Graph`, or making it a non persistent | |||
|
56 | /// parameter in the relevant methods that need one. | |||
|
57 | /// | |||
|
58 | /// Another possibility would be to introduce an abstract lock handle into | |||
|
59 | /// the core API, that would be tied to `GILGuard` / `Python<'p>` | |||
|
60 | /// in the case of the `cpython` crate bindings yet could leave room for other | |||
|
61 | /// mechanisms in other contexts. | |||
|
62 | ||||
|
63 | pub struct Index { | |||
|
64 | index: PyObject, | |||
|
65 | parents: IndexParentsFn, | |||
|
66 | } | |||
|
67 | ||||
|
68 | impl Index { | |||
|
69 | pub fn new(py: Python, index: PyObject) -> PyResult<Self> { | |||
|
70 | Ok(Index { | |||
|
71 | index: index, | |||
|
72 | parents: decapsule_parents_fn(py)?, | |||
|
73 | }) | |||
|
74 | } | |||
|
75 | } | |||
|
76 | ||||
|
77 | impl Graph for Index { | |||
|
78 | /// wrap a call to the C extern parents function | |||
|
79 | fn parents(&self, rev: Revision) -> Result<[Revision; 2], GraphError> { | |||
|
80 | let mut res: [c_int; 2] = [0; 2]; | |||
|
81 | let code = unsafe { | |||
|
82 | (self.parents)( | |||
|
83 | self.index.as_ptr(), | |||
|
84 | rev as c_int, | |||
|
85 | &mut res as *mut [c_int; 2], | |||
|
86 | ) | |||
|
87 | }; | |||
|
88 | match code { | |||
|
89 | 0 => Ok(res), | |||
|
90 | _ => Err(GraphError::ParentOutOfRange(rev)), | |||
|
91 | } | |||
|
92 | } | |||
|
93 | } | |||
|
94 | ||||
|
95 | /// Return the `index_get_parents` function of the parsers C Extension module. | |||
|
96 | /// | |||
|
97 | /// A pointer to the function is stored in the `parsers` module as a | |||
|
98 | /// standard [Python capsule](https://docs.python.org/2/c-api/capsule.html). | |||
|
99 | /// | |||
|
100 | /// This function retrieves the capsule and casts the function pointer | |||
|
101 | /// | |||
|
102 | /// Casting function pointers is one of the rare cases of | |||
|
103 | /// legitimate use cases of `mem::transmute()` (see | |||
|
104 | /// https://doc.rust-lang.org/std/mem/fn.transmute.html of | |||
|
105 | /// `mem::transmute()`. | |||
|
106 | /// It is inappropriate for architectures where | |||
|
107 | /// function and data pointer sizes differ (so-called "Harvard | |||
|
108 | /// architectures"), but these are nowadays mostly DSPs | |||
|
109 | /// and microcontrollers, hence out of our scope. | |||
|
110 | fn decapsule_parents_fn(py: Python) -> PyResult<IndexParentsFn> { | |||
|
111 | unsafe { | |||
|
112 | let caps_name = CStr::from_bytes_with_nul_unchecked( | |||
|
113 | b"mercurial.cext.parsers.index_get_parents_CAPI\0", | |||
|
114 | ); | |||
|
115 | let from_caps = PyCapsule_Import(caps_name.as_ptr(), 0); | |||
|
116 | if from_caps.is_null() { | |||
|
117 | return Err(PyErr::fetch(py)); | |||
|
118 | } | |||
|
119 | Ok(transmute(from_caps)) | |||
|
120 | } | |||
|
121 | } |
@@ -2866,6 +2866,7 b' static PyTypeObject rustlazyancestorsTyp' | |||||
2866 |
|
2866 | |||
2867 | void revlog_module_init(PyObject *mod) |
|
2867 | void revlog_module_init(PyObject *mod) | |
2868 | { |
|
2868 | { | |
|
2869 | PyObject *caps = NULL; | |||
2869 | HgRevlogIndex_Type.tp_new = PyType_GenericNew; |
|
2870 | HgRevlogIndex_Type.tp_new = PyType_GenericNew; | |
2870 | if (PyType_Ready(&HgRevlogIndex_Type) < 0) |
|
2871 | if (PyType_Ready(&HgRevlogIndex_Type) < 0) | |
2871 | return; |
|
2872 | return; | |
@@ -2885,6 +2886,12 b' void revlog_module_init(PyObject *mod)' | |||||
2885 | if (nullentry) |
|
2886 | if (nullentry) | |
2886 | PyObject_GC_UnTrack(nullentry); |
|
2887 | PyObject_GC_UnTrack(nullentry); | |
2887 |
|
2888 | |||
|
2889 | caps = PyCapsule_New(HgRevlogIndex_GetParents, | |||
|
2890 | "mercurial.cext.parsers.index_get_parents_CAPI", | |||
|
2891 | NULL); | |||
|
2892 | if (caps != NULL) | |||
|
2893 | PyModule_AddObject(mod, "index_get_parents_CAPI", caps); | |||
|
2894 | ||||
2888 | #ifdef WITH_RUST |
|
2895 | #ifdef WITH_RUST | |
2889 | rustlazyancestorsType.tp_new = PyType_GenericNew; |
|
2896 | rustlazyancestorsType.tp_new = PyType_GenericNew; | |
2890 | if (PyType_Ready(&rustlazyancestorsType) < 0) |
|
2897 | if (PyType_Ready(&rustlazyancestorsType) < 0) |
@@ -21,8 +21,10 b'' | |||||
21 | #[macro_use] |
|
21 | #[macro_use] | |
22 | extern crate cpython; |
|
22 | extern crate cpython; | |
23 | extern crate hg; |
|
23 | extern crate hg; | |
|
24 | extern crate libc; | |||
24 |
|
25 | |||
25 | mod ancestors; |
|
26 | mod ancestors; | |
|
27 | mod cindex; | |||
26 | mod exceptions; |
|
28 | mod exceptions; | |
27 |
|
29 | |||
28 | py_module_initializer!(rustext, initrustext, PyInit_rustext, |py, m| { |
|
30 | py_module_initializer!(rustext, initrustext, PyInit_rustext, |py, m| { |
General Comments 0
You need to be logged in to leave comments.
Login now