diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h index e2324b785..4de5e57cc 100644 --- a/base/include/android-base/properties.h +++ b/base/include/android-base/properties.h @@ -66,6 +66,12 @@ bool WaitForProperty(const std::string& key, const std::string& expected_value, std::chrono::milliseconds relative_timeout); +// Waits for the system property `key` to be created. +// Times out after `relative_timeout`. +// Returns true on success, false on timeout. +bool WaitForPropertyCreation(const std::string& key, + std::chrono::milliseconds relative_timeout); + } // namespace base } // namespace android diff --git a/base/properties.cpp b/base/properties.cpp index acd6c0fbf..32c012830 100644 --- a/base/properties.cpp +++ b/base/properties.cpp @@ -108,8 +108,10 @@ static void DurationToTimeSpec(timespec& ts, std::chrono::nanoseconds d) { ts.tv_nsec = ns.count(); } +using AbsTime = std::chrono::time_point; + static void UpdateTimeSpec(timespec& ts, - const std::chrono::time_point& timeout) { + const AbsTime& timeout) { auto now = std::chrono::steady_clock::now(); auto remaining_timeout = std::chrono::duration_cast(timeout - now); if (remaining_timeout < 0ns) { @@ -119,13 +121,16 @@ static void UpdateTimeSpec(timespec& ts, } } -bool WaitForProperty(const std::string& key, - const std::string& expected_value, - std::chrono::milliseconds relative_timeout) { +// Waits for the system property `key` to be created. +// Times out after `relative_timeout`. +// Sets absolute_timeout which represents absolute time for the timeout. +// Returns nullptr on timeout. +static const prop_info* WaitForPropertyCreation(const std::string& key, + const std::chrono::milliseconds& relative_timeout, + AbsTime& absolute_timeout) { // TODO: boot_clock? auto now = std::chrono::steady_clock::now(); - std::chrono::time_point absolute_timeout = now + relative_timeout; - timespec ts; + absolute_timeout = now + relative_timeout; // Find the property's prop_info*. const prop_info* pi; @@ -133,14 +138,25 @@ bool WaitForProperty(const std::string& key, while ((pi = __system_property_find(key.c_str())) == nullptr) { // The property doesn't even exist yet. // Wait for a global change and then look again. + timespec ts; UpdateTimeSpec(ts, absolute_timeout); - if (!__system_property_wait(nullptr, global_serial, &global_serial, &ts)) return false; + if (!__system_property_wait(nullptr, global_serial, &global_serial, &ts)) return nullptr; } + return pi; +} + +bool WaitForProperty(const std::string& key, + const std::string& expected_value, + std::chrono::milliseconds relative_timeout) { + AbsTime absolute_timeout; + const prop_info* pi = WaitForPropertyCreation(key, relative_timeout, absolute_timeout); + if (pi == nullptr) return false; WaitForPropertyData data; data.expected_value = &expected_value; data.done = false; while (true) { + timespec ts; // Check whether the property has the value we're looking for? __system_property_read_callback(pi, WaitForPropertyCallback, &data); if (data.done) return true; @@ -152,5 +168,11 @@ bool WaitForProperty(const std::string& key, } } +bool WaitForPropertyCreation(const std::string& key, + std::chrono::milliseconds relative_timeout) { + AbsTime absolute_timeout; + return (WaitForPropertyCreation(key, relative_timeout, absolute_timeout) != nullptr); +} + } // namespace base } // namespace android diff --git a/base/properties_test.cpp b/base/properties_test.cpp index c68c2f805..1bbe48249 100644 --- a/base/properties_test.cpp +++ b/base/properties_test.cpp @@ -150,3 +150,25 @@ TEST(properties, WaitForProperty_timeout) { // Upper bounds on timing are inherently flaky, but let's try... ASSERT_LT(std::chrono::duration_cast(t1 - t0), 600ms); } + +TEST(properties, WaitForPropertyCreation) { + std::thread thread([&]() { + std::this_thread::sleep_for(100ms); + android::base::SetProperty("debug.libbase.WaitForPropertyCreation_test", "a"); + }); + + ASSERT_TRUE(android::base::WaitForPropertyCreation( + "debug.libbase.WaitForPropertyCreation_test", 1s)); + thread.join(); +} + +TEST(properties, WaitForPropertyCreation_timeout) { + auto t0 = std::chrono::steady_clock::now(); + ASSERT_FALSE(android::base::WaitForPropertyCreation( + "debug.libbase.WaitForPropertyCreation_timeout_test", 200ms)); + auto t1 = std::chrono::steady_clock::now(); + + ASSERT_GE(std::chrono::duration_cast(t1 - t0), 200ms); + // Upper bounds on timing are inherently flaky, but let's try... + ASSERT_LT(std::chrono::duration_cast(t1 - t0), 600ms); +}