From: Alice Ryhl <aliceryhl@google.com>
To: Miguel Ojeda <ojeda@kernel.org>,
Andrew Morton <akpm@linux-foundation.org>
Cc: "Alex Gaynor" <alex.gaynor@gmail.com>,
"Wedson Almeida Filho" <wedsonaf@gmail.com>,
"Boqun Feng" <boqun.feng@gmail.com>,
"Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Benno Lossin" <benno.lossin@proton.me>,
"Andreas Hindborg" <a.hindborg@samsung.com>,
"Marco Elver" <elver@google.com>, "Coly Li" <colyli@suse.de>,
"Paolo Abeni" <pabeni@redhat.com>,
"Pierre Gondois" <pierre.gondois@arm.com>,
"Ingo Molnar" <mingo@kernel.org>,
"Jakub Kicinski" <kuba@kernel.org>,
"Wei Yang" <richard.weiyang@gmail.com>,
"Matthew Wilcox" <willy@infradead.org>,
linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org,
"Alice Ryhl" <aliceryhl@google.com>,
"Kees Cook" <kees@kernel.org>
Subject: [PATCH v4 07/10] rust: list: add iterators
Date: Tue, 06 Aug 2024 13:58:59 +0000 [thread overview]
Message-ID: <20240806-linked-list-v4-7-23efc510ec92@google.com> (raw)
In-Reply-To: <20240806-linked-list-v4-0-23efc510ec92@google.com>
Rust Binder has lists containing stuff such as all contexts or all
processes, and sometimes needs to iterate over them. This patch enables
Rust Binder to do that using a normal for loop.
The iterator returns the ArcBorrow type, so it is possible to grab a
refcount to values while iterating.
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
rust/kernel/list.rs | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 102 insertions(+)
diff --git a/rust/kernel/list.rs b/rust/kernel/list.rs
index 551c46a2401b..0d680156b8b1 100644
--- a/rust/kernel/list.rs
+++ b/rust/kernel/list.rs
@@ -5,7 +5,9 @@
//! A linked list implementation.
use crate::init::PinInit;
+use crate::sync::ArcBorrow;
use crate::types::Opaque;
+use core::iter::{DoubleEndedIterator, FusedIterator};
use core::marker::PhantomData;
use core::ptr;
@@ -437,6 +439,17 @@ pub fn push_all_back(&mut self, other: &mut List<T, ID>) {
// INVARIANT: The other list is now empty, so update its pointer.
other.first = ptr::null_mut();
}
+
+ /// Creates an iterator over the list.
+ pub fn iter(&self) -> Iter<'_, T, ID> {
+ // INVARIANT: If the list is empty, both pointers are null. Otherwise, both pointers point
+ // at the first element of the same list.
+ Iter {
+ current: self.first,
+ stop: self.first,
+ _ty: PhantomData,
+ }
+ }
}
impl<T: ?Sized + ListItem<ID>, const ID: u64> Default for List<T, ID> {
@@ -452,3 +465,92 @@ fn drop(&mut self) {
}
}
}
+
+/// An iterator over a [`List`].
+///
+/// # Invariants
+///
+/// * There must be a [`List`] that is immutably borrowed for the duration of `'a`.
+/// * The `current` pointer is null or points at a value in that [`List`].
+/// * The `stop` pointer is equal to the `first` field of that [`List`].
+#[derive(Clone)]
+pub struct Iter<'a, T: ?Sized + ListItem<ID>, const ID: u64 = 0> {
+ current: *mut ListLinksFields,
+ stop: *mut ListLinksFields,
+ _ty: PhantomData<&'a ListArc<T, ID>>,
+}
+
+impl<'a, T: ?Sized + ListItem<ID>, const ID: u64> Iterator for Iter<'a, T, ID> {
+ type Item = ArcBorrow<'a, T>;
+
+ fn next(&mut self) -> Option<ArcBorrow<'a, T>> {
+ if self.current.is_null() {
+ return None;
+ }
+
+ let current = self.current;
+
+ // SAFETY: We just checked that `current` is not null, so it is in a list, and hence not
+ // dangling. There's no race because the iterator holds an immutable borrow to the list.
+ let next = unsafe { (*current).next };
+ // INVARIANT: If `current` was the last element of the list, then this updates it to null.
+ // Otherwise, we update it to the next element.
+ self.current = if next != self.stop {
+ next
+ } else {
+ ptr::null_mut()
+ };
+
+ // SAFETY: The `current` pointer points at a value in the list.
+ let item = unsafe { T::view_value(ListLinks::from_fields(current)) };
+ // SAFETY:
+ // * All values in a list are stored in an `Arc`.
+ // * The value cannot be removed from the list for the duration of the lifetime annotated
+ // on the returned `ArcBorrow`, because removing it from the list would require mutable
+ // access to the list. However, the `ArcBorrow` is annotated with the iterator's
+ // lifetime, and the list is immutably borrowed for that lifetime.
+ // * Values in a list never have a `UniqueArc` reference.
+ Some(unsafe { ArcBorrow::from_raw(item) })
+ }
+}
+
+impl<'a, T: ?Sized + ListItem<ID>, const ID: u64> FusedIterator for Iter<'a, T, ID> {}
+
+impl<'a, T: ?Sized + ListItem<ID>, const ID: u64> IntoIterator for &'a List<T, ID> {
+ type IntoIter = Iter<'a, T, ID>;
+ type Item = ArcBorrow<'a, T>;
+
+ fn into_iter(self) -> Iter<'a, T, ID> {
+ self.iter()
+ }
+}
+
+/// An owning iterator into a [`List`].
+pub struct IntoIter<T: ?Sized + ListItem<ID>, const ID: u64 = 0> {
+ list: List<T, ID>,
+}
+
+impl<T: ?Sized + ListItem<ID>, const ID: u64> Iterator for IntoIter<T, ID> {
+ type Item = ListArc<T, ID>;
+
+ fn next(&mut self) -> Option<ListArc<T, ID>> {
+ self.list.pop_front()
+ }
+}
+
+impl<T: ?Sized + ListItem<ID>, const ID: u64> FusedIterator for IntoIter<T, ID> {}
+
+impl<T: ?Sized + ListItem<ID>, const ID: u64> DoubleEndedIterator for IntoIter<T, ID> {
+ fn next_back(&mut self) -> Option<ListArc<T, ID>> {
+ self.list.pop_back()
+ }
+}
+
+impl<T: ?Sized + ListItem<ID>, const ID: u64> IntoIterator for List<T, ID> {
+ type IntoIter = IntoIter<T, ID>;
+ type Item = ListArc<T, ID>;
+
+ fn into_iter(self) -> IntoIter<T, ID> {
+ IntoIter { list: self }
+ }
+}
--
2.46.0.rc2.264.g509ed76dc8-goog
next prev parent reply other threads:[~2024-08-06 13:59 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-08-06 13:58 [PATCH v4 00/10] Add Rust linked list for reference counted values Alice Ryhl
2024-08-06 13:58 ` [PATCH v4 01/10] rust: init: add `assert_pinned` macro Alice Ryhl
2024-08-06 13:58 ` [PATCH v4 02/10] rust: list: add ListArc Alice Ryhl
2024-08-06 13:58 ` [PATCH v4 03/10] rust: list: add tracking for ListArc Alice Ryhl
2024-08-06 14:30 ` Benno Lossin
2024-08-06 13:58 ` [PATCH v4 04/10] rust: list: add struct with prev/next pointers Alice Ryhl
2024-08-06 14:32 ` Benno Lossin
2024-08-06 13:58 ` [PATCH v4 05/10] rust: list: add macro for implementing ListItem Alice Ryhl
2024-08-06 14:47 ` Benno Lossin
2024-08-06 13:58 ` [PATCH v4 06/10] rust: list: add List Alice Ryhl
2024-08-06 14:44 ` Benno Lossin
2024-08-06 13:58 ` Alice Ryhl [this message]
2024-08-06 13:59 ` [PATCH v4 08/10] rust: list: add cursor Alice Ryhl
2024-08-06 13:59 ` [PATCH v4 09/10] rust: list: support heterogeneous lists Alice Ryhl
2024-08-06 14:47 ` Benno Lossin
2024-08-06 13:59 ` [PATCH v4 10/10] rust: list: add ListArcField Alice Ryhl
2024-08-06 14:00 ` [PATCH v4 00/10] Add Rust linked list for reference counted values Alice Ryhl
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240806-linked-list-v4-7-23efc510ec92@google.com \
--to=aliceryhl@google.com \
--cc=a.hindborg@samsung.com \
--cc=akpm@linux-foundation.org \
--cc=alex.gaynor@gmail.com \
--cc=benno.lossin@proton.me \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=colyli@suse.de \
--cc=elver@google.com \
--cc=gary@garyguo.net \
--cc=kees@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mingo@kernel.org \
--cc=ojeda@kernel.org \
--cc=pabeni@redhat.com \
--cc=pierre.gondois@arm.com \
--cc=richard.weiyang@gmail.com \
--cc=rust-for-linux@vger.kernel.org \
--cc=wedsonaf@gmail.com \
--cc=willy@infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).