Show More
@@ -687,8 +687,7 b' class dirstate(object):' | |||||
687 | delaywrite = self._ui.configint(b'debug', b'dirstate.delaywrite') |
|
687 | delaywrite = self._ui.configint(b'debug', b'dirstate.delaywrite') | |
688 | if delaywrite > 0: |
|
688 | if delaywrite > 0: | |
689 | # do we have any files to delay for? |
|
689 | # do we have any files to delay for? | |
690 |
|
|
690 | for f, e in pycompat.iteritems(self._map): | |
691 | for f, e in items: |
|
|||
692 | if e[0] == b'n' and e[3] == now: |
|
691 | if e[0] == b'n' and e[3] == now: | |
693 | import time # to avoid useless import |
|
692 | import time # to avoid useless import | |
694 |
|
693 | |||
@@ -700,12 +699,6 b' class dirstate(object):' | |||||
700 | time.sleep(end - clock) |
|
699 | time.sleep(end - clock) | |
701 | now = end # trust our estimate that the end is near now |
|
700 | now = end # trust our estimate that the end is near now | |
702 | break |
|
701 | break | |
703 | # since the iterator is potentially not deleted, |
|
|||
704 | # delete the iterator to release the reference for the Rust |
|
|||
705 | # implementation. |
|
|||
706 | # TODO make the Rust implementation behave like Python |
|
|||
707 | # since this would not work with a non ref-counting GC. |
|
|||
708 | del items |
|
|||
709 |
|
702 | |||
710 | self._map.write(st, now) |
|
703 | self._map.write(st, now) | |
711 | self._lastnormaltime = 0 |
|
704 | self._lastnormaltime = 0 |
@@ -38,17 +38,20 b' use std::sync::atomic::{AtomicUsize, Ord' | |||||
38 | /// `PySharedRefCell` is allowed only through its `borrow_mut()`. |
|
38 | /// `PySharedRefCell` is allowed only through its `borrow_mut()`. | |
39 | /// - The `py: Python<'_>` token, which makes sure that any data access is |
|
39 | /// - The `py: Python<'_>` token, which makes sure that any data access is | |
40 | /// synchronized by the GIL. |
|
40 | /// synchronized by the GIL. | |
|
41 | /// - The `borrow_count`, which is the number of references borrowed from | |||
|
42 | /// `PyLeaked`. Just like `RefCell`, mutation is prohibited while `PyLeaked` | |||
|
43 | /// is borrowed. | |||
41 | /// - The `generation` counter, which increments on `borrow_mut()`. `PyLeaked` |
|
44 | /// - The `generation` counter, which increments on `borrow_mut()`. `PyLeaked` | |
42 | /// reference is valid only if the `current_generation()` equals to the |
|
45 | /// reference is valid only if the `current_generation()` equals to the | |
43 | /// `generation` at the time of `leak_immutable()`. |
|
46 | /// `generation` at the time of `leak_immutable()`. | |
44 | #[derive(Debug, Default)] |
|
47 | #[derive(Debug, Default)] | |
45 | struct PySharedState { |
|
48 | struct PySharedState { | |
46 | leak_count: Cell<usize>, |
|
|||
47 | mutably_borrowed: Cell<bool>, |
|
49 | mutably_borrowed: Cell<bool>, | |
48 | // The counter variable could be Cell<usize> since any operation on |
|
50 | // The counter variable could be Cell<usize> since any operation on | |
49 | // PySharedState is synchronized by the GIL, but being "atomic" makes |
|
51 | // PySharedState is synchronized by the GIL, but being "atomic" makes | |
50 | // PySharedState inherently Sync. The ordering requirement doesn't |
|
52 | // PySharedState inherently Sync. The ordering requirement doesn't | |
51 | // matter thanks to the GIL. |
|
53 | // matter thanks to the GIL. | |
|
54 | borrow_count: AtomicUsize, | |||
52 | generation: AtomicUsize, |
|
55 | generation: AtomicUsize, | |
53 | } |
|
56 | } | |
54 |
|
57 | |||
@@ -69,7 +72,7 b' impl PySharedState {' | |||||
69 | mutable reference in a Python object", |
|
72 | mutable reference in a Python object", | |
70 | )); |
|
73 | )); | |
71 | } |
|
74 | } | |
72 |
match self. |
|
75 | match self.current_borrow_count(py) { | |
73 | 0 => { |
|
76 | 0 => { | |
74 | self.mutably_borrowed.replace(true); |
|
77 | self.mutably_borrowed.replace(true); | |
75 | // Note that this wraps around to the same value if mutably |
|
78 | // Note that this wraps around to the same value if mutably | |
@@ -78,21 +81,9 b' impl PySharedState {' | |||||
78 | self.generation.fetch_add(1, Ordering::Relaxed); |
|
81 | self.generation.fetch_add(1, Ordering::Relaxed); | |
79 | Ok(PyRefMut::new(py, pyrefmut, self)) |
|
82 | Ok(PyRefMut::new(py, pyrefmut, self)) | |
80 | } |
|
83 | } | |
81 | // TODO |
|
|||
82 | // For now, this works differently than Python references |
|
|||
83 | // in the case of iterators. |
|
|||
84 | // Python does not complain when the data an iterator |
|
|||
85 | // points to is modified if the iterator is never used |
|
|||
86 | // afterwards. |
|
|||
87 | // Here, we are stricter than this by refusing to give a |
|
|||
88 | // mutable reference if it is already borrowed. |
|
|||
89 | // While the additional safety might be argued for, it |
|
|||
90 | // breaks valid programming patterns in Python and we need |
|
|||
91 | // to fix this issue down the line. |
|
|||
92 | _ => Err(AlreadyBorrowed::new( |
|
84 | _ => Err(AlreadyBorrowed::new( | |
93 | py, |
|
85 | py, | |
94 |
"Cannot borrow mutably while |
|
86 | "Cannot borrow mutably while immutably borrowed", | |
95 | immutable references in Python objects", |
|
|||
96 | )), |
|
87 | )), | |
97 | } |
|
88 | } | |
98 | } |
|
89 | } | |
@@ -121,23 +112,35 b' impl PySharedState {' | |||||
121 | // can move stuff to PySharedRefCell? |
|
112 | // can move stuff to PySharedRefCell? | |
122 | let ptr = data.as_ptr(); |
|
113 | let ptr = data.as_ptr(); | |
123 | let state_ptr: *const PySharedState = &data.py_shared_state; |
|
114 | let state_ptr: *const PySharedState = &data.py_shared_state; | |
124 | self.leak_count.replace(self.leak_count.get() + 1); |
|
|||
125 | Ok((&*ptr, &*state_ptr)) |
|
115 | Ok((&*ptr, &*state_ptr)) | |
126 | } |
|
116 | } | |
127 |
|
117 | |||
|
118 | fn current_borrow_count(&self, _py: Python) -> usize { | |||
|
119 | self.borrow_count.load(Ordering::Relaxed) | |||
|
120 | } | |||
|
121 | ||||
|
122 | fn increase_borrow_count(&self, _py: Python) { | |||
|
123 | // Note that this wraps around if there are more than usize::MAX | |||
|
124 | // borrowed references, which shouldn't happen due to memory limit. | |||
|
125 | self.borrow_count.fetch_add(1, Ordering::Relaxed); | |||
|
126 | } | |||
|
127 | ||||
|
128 | fn decrease_borrow_count(&self, _py: Python) { | |||
|
129 | let prev_count = self.borrow_count.fetch_sub(1, Ordering::Relaxed); | |||
|
130 | assert!(prev_count > 0); | |||
|
131 | } | |||
|
132 | ||||
128 | /// # Safety |
|
133 | /// # Safety | |
129 | /// |
|
134 | /// | |
130 | /// It's up to you to make sure the reference is about to be deleted |
|
135 | /// It's up to you to make sure the reference is about to be deleted | |
131 | /// when updating the leak count. |
|
136 | /// when updating the leak count. | |
132 |
fn decrease_leak_count(&self, |
|
137 | fn decrease_leak_count(&self, py: Python, mutable: bool) { | |
133 | if mutable { |
|
138 | if mutable { | |
134 |
assert_eq!(self. |
|
139 | assert_eq!(self.current_borrow_count(py), 0); | |
135 | assert!(self.mutably_borrowed.get()); |
|
140 | assert!(self.mutably_borrowed.get()); | |
136 | self.mutably_borrowed.replace(false); |
|
141 | self.mutably_borrowed.replace(false); | |
137 | } else { |
|
142 | } else { | |
138 | let count = self.leak_count.get(); |
|
143 | unimplemented!(); | |
139 | assert!(count > 0); |
|
|||
140 | self.leak_count.replace(count - 1); |
|
|||
141 | } |
|
144 | } | |
142 | } |
|
145 | } | |
143 |
|
146 | |||
@@ -146,6 +149,32 b' impl PySharedState {' | |||||
146 | } |
|
149 | } | |
147 | } |
|
150 | } | |
148 |
|
151 | |||
|
152 | /// Helper to keep the borrow count updated while the shared object is | |||
|
153 | /// immutably borrowed without using the `RefCell` interface. | |||
|
154 | struct BorrowPyShared<'a> { | |||
|
155 | py: Python<'a>, | |||
|
156 | py_shared_state: &'a PySharedState, | |||
|
157 | } | |||
|
158 | ||||
|
159 | impl<'a> BorrowPyShared<'a> { | |||
|
160 | fn new( | |||
|
161 | py: Python<'a>, | |||
|
162 | py_shared_state: &'a PySharedState, | |||
|
163 | ) -> BorrowPyShared<'a> { | |||
|
164 | py_shared_state.increase_borrow_count(py); | |||
|
165 | BorrowPyShared { | |||
|
166 | py, | |||
|
167 | py_shared_state, | |||
|
168 | } | |||
|
169 | } | |||
|
170 | } | |||
|
171 | ||||
|
172 | impl Drop for BorrowPyShared<'_> { | |||
|
173 | fn drop(&mut self) { | |||
|
174 | self.py_shared_state.decrease_borrow_count(self.py); | |||
|
175 | } | |||
|
176 | } | |||
|
177 | ||||
149 | /// `RefCell` wrapper to be safely used in conjunction with `PySharedState`. |
|
178 | /// `RefCell` wrapper to be safely used in conjunction with `PySharedState`. | |
150 | /// |
|
179 | /// | |
151 | /// This object can be stored in a `py_class!` object as a data field. Any |
|
180 | /// This object can be stored in a `py_class!` object as a data field. Any | |
@@ -273,13 +302,6 b" impl<'a, T> Drop for PyRefMut<'a, T> {" | |||||
273 | /// Allows a `py_class!` generated struct to share references to one of its |
|
302 | /// Allows a `py_class!` generated struct to share references to one of its | |
274 | /// data members with Python. |
|
303 | /// data members with Python. | |
275 | /// |
|
304 | /// | |
276 | /// # Warning |
|
|||
277 | /// |
|
|||
278 | /// TODO allow Python container types: for now, integration with the garbage |
|
|||
279 | /// collector does not extend to Rust structs holding references to Python |
|
|||
280 | /// objects. Should the need surface, `__traverse__` and `__clear__` will |
|
|||
281 | /// need to be written as per the `rust-cpython` docs on GC integration. |
|
|||
282 | /// |
|
|||
283 | /// # Parameters |
|
305 | /// # Parameters | |
284 | /// |
|
306 | /// | |
285 | /// * `$name` is the same identifier used in for `py_class!` macro call. |
|
307 | /// * `$name` is the same identifier used in for `py_class!` macro call. | |
@@ -376,7 +398,7 b' impl<T> PyLeaked<T> {' | |||||
376 | ) -> PyResult<PyLeakedRef<'a, T>> { |
|
398 | ) -> PyResult<PyLeakedRef<'a, T>> { | |
377 | self.validate_generation(py)?; |
|
399 | self.validate_generation(py)?; | |
378 | Ok(PyLeakedRef { |
|
400 | Ok(PyLeakedRef { | |
379 | _py: py, |
|
401 | _borrow: BorrowPyShared::new(py, self.py_shared_state), | |
380 | data: self.data.as_ref().unwrap(), |
|
402 | data: self.data.as_ref().unwrap(), | |
381 | }) |
|
403 | }) | |
382 | } |
|
404 | } | |
@@ -393,7 +415,7 b' impl<T> PyLeaked<T> {' | |||||
393 | ) -> PyResult<PyLeakedRefMut<'a, T>> { |
|
415 | ) -> PyResult<PyLeakedRefMut<'a, T>> { | |
394 | self.validate_generation(py)?; |
|
416 | self.validate_generation(py)?; | |
395 | Ok(PyLeakedRefMut { |
|
417 | Ok(PyLeakedRefMut { | |
396 | _py: py, |
|
418 | _borrow: BorrowPyShared::new(py, self.py_shared_state), | |
397 | data: self.data.as_mut().unwrap(), |
|
419 | data: self.data.as_mut().unwrap(), | |
398 | }) |
|
420 | }) | |
399 | } |
|
421 | } | |
@@ -451,23 +473,9 b' impl<T> PyLeaked<T> {' | |||||
451 | } |
|
473 | } | |
452 | } |
|
474 | } | |
453 |
|
475 | |||
454 | impl<T> Drop for PyLeaked<T> { |
|
|||
455 | fn drop(&mut self) { |
|
|||
456 | // py_shared_state should be alive since we do have |
|
|||
457 | // a Python reference to the owner object. Taking GIL makes |
|
|||
458 | // sure that the state is only accessed by this thread. |
|
|||
459 | let gil = Python::acquire_gil(); |
|
|||
460 | let py = gil.python(); |
|
|||
461 | if self.data.is_none() { |
|
|||
462 | return; // moved to another PyLeaked |
|
|||
463 | } |
|
|||
464 | self.py_shared_state.decrease_leak_count(py, false); |
|
|||
465 | } |
|
|||
466 | } |
|
|||
467 |
|
||||
468 | /// Immutably borrowed reference to a leaked value. |
|
476 | /// Immutably borrowed reference to a leaked value. | |
469 | pub struct PyLeakedRef<'a, T> { |
|
477 | pub struct PyLeakedRef<'a, T> { | |
470 | _py: Python<'a>, |
|
478 | _borrow: BorrowPyShared<'a>, | |
471 | data: &'a T, |
|
479 | data: &'a T, | |
472 | } |
|
480 | } | |
473 |
|
481 | |||
@@ -481,7 +489,7 b" impl<T> Deref for PyLeakedRef<'_, T> {" | |||||
481 |
|
489 | |||
482 | /// Mutably borrowed reference to a leaked value. |
|
490 | /// Mutably borrowed reference to a leaked value. | |
483 | pub struct PyLeakedRefMut<'a, T> { |
|
491 | pub struct PyLeakedRefMut<'a, T> { | |
484 | _py: Python<'a>, |
|
492 | _borrow: BorrowPyShared<'a>, | |
485 | data: &'a mut T, |
|
493 | data: &'a mut T, | |
486 | } |
|
494 | } | |
487 |
|
495 | |||
@@ -570,6 +578,7 b' macro_rules! py_shared_iterator {' | |||||
570 | let mut iter = leaked.try_borrow_mut(py)?; |
|
578 | let mut iter = leaked.try_borrow_mut(py)?; | |
571 | match iter.next() { |
|
579 | match iter.next() { | |
572 | None => { |
|
580 | None => { | |
|
581 | drop(iter); | |||
573 | // replace Some(inner) by None, drop $leaked |
|
582 | // replace Some(inner) by None, drop $leaked | |
574 | inner_opt.take(); |
|
583 | inner_opt.take(); | |
575 | Ok(None) |
|
584 | Ok(None) | |
@@ -649,9 +658,7 b' mod test {' | |||||
649 | let (gil, owner) = prepare_env(); |
|
658 | let (gil, owner) = prepare_env(); | |
650 | let py = gil.python(); |
|
659 | let py = gil.python(); | |
651 | let leaked = owner.string_shared(py).leak_immutable().unwrap(); |
|
660 | let leaked = owner.string_shared(py).leak_immutable().unwrap(); | |
652 | owner.string(py).py_shared_state.leak_count.replace(0); // XXX cheat |
|
|||
653 | owner.string_shared(py).borrow_mut().unwrap().clear(); |
|
661 | owner.string_shared(py).borrow_mut().unwrap().clear(); | |
654 | owner.string(py).py_shared_state.leak_count.replace(1); // XXX cheat |
|
|||
655 | assert!(leaked.try_borrow(py).is_err()); |
|
662 | assert!(leaked.try_borrow(py).is_err()); | |
656 | } |
|
663 | } | |
657 |
|
664 | |||
@@ -661,9 +668,7 b' mod test {' | |||||
661 | let py = gil.python(); |
|
668 | let py = gil.python(); | |
662 | let leaked = owner.string_shared(py).leak_immutable().unwrap(); |
|
669 | let leaked = owner.string_shared(py).leak_immutable().unwrap(); | |
663 | let mut leaked_iter = unsafe { leaked.map(py, |s| s.chars()) }; |
|
670 | let mut leaked_iter = unsafe { leaked.map(py, |s| s.chars()) }; | |
664 | owner.string(py).py_shared_state.leak_count.replace(0); // XXX cheat |
|
|||
665 | owner.string_shared(py).borrow_mut().unwrap().clear(); |
|
671 | owner.string_shared(py).borrow_mut().unwrap().clear(); | |
666 | owner.string(py).py_shared_state.leak_count.replace(1); // XXX cheat |
|
|||
667 | assert!(leaked_iter.try_borrow_mut(py).is_err()); |
|
672 | assert!(leaked_iter.try_borrow_mut(py).is_err()); | |
668 | } |
|
673 | } | |
669 |
|
674 | |||
@@ -673,19 +678,39 b' mod test {' | |||||
673 | let (gil, owner) = prepare_env(); |
|
678 | let (gil, owner) = prepare_env(); | |
674 | let py = gil.python(); |
|
679 | let py = gil.python(); | |
675 | let leaked = owner.string_shared(py).leak_immutable().unwrap(); |
|
680 | let leaked = owner.string_shared(py).leak_immutable().unwrap(); | |
676 | owner.string(py).py_shared_state.leak_count.replace(0); // XXX cheat |
|
|||
677 | owner.string_shared(py).borrow_mut().unwrap().clear(); |
|
681 | owner.string_shared(py).borrow_mut().unwrap().clear(); | |
678 | owner.string(py).py_shared_state.leak_count.replace(1); // XXX cheat |
|
|||
679 | let _leaked_iter = unsafe { leaked.map(py, |s| s.chars()) }; |
|
682 | let _leaked_iter = unsafe { leaked.map(py, |s| s.chars()) }; | |
680 | } |
|
683 | } | |
681 |
|
684 | |||
682 | #[test] |
|
685 | #[test] | |
683 | fn test_borrow_mut_while_leaked() { |
|
686 | fn test_borrow_mut_while_leaked_ref() { | |
684 | let (gil, owner) = prepare_env(); |
|
687 | let (gil, owner) = prepare_env(); | |
685 | let py = gil.python(); |
|
688 | let py = gil.python(); | |
686 | assert!(owner.string_shared(py).borrow_mut().is_ok()); |
|
689 | assert!(owner.string_shared(py).borrow_mut().is_ok()); | |
687 |
let |
|
690 | let leaked = owner.string_shared(py).leak_immutable().unwrap(); | |
688 | // TODO: will be allowed |
|
691 | { | |
|
692 | let _leaked_ref = leaked.try_borrow(py).unwrap(); | |||
|
693 | assert!(owner.string_shared(py).borrow_mut().is_err()); | |||
|
694 | { | |||
|
695 | let _leaked_ref2 = leaked.try_borrow(py).unwrap(); | |||
|
696 | assert!(owner.string_shared(py).borrow_mut().is_err()); | |||
|
697 | } | |||
689 | assert!(owner.string_shared(py).borrow_mut().is_err()); |
|
698 | assert!(owner.string_shared(py).borrow_mut().is_err()); | |
690 | } |
|
699 | } | |
|
700 | assert!(owner.string_shared(py).borrow_mut().is_ok()); | |||
691 | } |
|
701 | } | |
|
702 | ||||
|
703 | #[test] | |||
|
704 | fn test_borrow_mut_while_leaked_ref_mut() { | |||
|
705 | let (gil, owner) = prepare_env(); | |||
|
706 | let py = gil.python(); | |||
|
707 | assert!(owner.string_shared(py).borrow_mut().is_ok()); | |||
|
708 | let leaked = owner.string_shared(py).leak_immutable().unwrap(); | |||
|
709 | let mut leaked_iter = unsafe { leaked.map(py, |s| s.chars()) }; | |||
|
710 | { | |||
|
711 | let _leaked_ref = leaked_iter.try_borrow_mut(py).unwrap(); | |||
|
712 | assert!(owner.string_shared(py).borrow_mut().is_err()); | |||
|
713 | } | |||
|
714 | assert!(owner.string_shared(py).borrow_mut().is_ok()); | |||
|
715 | } | |||
|
716 | } |
General Comments 0
You need to be logged in to leave comments.
Login now