##// END OF EJS Templates
rust: fix unsound `OwningDirstateMap`...
Raphaël Gomès -
r50249:dd6b67d5 stable
parent child Browse files
Show More
@@ -3,6 +3,12 b''
3 version = 3
3 version = 3
4
4
5 [[package]]
5 [[package]]
6 name = "Inflector"
7 version = "0.11.4"
8 source = "registry+https://github.com/rust-lang/crates.io-index"
9 checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
10
11 [[package]]
6 name = "adler"
12 name = "adler"
7 version = "0.2.3"
13 version = "0.2.3"
8 source = "registry+https://github.com/rust-lang/crates.io-index"
14 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -18,6 +24,12 b' dependencies = ['
18 ]
24 ]
19
25
20 [[package]]
26 [[package]]
27 name = "aliasable"
28 version = "0.1.3"
29 source = "registry+https://github.com/rust-lang/crates.io-index"
30 checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
31
32 [[package]]
21 name = "ansi_term"
33 name = "ansi_term"
22 version = "0.11.0"
34 version = "0.11.0"
23 source = "registry+https://github.com/rust-lang/crates.io-index"
35 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -407,6 +419,7 b' dependencies = ['
407 "log",
419 "log",
408 "memmap2",
420 "memmap2",
409 "micro-timer",
421 "micro-timer",
422 "ouroboros",
410 "pretty_assertions",
423 "pretty_assertions",
411 "rand 0.8.4",
424 "rand 0.8.4",
412 "rand_distr",
425 "rand_distr",
@@ -415,7 +428,6 b' dependencies = ['
415 "regex",
428 "regex",
416 "same-file",
429 "same-file",
417 "sha-1",
430 "sha-1",
418 "stable_deref_trait",
419 "tempfile",
431 "tempfile",
420 "twox-hash",
432 "twox-hash",
421 "zstd",
433 "zstd",
@@ -623,6 +635,30 b' source = "registry+https://github.com/ru'
623 checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
635 checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
624
636
625 [[package]]
637 [[package]]
638 name = "ouroboros"
639 version = "0.15.0"
640 source = "registry+https://github.com/rust-lang/crates.io-index"
641 checksum = "9f31a3b678685b150cba82b702dcdc5e155893f63610cf388d30cd988d4ca2bf"
642 dependencies = [
643 "aliasable",
644 "ouroboros_macro",
645 "stable_deref_trait",
646 ]
647
648 [[package]]
649 name = "ouroboros_macro"
650 version = "0.15.0"
651 source = "registry+https://github.com/rust-lang/crates.io-index"
652 checksum = "084fd65d5dd8b3772edccb5ffd1e4b7eba43897ecd0f9401e330e8c542959408"
653 dependencies = [
654 "Inflector",
655 "proc-macro-error",
656 "proc-macro2",
657 "quote",
658 "syn",
659 ]
660
661 [[package]]
626 name = "output_vt100"
662 name = "output_vt100"
627 version = "0.1.2"
663 version = "0.1.2"
628 source = "registry+https://github.com/rust-lang/crates.io-index"
664 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -662,6 +698,30 b' dependencies = ['
662 ]
698 ]
663
699
664 [[package]]
700 [[package]]
701 name = "proc-macro-error"
702 version = "1.0.4"
703 source = "registry+https://github.com/rust-lang/crates.io-index"
704 checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
705 dependencies = [
706 "proc-macro-error-attr",
707 "proc-macro2",
708 "quote",
709 "syn",
710 "version_check",
711 ]
712
713 [[package]]
714 name = "proc-macro-error-attr"
715 version = "1.0.4"
716 source = "registry+https://github.com/rust-lang/crates.io-index"
717 checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
718 dependencies = [
719 "proc-macro2",
720 "quote",
721 "version_check",
722 ]
723
724 [[package]]
665 name = "proc-macro2"
725 name = "proc-macro2"
666 version = "1.0.24"
726 version = "1.0.24"
667 source = "registry+https://github.com/rust-lang/crates.io-index"
727 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -18,6 +18,7 b' im-rc = "15.0.*"'
18 itertools = "0.9"
18 itertools = "0.9"
19 lazy_static = "1.4.0"
19 lazy_static = "1.4.0"
20 libc = "0.2"
20 libc = "0.2"
21 ouroboros = "0.15.0"
21 rand = "0.8.4"
22 rand = "0.8.4"
22 rand_pcg = "0.3.1"
23 rand_pcg = "0.3.1"
23 rand_distr = "0.4.2"
24 rand_distr = "0.4.2"
@@ -26,7 +27,6 b' regex = "1.3.9"'
26 sha-1 = "0.9.6"
27 sha-1 = "0.9.6"
27 twox-hash = "1.5.0"
28 twox-hash = "1.5.0"
28 same-file = "1.0.6"
29 same-file = "1.0.6"
29 stable_deref_trait = "1.2.0"
30 tempfile = "3.1.0"
30 tempfile = "3.1.0"
31 crossbeam-channel = "0.4"
31 crossbeam-channel = "0.4"
32 micro-timer = "0.3.0"
32 micro-timer = "0.3.0"
@@ -136,8 +136,6 b' pub enum StatusError {'
136 DirstateV2ParseError(DirstateV2ParseError),
136 DirstateV2ParseError(DirstateV2ParseError),
137 }
137 }
138
138
139 pub type StatusResult<T> = Result<T, StatusError>;
140
141 impl fmt::Display for StatusError {
139 impl fmt::Display for StatusError {
142 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
140 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
143 match self {
141 match self {
@@ -725,10 +725,11 b' where'
725
725
726 impl OwningDirstateMap {
726 impl OwningDirstateMap {
727 pub fn clear(&mut self) {
727 pub fn clear(&mut self) {
728 let map = self.get_map_mut();
728 self.with_dmap_mut(|map| {
729 map.root = Default::default();
729 map.root = Default::default();
730 map.nodes_with_entry_count = 0;
730 map.nodes_with_entry_count = 0;
731 map.nodes_with_copy_source_count = 0;
731 map.nodes_with_copy_source_count = 0;
732 });
732 }
733 }
733
734
734 pub fn set_entry(
735 pub fn set_entry(
@@ -736,9 +737,10 b' impl OwningDirstateMap {'
736 filename: &HgPath,
737 filename: &HgPath,
737 entry: DirstateEntry,
738 entry: DirstateEntry,
738 ) -> Result<(), DirstateV2ParseError> {
739 ) -> Result<(), DirstateV2ParseError> {
739 let map = self.get_map_mut();
740 self.with_dmap_mut(|map| {
740 map.get_or_insert(&filename)?.data = NodeData::Entry(entry);
741 map.get_or_insert(&filename)?.data = NodeData::Entry(entry);
741 Ok(())
742 Ok(())
743 })
742 }
744 }
743
745
744 pub fn add_file(
746 pub fn add_file(
@@ -747,8 +749,9 b' impl OwningDirstateMap {'
747 entry: DirstateEntry,
749 entry: DirstateEntry,
748 ) -> Result<(), DirstateError> {
750 ) -> Result<(), DirstateError> {
749 let old_state = self.get(filename)?.map(|e| e.state());
751 let old_state = self.get(filename)?.map(|e| e.state());
750 let map = self.get_map_mut();
752 self.with_dmap_mut(|map| {
751 Ok(map.add_or_remove_file(filename, old_state, entry)?)
753 Ok(map.add_or_remove_file(filename, old_state, entry)?)
754 })
752 }
755 }
753
756
754 pub fn remove_file(
757 pub fn remove_file(
@@ -779,9 +782,10 b' impl OwningDirstateMap {'
779 if size == 0 {
782 if size == 0 {
780 self.copy_map_remove(filename)?;
783 self.copy_map_remove(filename)?;
781 }
784 }
782 let map = self.get_map_mut();
785 self.with_dmap_mut(|map| {
783 let entry = DirstateEntry::new_removed(size);
786 let entry = DirstateEntry::new_removed(size);
784 Ok(map.add_or_remove_file(filename, old_state, entry)?)
787 Ok(map.add_or_remove_file(filename, old_state, entry)?)
788 })
785 }
789 }
786
790
787 pub fn drop_entry_and_copy_source(
791 pub fn drop_entry_and_copy_source(
@@ -791,7 +795,6 b' impl OwningDirstateMap {'
791 let was_tracked = self
795 let was_tracked = self
792 .get(filename)?
796 .get(filename)?
793 .map_or(false, |e| e.state().is_tracked());
797 .map_or(false, |e| e.state().is_tracked());
794 let map = self.get_map_mut();
795 struct Dropped {
798 struct Dropped {
796 was_tracked: bool,
799 was_tracked: bool,
797 had_entry: bool,
800 had_entry: bool,
@@ -878,6 +881,7 b' impl OwningDirstateMap {'
878 Ok(Some((dropped, remove)))
881 Ok(Some((dropped, remove)))
879 }
882 }
880
883
884 self.with_dmap_mut(|map| {
881 if let Some((dropped, _removed)) = recur(
885 if let Some((dropped, _removed)) = recur(
882 map.on_disk,
886 map.on_disk,
883 &mut map.unreachable_bytes,
887 &mut map.unreachable_bytes,
@@ -894,13 +898,14 b' impl OwningDirstateMap {'
894 debug_assert!(!was_tracked);
898 debug_assert!(!was_tracked);
895 }
899 }
896 Ok(())
900 Ok(())
901 })
897 }
902 }
898
903
899 pub fn has_tracked_dir(
904 pub fn has_tracked_dir(
900 &mut self,
905 &mut self,
901 directory: &HgPath,
906 directory: &HgPath,
902 ) -> Result<bool, DirstateError> {
907 ) -> Result<bool, DirstateError> {
903 let map = self.get_map_mut();
908 self.with_dmap_mut(|map| {
904 if let Some(node) = map.get_node(directory)? {
909 if let Some(node) = map.get_node(directory)? {
905 // A node without a `DirstateEntry` was created to hold child
910 // A node without a `DirstateEntry` was created to hold child
906 // nodes, and is therefore a directory.
911 // nodes, and is therefore a directory.
@@ -909,13 +914,14 b' impl OwningDirstateMap {'
909 } else {
914 } else {
910 Ok(false)
915 Ok(false)
911 }
916 }
917 })
912 }
918 }
913
919
914 pub fn has_dir(
920 pub fn has_dir(
915 &mut self,
921 &mut self,
916 directory: &HgPath,
922 directory: &HgPath,
917 ) -> Result<bool, DirstateError> {
923 ) -> Result<bool, DirstateError> {
918 let map = self.get_map_mut();
924 self.with_dmap_mut(|map| {
919 if let Some(node) = map.get_node(directory)? {
925 if let Some(node) = map.get_node(directory)? {
920 // A node without a `DirstateEntry` was created to hold child
926 // A node without a `DirstateEntry` was created to hold child
921 // nodes, and is therefore a directory.
927 // nodes, and is therefore a directory.
@@ -924,6 +930,7 b' impl OwningDirstateMap {'
924 } else {
930 } else {
925 Ok(false)
931 Ok(false)
926 }
932 }
933 })
927 }
934 }
928
935
929 #[timed]
936 #[timed]
@@ -975,16 +982,29 b' impl OwningDirstateMap {'
975 on_disk::write(map, can_append)
982 on_disk::write(map, can_append)
976 }
983 }
977
984
978 pub fn status<'a>(
985 /// `callback` allows the caller to process and do something with the
979 &'a mut self,
986 /// results of the status. This is needed to do so efficiently (i.e.
980 matcher: &'a (dyn Matcher + Sync),
987 /// without cloning the `DirstateStatus` object with its paths) because
988 /// we need to borrow from `Self`.
989 pub fn with_status<R>(
990 &mut self,
991 matcher: &(dyn Matcher + Sync),
981 root_dir: PathBuf,
992 root_dir: PathBuf,
982 ignore_files: Vec<PathBuf>,
993 ignore_files: Vec<PathBuf>,
983 options: StatusOptions,
994 options: StatusOptions,
984 ) -> Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError>
995 callback: impl for<'r> FnOnce(
985 {
996 Result<(DirstateStatus<'r>, Vec<PatternFileWarning>), StatusError>,
986 let map = self.get_map_mut();
997 ) -> R,
987 super::status::status(map, matcher, root_dir, ignore_files, options)
998 ) -> R {
999 self.with_dmap_mut(|map| {
1000 callback(super::status::status(
1001 map,
1002 matcher,
1003 root_dir,
1004 ignore_files,
1005 options,
1006 ))
1007 })
988 }
1008 }
989
1009
990 pub fn copy_map_len(&self) -> usize {
1010 pub fn copy_map_len(&self) -> usize {
@@ -1032,7 +1052,7 b' impl OwningDirstateMap {'
1032 &mut self,
1052 &mut self,
1033 key: &HgPath,
1053 key: &HgPath,
1034 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1054 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1035 let map = self.get_map_mut();
1055 self.with_dmap_mut(|map| {
1036 let count = &mut map.nodes_with_copy_source_count;
1056 let count = &mut map.nodes_with_copy_source_count;
1037 let unreachable_bytes = &mut map.unreachable_bytes;
1057 let unreachable_bytes = &mut map.unreachable_bytes;
1038 Ok(DirstateMap::get_node_mut(
1058 Ok(DirstateMap::get_node_mut(
@@ -1048,6 +1068,7 b' impl OwningDirstateMap {'
1048 }
1068 }
1049 node.copy_source.take().map(Cow::into_owned)
1069 node.copy_source.take().map(Cow::into_owned)
1050 }))
1070 }))
1071 })
1051 }
1072 }
1052
1073
1053 pub fn copy_map_insert(
1074 pub fn copy_map_insert(
@@ -1055,7 +1076,7 b' impl OwningDirstateMap {'
1055 key: HgPathBuf,
1076 key: HgPathBuf,
1056 value: HgPathBuf,
1077 value: HgPathBuf,
1057 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1078 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
1058 let map = self.get_map_mut();
1079 self.with_dmap_mut(|map| {
1059 let node = DirstateMap::get_or_insert_node(
1080 let node = DirstateMap::get_or_insert_node(
1060 map.on_disk,
1081 map.on_disk,
1061 &mut map.unreachable_bytes,
1082 &mut map.unreachable_bytes,
@@ -1068,6 +1089,7 b' impl OwningDirstateMap {'
1068 map.nodes_with_copy_source_count += 1
1089 map.nodes_with_copy_source_count += 1
1069 }
1090 }
1070 Ok(node.copy_source.replace(value.into()).map(Cow::into_owned))
1091 Ok(node.copy_source.replace(value.into()).map(Cow::into_owned))
1092 })
1071 }
1093 }
1072
1094
1073 pub fn len(&self) -> usize {
1095 pub fn len(&self) -> usize {
@@ -1115,7 +1137,7 b' impl OwningDirstateMap {'
1115 >,
1137 >,
1116 DirstateError,
1138 DirstateError,
1117 > {
1139 > {
1118 let map = self.get_map_mut();
1140 let map = self.get_map();
1119 let on_disk = map.on_disk;
1141 let on_disk = map.on_disk;
1120 Ok(Box::new(filter_map_results(
1142 Ok(Box::new(filter_map_results(
1121 map.iter_nodes(),
1143 map.iter_nodes(),
@@ -1,142 +1,89 b''
1 use crate::{DirstateError, DirstateParents};
2
1 use super::dirstate_map::DirstateMap;
3 use super::dirstate_map::DirstateMap;
2 use stable_deref_trait::StableDeref;
3 use std::ops::Deref;
4 use std::ops::Deref;
4
5
5 /*
6 use ouroboros::self_referencing;
6 // /!\ This is unsound and can cause use after free. It will be fixed in the
7 // next patch
8
9 // If we change `value` from its current use of `HgPathBuf` to `&HgPath`,
10 // nothing here tells that `value` will outlive `OwningDirstateMap`
11 pub fn copy_map_insert<'a,'owned>(
12 &'owned mut self,
13 key: &HgPath,
14 value: &'a HgPath,
15 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
16 // `'local` is smaller than `'a` here
17 let map: &'local mut DirstateMap<'local> = self.get_map_mut();
18 let node: &'local mut Node<'local> = DirstateMap::get_or_insert_node(
19 map.on_disk,
20 &mut map.unreachable_bytes,
21 &mut map.root,
22 &key,
23 WithBasename::to_cow_owned,
24 |_ancestor| {},
25 )?;
26 if node.copy_source.is_none() {
27 map.nodes_with_copy_source_count += 1
28 }
29 Ok(node.copy_source.replace(value.into()).map(Cow::into_owned))
30 // and right here ----------^^^^^^^^^^^^
31 // we are storing `&'a HgPath` in `Node<'local>` which is possible
32 // because to the compiler, `'a` is longer than ``local`.
33 // It is wrong because nothing proves that `&'a HgPath` will outlive `self`.
34 }
35
36 // All of this is caused by the wrong cast of the DirstateMap pointer that
37 // fakes the lifetime of `DirstateMap` and ensures the compiler that it lives
38 // as long as `on_disk`, which is only true for its immutable data.
39 // This will be fixed in the next commit.
40 */
41
7
42 /// Keep a `DirstateMap<'on_disk>` next to the `on_disk` buffer that it
8 /// Keep a `DirstateMap<'on_disk>` next to the `on_disk` buffer that it
43 /// borrows.
9 /// borrows.
44 ///
10 #[self_referencing]
45 /// This is similar to [`OwningRef`] which is more limited because it
46 /// represents exactly one `&T` reference next to the value it borrows, as
47 /// opposed to a struct that may contain an arbitrary number of references in
48 /// arbitrarily-nested data structures.
49 ///
50 /// [`OwningRef`]: https://docs.rs/owning_ref/0.4.1/owning_ref/struct.OwningRef.html
51 pub struct OwningDirstateMap {
11 pub struct OwningDirstateMap {
52 /// Owned handle to a bytes buffer with a stable address.
53 ///
54 /// See <https://docs.rs/owning_ref/0.4.1/owning_ref/trait.StableAddress.html>.
55 on_disk: Box<dyn Deref<Target = [u8]> + Send>,
12 on_disk: Box<dyn Deref<Target = [u8]> + Send>,
56
13 #[borrows(on_disk)]
57 /// Pointer for `Box<DirstateMap<'on_disk>>`, typed-erased because the
14 #[covariant]
58 /// language cannot represent a lifetime referencing a sibling field.
15 map: DirstateMap<'this>,
59 /// This is not quite a self-referencial struct (moving this struct is not
60 /// a problem as it doesn’t change the address of the bytes buffer owned
61 /// by `on_disk`) but touches similar borrow-checker limitations.
62 ptr: *mut (),
63 }
16 }
64
17
65 impl OwningDirstateMap {
18 impl OwningDirstateMap {
66 pub fn new_empty<OnDisk>(on_disk: OnDisk) -> Self
19 pub fn new_empty<OnDisk>(on_disk: OnDisk) -> Self
67 where
20 where
68 OnDisk: Deref<Target = [u8]> + StableDeref + Send + 'static,
21 OnDisk: Deref<Target = [u8]> + Send + 'static,
69 {
22 {
70 let on_disk = Box::new(on_disk);
23 let on_disk = Box::new(on_disk);
71 let bytes: &'_ [u8] = &on_disk;
72 let map = DirstateMap::empty(bytes);
73
24
74 // Like in `bytes` above, this `'_` lifetime parameter borrows from
25 OwningDirstateMapBuilder {
75 // the bytes buffer owned by `on_disk`.
26 on_disk,
76 let ptr: *mut DirstateMap<'_> = Box::into_raw(Box::new(map));
27 map_builder: |bytes| DirstateMap::empty(&bytes),
77
28 }
78 // Erase the pointed type entirely in order to erase the lifetime.
29 .build()
79 let ptr: *mut () = ptr.cast();
80
81 Self { on_disk, ptr }
82 }
30 }
83
31
84 pub fn get_pair_mut<'a>(
32 pub fn new_v1<OnDisk>(
85 &'a mut self,
33 on_disk: OnDisk,
86 ) -> (&'a [u8], &'a mut DirstateMap<'a>) {
34 ) -> Result<(Self, DirstateParents), DirstateError>
87 // SAFETY: We cast the type-erased pointer back to the same type it had
35 where
88 // in `new`, except with a different lifetime parameter. This time we
36 OnDisk: Deref<Target = [u8]> + Send + 'static,
89 // connect the lifetime to that of `self`. This cast is valid because
37 {
90 // `self` owns the same `on_disk` whose buffer `DirstateMap`
38 let on_disk = Box::new(on_disk);
91 // references. That buffer has a stable memory address because our
39 let mut parents = DirstateParents::NULL;
92 // `Self::new_empty` counstructor requires `StableDeref`.
40
93 let ptr: *mut DirstateMap<'a> = self.ptr.cast();
41 Ok((
94 // SAFETY: we dereference that pointer, connecting the lifetime of the
42 OwningDirstateMapTryBuilder {
95 // new `&mut` to that of `self`. This is valid because the
43 on_disk,
96 // raw pointer is to a boxed value, and `self` owns that box.
44 map_builder: |bytes| {
97 (&self.on_disk, unsafe { &mut *ptr })
45 DirstateMap::new_v1(&bytes).map(|(dmap, p)| {
46 parents = p.unwrap_or(DirstateParents::NULL);
47 dmap
48 })
49 },
98 }
50 }
99
51 .try_build()?,
100 pub fn get_map_mut<'a>(&'a mut self) -> &'a mut DirstateMap<'a> {
52 parents,
101 self.get_pair_mut().1
53 ))
102 }
54 }
103
55
104 pub fn get_map<'a>(&'a self) -> &'a DirstateMap<'a> {
56 pub fn new_v2<OnDisk>(
105 // SAFETY: same reasoning as in `get_pair_mut` above.
57 on_disk: OnDisk,
106 let ptr: *mut DirstateMap<'a> = self.ptr.cast();
58 data_size: usize,
107 unsafe { &*ptr }
59 metadata: &[u8],
60 ) -> Result<Self, DirstateError>
61 where
62 OnDisk: Deref<Target = [u8]> + Send + 'static,
63 {
64 let on_disk = Box::new(on_disk);
65
66 OwningDirstateMapTryBuilder {
67 on_disk,
68 map_builder: |bytes| {
69 DirstateMap::new_v2(&bytes, data_size, metadata)
70 },
108 }
71 }
109
72 .try_build()
110 pub fn on_disk<'a>(&'a self) -> &'a [u8] {
111 &self.on_disk
112 }
113 }
73 }
114
74
115 impl Drop for OwningDirstateMap {
75 pub fn with_dmap_mut<R>(
116 fn drop(&mut self) {
76 &mut self,
117 // Silence a "field is never read" warning, and demonstrate that this
77 f: impl FnOnce(&mut DirstateMap) -> R,
118 // value is still alive.
78 ) -> R {
119 let _: &Box<dyn Deref<Target = [u8]> + Send> = &self.on_disk;
79 self.with_map_mut(f)
120 // SAFETY: this cast is the same as in `get_mut`, and is valid for the
80 }
121 // same reason. `self.on_disk` still exists at this point, drop glue
81
122 // will drop it implicitly after this `drop` method returns.
82 pub fn get_map(&self) -> &DirstateMap {
123 let ptr: *mut DirstateMap<'_> = self.ptr.cast();
83 self.borrow_map()
124 // SAFETY: `Box::from_raw` takes ownership of the box away from `self`.
84 }
125 // This is fine because drop glue does nothing for `*mut ()` and we’re
85
126 // in `drop`, so `get` and `get_mut` cannot be called again.
86 pub fn on_disk(&self) -> &[u8] {
127 unsafe { drop(Box::from_raw(ptr)) }
87 self.borrow_on_disk()
128 }
88 }
129 }
89 }
130
131 fn _static_assert_is_send<T: Send>() {}
132
133 fn _static_assert_fields_are_send() {
134 _static_assert_is_send::<Box<DirstateMap<'_>>>();
135 }
136
137 // SAFETY: we don’t get this impl implicitly because `*mut (): !Send` because
138 // thread-safety of raw pointers is unknown in the general case. However this
139 // particular raw pointer represents a `Box<DirstateMap<'on_disk>>` that we
140 // own. Since that `Box` is `Send` as shown in above, it is sound to mark
141 // this struct as `Send` too.
142 unsafe impl Send for OwningDirstateMap {}
@@ -40,13 +40,14 b' use std::time::SystemTime;'
40 /// exists in one of the two trees, depending on information requested by
40 /// exists in one of the two trees, depending on information requested by
41 /// `options` we may need to traverse the remaining subtree.
41 /// `options` we may need to traverse the remaining subtree.
42 #[timed]
42 #[timed]
43 pub fn status<'tree, 'on_disk: 'tree>(
43 pub fn status<'dirstate>(
44 dmap: &'tree mut DirstateMap<'on_disk>,
44 dmap: &'dirstate mut DirstateMap,
45 matcher: &(dyn Matcher + Sync),
45 matcher: &(dyn Matcher + Sync),
46 root_dir: PathBuf,
46 root_dir: PathBuf,
47 ignore_files: Vec<PathBuf>,
47 ignore_files: Vec<PathBuf>,
48 options: StatusOptions,
48 options: StatusOptions,
49 ) -> Result<(DirstateStatus<'on_disk>, Vec<PatternFileWarning>), StatusError> {
49 ) -> Result<(DirstateStatus<'dirstate>, Vec<PatternFileWarning>), StatusError>
50 {
50 // Force the global rayon threadpool to not exceed 16 concurrent threads.
51 // Force the global rayon threadpool to not exceed 16 concurrent threads.
51 // This is a stop-gap measure until we figure out why using more than 16
52 // This is a stop-gap measure until we figure out why using more than 16
52 // threads makes `status` slower for each additional thread.
53 // threads makes `status` slower for each additional thread.
@@ -1,7 +1,6 b''
1 use crate::changelog::Changelog;
1 use crate::changelog::Changelog;
2 use crate::config::{Config, ConfigError, ConfigParseError};
2 use crate::config::{Config, ConfigError, ConfigParseError};
3 use crate::dirstate::DirstateParents;
3 use crate::dirstate::DirstateParents;
4 use crate::dirstate_tree::dirstate_map::DirstateMap;
5 use crate::dirstate_tree::on_disk::Docket as DirstateDocket;
4 use crate::dirstate_tree::on_disk::Docket as DirstateDocket;
6 use crate::dirstate_tree::owning::OwningDirstateMap;
5 use crate::dirstate_tree::owning::OwningDirstateMap;
7 use crate::errors::HgResultExt;
6 use crate::errors::HgResultExt;
@@ -340,25 +339,19 b' impl Repo {'
340 .set(Some(docket.uuid.to_owned()));
339 .set(Some(docket.uuid.to_owned()));
341 let data_size = docket.data_size();
340 let data_size = docket.data_size();
342 let metadata = docket.tree_metadata();
341 let metadata = docket.tree_metadata();
343 let mut map = if let Some(data_mmap) = self
342 if let Some(data_mmap) = self
344 .hg_vfs()
343 .hg_vfs()
345 .mmap_open(docket.data_filename())
344 .mmap_open(docket.data_filename())
346 .io_not_found_as_none()?
345 .io_not_found_as_none()?
347 {
346 {
348 OwningDirstateMap::new_empty(data_mmap)
347 OwningDirstateMap::new_v2(data_mmap, data_size, metadata)
349 } else {
348 } else {
350 OwningDirstateMap::new_empty(Vec::new())
349 OwningDirstateMap::new_v2(Vec::new(), data_size, metadata)
351 };
350 }
352 let (on_disk, placeholder) = map.get_pair_mut();
353 *placeholder = DirstateMap::new_v2(on_disk, data_size, metadata)?;
354 Ok(map)
355 } else {
351 } else {
356 let mut map = OwningDirstateMap::new_empty(dirstate_file_contents);
352 let (map, parents) =
357 let (on_disk, placeholder) = map.get_pair_mut();
353 OwningDirstateMap::new_v1(dirstate_file_contents)?;
358 let (inner, parents) = DirstateMap::new_v1(on_disk)?;
354 self.dirstate_parents.set(parents);
359 self.dirstate_parents
360 .set(parents.unwrap_or(DirstateParents::NULL));
361 *placeholder = inner;
362 Ok(map)
355 Ok(map)
363 }
356 }
364 }
357 }
@@ -23,7 +23,6 b' use crate::{'
23 };
23 };
24 use hg::{
24 use hg::{
25 dirstate::StateMapIter,
25 dirstate::StateMapIter,
26 dirstate_tree::dirstate_map::DirstateMap as TreeDirstateMap,
27 dirstate_tree::on_disk::DirstateV2ParseError,
26 dirstate_tree::on_disk::DirstateV2ParseError,
28 dirstate_tree::owning::OwningDirstateMap,
27 dirstate_tree::owning::OwningDirstateMap,
29 revlog::Node,
28 revlog::Node,
@@ -53,18 +52,12 b' py_class!(pub class DirstateMap |py| {'
53 on_disk: PyBytes,
52 on_disk: PyBytes,
54 ) -> PyResult<PyObject> {
53 ) -> PyResult<PyObject> {
55 let on_disk = PyBytesDeref::new(py, on_disk);
54 let on_disk = PyBytesDeref::new(py, on_disk);
56 let mut map = OwningDirstateMap::new_empty(on_disk);
55 let (map, parents) = OwningDirstateMap::new_v1(on_disk)
57 let (on_disk, map_placeholder) = map.get_pair_mut();
58
59 let (actual_map, parents) = TreeDirstateMap::new_v1(on_disk)
60 .map_err(|e| dirstate_error(py, e))?;
56 .map_err(|e| dirstate_error(py, e))?;
61 *map_placeholder = actual_map;
62 let map = Self::create_instance(py, map)?;
57 let map = Self::create_instance(py, map)?;
63 let parents = parents.map(|p| {
58 let p1 = PyBytes::new(py, parents.p1.as_bytes());
64 let p1 = PyBytes::new(py, p.p1.as_bytes());
59 let p2 = PyBytes::new(py, parents.p2.as_bytes());
65 let p2 = PyBytes::new(py, p.p2.as_bytes());
60 let parents = (p1, p2);
66 (p1, p2)
67 });
68 Ok((map, parents).to_py_object(py).into_object())
61 Ok((map, parents).to_py_object(py).into_object())
69 }
62 }
70
63
@@ -79,9 +72,7 b' py_class!(pub class DirstateMap |py| {'
79 PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e))
72 PyErr::new::<exc::OSError, _>(py, format!("Dirstate error: {:?}", e))
80 };
73 };
81 let on_disk = PyBytesDeref::new(py, on_disk);
74 let on_disk = PyBytesDeref::new(py, on_disk);
82 let mut map = OwningDirstateMap::new_empty(on_disk);
75 let map = OwningDirstateMap::new_v2(
83 let (on_disk, map_placeholder) = map.get_pair_mut();
84 *map_placeholder = TreeDirstateMap::new_v2(
85 on_disk, data_size, tree_metadata.data(py),
76 on_disk, data_size, tree_metadata.data(py),
86 ).map_err(dirstate_error)?;
77 ).map_err(dirstate_error)?;
87 let map = Self::create_instance(py, map)?;
78 let map = Self::create_instance(py, map)?;
@@ -127,11 +127,16 b' pub fn status_wrapper('
127 // The caller may call `copymap.items()` separately
127 // The caller may call `copymap.items()` separately
128 let list_copies = false;
128 let list_copies = false;
129
129
130 let after_status = |res: Result<(DirstateStatus<'_>, _), StatusError>| {
131 let (status_res, warnings) =
132 res.map_err(|e| handle_fallback(py, e))?;
133 build_response(py, status_res, warnings)
134 };
135
130 match matcher.get_type(py).name(py).borrow() {
136 match matcher.get_type(py).name(py).borrow() {
131 "alwaysmatcher" => {
137 "alwaysmatcher" => {
132 let matcher = AlwaysMatcher;
138 let matcher = AlwaysMatcher;
133 let (status_res, warnings) = dmap
139 dmap.with_status(
134 .status(
135 &matcher,
140 &matcher,
136 root_dir.to_path_buf(),
141 root_dir.to_path_buf(),
137 ignore_files,
142 ignore_files,
@@ -143,9 +148,8 b' pub fn status_wrapper('
143 list_copies,
148 list_copies,
144 collect_traversed_dirs,
149 collect_traversed_dirs,
145 },
150 },
151 after_status,
146 )
152 )
147 .map_err(|e| handle_fallback(py, e))?;
148 build_response(py, status_res, warnings)
149 }
153 }
150 "exactmatcher" => {
154 "exactmatcher" => {
151 let files = matcher.call_method(
155 let files = matcher.call_method(
@@ -167,8 +171,7 b' pub fn status_wrapper('
167 let files = files?;
171 let files = files?;
168 let matcher = FileMatcher::new(files.as_ref())
172 let matcher = FileMatcher::new(files.as_ref())
169 .map_err(|e| PyErr::new::<ValueError, _>(py, e.to_string()))?;
173 .map_err(|e| PyErr::new::<ValueError, _>(py, e.to_string()))?;
170 let (status_res, warnings) = dmap
174 dmap.with_status(
171 .status(
172 &matcher,
175 &matcher,
173 root_dir.to_path_buf(),
176 root_dir.to_path_buf(),
174 ignore_files,
177 ignore_files,
@@ -180,9 +183,8 b' pub fn status_wrapper('
180 list_copies,
183 list_copies,
181 collect_traversed_dirs,
184 collect_traversed_dirs,
182 },
185 },
186 after_status,
183 )
187 )
184 .map_err(|e| handle_fallback(py, e))?;
185 build_response(py, status_res, warnings)
186 }
188 }
187 "includematcher" => {
189 "includematcher" => {
188 // Get the patterns from Python even though most of them are
190 // Get the patterns from Python even though most of them are
@@ -219,8 +221,7 b' pub fn status_wrapper('
219 let matcher = IncludeMatcher::new(ignore_patterns)
221 let matcher = IncludeMatcher::new(ignore_patterns)
220 .map_err(|e| handle_fallback(py, e.into()))?;
222 .map_err(|e| handle_fallback(py, e.into()))?;
221
223
222 let (status_res, warnings) = dmap
224 dmap.with_status(
223 .status(
224 &matcher,
225 &matcher,
225 root_dir.to_path_buf(),
226 root_dir.to_path_buf(),
226 ignore_files,
227 ignore_files,
@@ -232,10 +233,8 b' pub fn status_wrapper('
232 list_copies,
233 list_copies,
233 collect_traversed_dirs,
234 collect_traversed_dirs,
234 },
235 },
236 after_status,
235 )
237 )
236 .map_err(|e| handle_fallback(py, e))?;
237
238 build_response(py, status_res, warnings)
239 }
238 }
240 e => Err(PyErr::new::<ValueError, _>(
239 e => Err(PyErr::new::<ValueError, _>(
241 py,
240 py,
@@ -25,6 +25,9 b' use hg::utils::files::get_bytes_from_os_'
25 use hg::utils::files::get_bytes_from_path;
25 use hg::utils::files::get_bytes_from_path;
26 use hg::utils::files::get_path_from_bytes;
26 use hg::utils::files::get_path_from_bytes;
27 use hg::utils::hg_path::{hg_path_to_path_buf, HgPath};
27 use hg::utils::hg_path::{hg_path_to_path_buf, HgPath};
28 use hg::DirstateStatus;
29 use hg::PatternFileWarning;
30 use hg::StatusError;
28 use hg::StatusOptions;
31 use hg::StatusOptions;
29 use log::info;
32 use log::info;
30 use std::io;
33 use std::io;
@@ -230,12 +233,12 b' pub fn run(invocation: &crate::CliInvoca'
230 list_copies,
233 list_copies,
231 collect_traversed_dirs: false,
234 collect_traversed_dirs: false,
232 };
235 };
233 let (mut ds_status, pattern_warnings) = dmap.status(
236
234 &AlwaysMatcher,
237 type StatusResult<'a> =
235 repo.working_directory_path().to_owned(),
238 Result<(DirstateStatus<'a>, Vec<PatternFileWarning>), StatusError>;
236 ignore_files(repo, config),
239
237 options,
240 let after_status = |res: StatusResult| -> Result<_, CommandError> {
238 )?;
241 let (mut ds_status, pattern_warnings) = res?;
239 for warning in pattern_warnings {
242 for warning in pattern_warnings {
240 match warning {
243 match warning {
241 hg::PatternFileWarning::InvalidSyntax(path, syntax) => ui
244 hg::PatternFileWarning::InvalidSyntax(path, syntax) => ui
@@ -338,10 +341,25 b' pub fn run(invocation: &crate::CliInvoca'
338 output.display(b"C ", "status.clean", ds_status.clean)?;
341 output.display(b"C ", "status.clean", ds_status.clean)?;
339 }
342 }
340
343
341 let mut dirstate_write_needed = ds_status.dirty;
344 let dirstate_write_needed = ds_status.dirty;
342 let filesystem_time_at_status_start =
345 let filesystem_time_at_status_start =
343 ds_status.filesystem_time_at_status_start;
346 ds_status.filesystem_time_at_status_start;
344
347
348 Ok((
349 fixup,
350 dirstate_write_needed,
351 filesystem_time_at_status_start,
352 ))
353 };
354 let (fixup, mut dirstate_write_needed, filesystem_time_at_status_start) =
355 dmap.with_status(
356 &AlwaysMatcher,
357 repo.working_directory_path().to_owned(),
358 ignore_files(repo, config),
359 options,
360 after_status,
361 )?;
362
345 if (fixup.is_empty() || filesystem_time_at_status_start.is_none())
363 if (fixup.is_empty() || filesystem_time_at_status_start.is_none())
346 && !dirstate_write_needed
364 && !dirstate_write_needed
347 {
365 {
General Comments 0
You need to be logged in to leave comments. Login now