Merge "Add timeout support to android::base::WaitForProperty."
This commit is contained in:
commit
ff692a5ace
3 changed files with 58 additions and 9 deletions
|
|
@ -23,6 +23,7 @@
|
|||
#error Only bionic supports system properties.
|
||||
#endif
|
||||
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
|
|
@ -58,8 +59,12 @@ template <typename T> T GetUintProperty(const std::string& key,
|
|||
// tell you whether or not your call succeeded. A `false` return value definitely means failure.
|
||||
bool SetProperty(const std::string& key, const std::string& value);
|
||||
|
||||
// Waits for the system property `key` to have the value `expected_value`, .
|
||||
void WaitForProperty(const std::string& key, const std::string& expected_value);
|
||||
// Waits for the system property `key` to have the value `expected_value`.
|
||||
// Times out after `relative_timeout`.
|
||||
// Returns true on success, false on timeout.
|
||||
bool WaitForProperty(const std::string& key,
|
||||
const std::string& expected_value,
|
||||
std::chrono::milliseconds relative_timeout);
|
||||
|
||||
} // namespace base
|
||||
} // namespace android
|
||||
|
|
|
|||
|
|
@ -21,10 +21,14 @@
|
|||
#include <sys/system_properties.h>
|
||||
#include <sys/_system_properties.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
|
||||
#include <android-base/parseint.h>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace android {
|
||||
namespace base {
|
||||
|
||||
|
|
@ -96,14 +100,41 @@ static void WaitForPropertyCallback(void* data_ptr, const char*, const char* val
|
|||
}
|
||||
}
|
||||
|
||||
void WaitForProperty(const std::string& key, const std::string& expected_value) {
|
||||
// TODO: chrono_utils?
|
||||
static void DurationToTimeSpec(timespec& ts, std::chrono::nanoseconds d) {
|
||||
auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
|
||||
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(d - s);
|
||||
ts.tv_sec = s.count();
|
||||
ts.tv_nsec = ns.count();
|
||||
}
|
||||
|
||||
static void UpdateTimeSpec(timespec& ts,
|
||||
const std::chrono::time_point<std::chrono::steady_clock>& timeout) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto remaining_timeout = std::chrono::duration_cast<std::chrono::nanoseconds>(timeout - now);
|
||||
if (remaining_timeout < 0ns) {
|
||||
ts = { 0, 0 };
|
||||
} else {
|
||||
DurationToTimeSpec(ts, remaining_timeout);
|
||||
}
|
||||
}
|
||||
|
||||
bool WaitForProperty(const std::string& key,
|
||||
const std::string& expected_value,
|
||||
std::chrono::milliseconds relative_timeout) {
|
||||
// TODO: boot_clock?
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
std::chrono::time_point<std::chrono::steady_clock> absolute_timeout = now + relative_timeout;
|
||||
timespec ts;
|
||||
|
||||
// Find the property's prop_info*.
|
||||
const prop_info* pi;
|
||||
unsigned global_serial = 0;
|
||||
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.
|
||||
global_serial = __system_property_wait_any(global_serial);
|
||||
UpdateTimeSpec(ts, absolute_timeout);
|
||||
if (!__system_property_wait(nullptr, global_serial, &global_serial, &ts)) return false;
|
||||
}
|
||||
|
||||
WaitForPropertyData data;
|
||||
|
|
@ -112,10 +143,12 @@ void WaitForProperty(const std::string& key, const std::string& expected_value)
|
|||
while (true) {
|
||||
// Check whether the property has the value we're looking for?
|
||||
__system_property_read_callback(pi, WaitForPropertyCallback, &data);
|
||||
if (data.done) return;
|
||||
if (data.done) return true;
|
||||
|
||||
// It didn't so wait for it to change before checking again.
|
||||
__system_property_wait(pi, data.last_read_serial);
|
||||
// It didn't, so wait for the property to change before checking again.
|
||||
UpdateTimeSpec(ts, absolute_timeout);
|
||||
uint32_t unused;
|
||||
if (!__system_property_wait(pi, data.last_read_serial, &unused, &ts)) return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -134,8 +134,19 @@ TEST(properties, WaitForProperty) {
|
|||
android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
|
||||
});
|
||||
|
||||
android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a");
|
||||
ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
|
||||
flag = true;
|
||||
android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b");
|
||||
ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", 1s));
|
||||
thread.join();
|
||||
}
|
||||
|
||||
TEST(properties, WaitForProperty_timeout) {
|
||||
auto t0 = std::chrono::steady_clock::now();
|
||||
ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_timeout_test", "a",
|
||||
200ms));
|
||||
auto t1 = std::chrono::steady_clock::now();
|
||||
|
||||
ASSERT_GE(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 200ms);
|
||||
// Upper bounds on timing are inherently flaky, but let's try...
|
||||
ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue