Re: [PATCH v4 12/13] rust: sync: introduce `CondVar`

From: Alice Ryhl
Date: Fri Apr 14 2023 - 07:55:07 EST


On 4/11/23 07:45, Wedson Almeida Filho wrote:
From: Wedson Almeida Filho <walmeida@xxxxxxxxxxxxx>

This is the traditional condition variable or monitor synchronisation
primitive. It is implemented with C's `wait_queue_head_t`.

It allows users to release a lock and go to sleep while guaranteeing
that notifications won't be missed. This is achieved by enqueuing a wait
entry before releasing the lock.

Cc: Peter Zijlstra <peterz@xxxxxxxxxxxxx>
Cc: Ingo Molnar <mingo@xxxxxxxxxx>
Cc: Will Deacon <will@xxxxxxxxxx>
Cc: Waiman Long <longman@xxxxxxxxxx>
Reviewed-by: Martin Rodriguez Reboredo <yakoyoku@xxxxxxxxx>
Signed-off-by: Wedson Almeida Filho <walmeida@xxxxxxxxxxxxx>

Reviewed-by: Alice Ryhl <aliceryhl@xxxxxxxxxx>

I have a few methods below that the binder driver will need on the condvar. I'll let it be up to you whether you wish to include them in this patch. Otherwise, we will add them when we start upstreaming binder.

+ /// Releases the lock and waits for a notification in interruptible mode.
+ ///
+ /// Atomically releases the given lock (whose ownership is proven by the guard) and puts the
+ /// thread to sleep, reacquiring the lock on wake up. It wakes up when notified by
+ /// [`CondVar::notify_one`] or [`CondVar::notify_all`], or when the thread receives a signal.
+ /// It may also wake up spuriously.
+ ///
+ /// Returns whether there is a signal pending.
+ #[must_use = "wait returns if a signal is pending, so the caller must check the return value"]
+ pub fn wait<T: ?Sized, B: Backend>(&self, guard: &mut Guard<'_, T, B>) -> bool {
+ self.wait_internal(bindings::TASK_INTERRUPTIBLE, guard);
+ crate::current!().signal_pending()
+ }

The binder driver will need a `wait_timeout` method.

+ /// Calls the kernel function to notify the appropriate number of threads with the given flags.
+ fn notify(&self, count: i32, flags: u32) {
+ // SAFETY: `wait_list` points to valid memory.
+ unsafe {
+ bindings::__wake_up(
+ self.wait_list.get(),
+ bindings::TASK_NORMAL,
+ count,
+ flags as _,
+ )
+ };
+ }
+
+ /// Wakes a single waiter up, if any.
+ ///
+ /// This is not 'sticky' in the sense that if no thread is waiting, the notification is lost
+ /// completely (as opposed to automatically waking up the next waiter).
+ pub fn notify_one(&self) {
+ self.notify(1, 0);
+ }
+
+ /// Wakes all waiters up, if any.
+ ///
+ /// This is not 'sticky' in the sense that if no thread is waiting, the notification is lost
+ /// completely (as opposed to automatically waking up the next waiter).
+ pub fn notify_all(&self) {
+ self.notify(0, 0);
+ }

Android binder will also need a `notify_sync` method. It could be implemented like this:

/// Calls the kernel function to notify one thread synchronously.
pub fn notify_sync(&self) {
// SAFETY: `wait_list` points to valid memory.
unsafe { bindings::__wake_up_sync(self.wait_list.get(), bindings::TASK_NORMAL) };
}