##// END OF EJS Templates
rust-cpython: implement Graph using C parents function...
Georges Racinet -
r41082:4c25038c default
parent child Browse files
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