Add optional timeout to PropertyWatcher::wait. am: 9349cf0598 am: 6bf2d6abc3 am: 7168a32209 am: 091f367b37 am: c4daacacc3 am: 2ba2eb9c29
Original change: https://android-review.googlesource.com/c/platform/system/librustutils/+/2584503
Change-Id: I56bdc442c16d6bb3bdfa9b6d16342c067e3710e6
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Android.bp b/Android.bp
index 90f5f47..fcc53b5 100644
--- a/Android.bp
+++ b/Android.bp
@@ -8,7 +8,8 @@
rustlibs: [
"libanyhow",
"libcutils_bindgen",
- "libsystem_properties_bindgen",
+ "liblibc",
+ "libsystem_properties_bindgen_sys",
"libthiserror",
],
}
@@ -40,6 +41,33 @@
auto_gen_config: true,
}
+// Build a separate rust_library rather than depending directly on libsystem_properties_bindgen,
+// to work around the fact that rust_bindgen targets only produce rlibs and not dylibs, which would
+// result in duplicate conflicting versions of liblibc. This will hopefully be fixed in the build
+// system, at which point we can delete this target and go back to using libsystem_properties_bindgen
+// directly.
+rust_library {
+ name: "libsystem_properties_bindgen_sys",
+ crate_name: "system_properties_bindgen",
+ srcs: [
+ ":libsystem_properties_bindgen",
+ ],
+ rustlibs: [
+ "liblibc",
+ ],
+ vendor_available: true,
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.btservices",
+ "com.android.compos",
+ "com.android.uwb",
+ "com.android.virt",
+ ],
+ min_sdk_version: "29",
+ lints: "none",
+ clippy_lints: "none",
+}
+
rust_bindgen {
name: "libsystem_properties_bindgen",
wrapper_src: "bindgen/system_properties.h",
@@ -53,6 +81,12 @@
"--allowlist-function=__system_property_read_callback",
"--allowlist-function=__system_property_set",
"--allowlist-function=__system_property_wait",
+ "--blocklist-type=timespec",
+ "--raw-line",
+ "use libc::timespec;",
+ ],
+ rustlibs: [
+ "liblibc",
],
vendor_available: true,
apex_available: [
@@ -95,6 +129,9 @@
srcs: [":libsystem_properties_bindgen"],
crate_name: "system_properties_bindgen_test",
test_suites: ["general-tests"],
+ rustlibs: [
+ "liblibc",
+ ],
auto_gen_config: true,
clippy_lints: "none",
lints: "none",
diff --git a/system_properties.rs b/system_properties.rs
index f2ceeeb..6edef94 100644
--- a/system_properties.rs
+++ b/system_properties.rs
@@ -16,11 +16,13 @@
//! in Android system properties.
use anyhow::Context;
+use libc::timespec;
use std::os::raw::c_char;
use std::ptr::null;
use std::{
ffi::{c_uint, c_void, CStr, CString},
str::Utf8Error,
+ time::{Duration, Instant},
};
use system_properties_bindgen::prop_info as PropInfo;
use thiserror::Error;
@@ -37,7 +39,7 @@
/// System properties are not initialized
#[error("System properties are not initialized.")]
Uninitialized,
- /// __system_property_wait timed out despite being given no timeout.
+ /// __system_property_wait timed out.
#[error("Wait failed")]
WaitFailed,
/// read callback was not called
@@ -159,12 +161,13 @@
// Waits for the property that self is watching to be created. Returns immediately if the
// property already exists.
- fn wait_for_property_creation(&mut self) -> Result<()> {
+ fn wait_for_property_creation_until(&mut self, until: Option<Instant>) -> Result<()> {
let mut global_serial = 0;
loop {
match self.get_prop_info() {
Some(_) => return Ok(()),
None => {
+ let remaining_timeout = remaining_time_until(until);
// Unsafe call for FFI. The function modifies only global_serial, and has
// no side-effects.
if !unsafe {
@@ -174,7 +177,11 @@
null(),
global_serial,
&mut global_serial,
- null(),
+ if let Some(remaining_timeout) = &remaining_timeout {
+ remaining_timeout
+ } else {
+ null()
+ },
)
} {
return Err(PropertyWatcherError::WaitFailed);
@@ -184,16 +191,17 @@
}
}
- /// Wait for the system property to change. This
- /// records the serial number of the last change, so
- /// race conditions are avoided.
- pub fn wait(&mut self) -> Result<()> {
+ /// Waits until the system property changes, or `until` is reached.
+ ///
+ /// This records the serial number of the last change, so race conditions are avoided.
+ fn wait_for_property_change_until(&mut self, until: Option<Instant>) -> Result<()> {
// If the property is null, then wait for it to be created. Subsequent waits will
// skip this step and wait for our specific property to change.
if self.prop_info.is_null() {
- return self.wait_for_property_creation();
+ return self.wait_for_property_creation_until(None);
}
+ let remaining_timeout = remaining_time_until(until);
let mut new_serial = self.serial;
// Unsafe block to call __system_property_wait.
// All arguments are private to PropertyWatcher so we
@@ -203,7 +211,11 @@
self.prop_info,
self.serial,
&mut new_serial,
- null(),
+ if let Some(remaining_timeout) = &remaining_timeout {
+ remaining_timeout
+ } else {
+ null()
+ },
)
} {
return Err(PropertyWatcherError::WaitFailed);
@@ -211,6 +223,14 @@
self.serial = new_serial;
Ok(())
}
+
+ /// Waits for the system property to change, or the timeout to elapse.
+ ///
+ /// This records the serial number of the last change, so race conditions are avoided.
+ pub fn wait(&mut self, timeout: Option<Duration>) -> Result<()> {
+ let until = timeout.map(|timeout| Instant::now() + timeout);
+ self.wait_for_property_change_until(until)
+ }
}
/// Reads a system property.
@@ -232,6 +252,27 @@
}
}
+/// Returns the duration remaining until the given instant.
+///
+/// Returns `None` if `None` is passed in, or `Some(0)` if `until` is in the past.
+fn remaining_time_until(until: Option<Instant>) -> Option<timespec> {
+ until.map(|until| {
+ duration_to_timespec(
+ until
+ .checked_duration_since(Instant::now())
+ .unwrap_or_default(),
+ )
+ })
+}
+
+/// Converts the given `Duration` to a C `timespec`.
+fn duration_to_timespec(duration: Duration) -> timespec {
+ timespec {
+ tv_sec: duration.as_secs().try_into().unwrap(),
+ tv_nsec: duration.subsec_nanos().try_into().unwrap(),
+ }
+}
+
/// Returns true if the system property `name` has the value "1", "y", "yes", "on", or "true",
/// false for "0", "n", "no", "off", or "false", or `default_value` otherwise.
pub fn read_bool(name: &str, default_value: bool) -> Result<bool> {
diff --git a/system_properties_fuzzer.rs b/system_properties_fuzzer.rs
index 5950ac2..a6d1e05 100644
--- a/system_properties_fuzzer.rs
+++ b/system_properties_fuzzer.rs
@@ -98,7 +98,7 @@
// Spawn a thread that will wait for a change to the property.
let waiter = thread::spawn(move || {
let result = match system_properties::PropertyWatcher::new(prop_str) {
- Ok(mut watcher) => watcher.wait(),
+ Ok(mut watcher) => watcher.wait(None),
Err(e) => Err(e),
};
waited_clone.store(true, Ordering::Relaxed);