Merge "Implement android::base::WaitForProperty." am: 01003d40fc
am: d96f68ee21
Change-Id: Icbc1efd9293849d3b4cd9ccaba84eeacde974f9c
diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h
index 4d7082a..e275fa2 100644
--- a/base/include/android-base/properties.h
+++ b/base/include/android-base/properties.h
@@ -58,6 +58,9 @@
// 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);
+
} // namespace base
} // namespace android
diff --git a/base/properties.cpp b/base/properties.cpp
index 37daf9a..0f0f638 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -14,9 +14,12 @@
* limitations under the License.
*/
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+
#include "android-base/properties.h"
#include <sys/system_properties.h>
+#include <sys/_system_properties.h>
#include <string>
@@ -78,5 +81,43 @@
return (__system_property_set(key.c_str(), value.c_str()) == 0);
}
+struct WaitForPropertyData {
+ bool done;
+ const std::string* expected_value;
+ unsigned last_read_serial;
+};
+
+static void WaitForPropertyCallback(void* data_ptr, const char*, const char* value, unsigned serial) {
+ WaitForPropertyData* data = reinterpret_cast<WaitForPropertyData*>(data_ptr);
+ if (*data->expected_value == value) {
+ data->done = true;
+ } else {
+ data->last_read_serial = serial;
+ }
+}
+
+void WaitForProperty(const std::string& key, const std::string& expected_value) {
+ // 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);
+ }
+
+ WaitForPropertyData data;
+ data.expected_value = &expected_value;
+ data.done = false;
+ while (true) {
+ // Check whether the property has the value we're looking for?
+ __system_property_read_callback(pi, WaitForPropertyCallback, &data);
+ if (data.done) return;
+
+ // It didn't so wait for it to change before checking again.
+ __system_property_wait(pi, data.last_read_serial);
+ }
+}
+
} // namespace base
} // namespace android
diff --git a/base/properties_test.cpp b/base/properties_test.cpp
index da89ec5..d8186be 100644
--- a/base/properties_test.cpp
+++ b/base/properties_test.cpp
@@ -18,7 +18,12 @@
#include <gtest/gtest.h>
+#include <atomic>
+#include <chrono>
#include <string>
+#include <thread>
+
+using namespace std::chrono_literals;
TEST(properties, smoke) {
android::base::SetProperty("debug.libbase.property_test", "hello");
@@ -119,3 +124,18 @@
TEST(properties, GetUintProperty_uint16_t) { CheckGetUintProperty<uint16_t>(); }
TEST(properties, GetUintProperty_uint32_t) { CheckGetUintProperty<uint32_t>(); }
TEST(properties, GetUintProperty_uint64_t) { CheckGetUintProperty<uint64_t>(); }
+
+TEST(properties, WaitForProperty) {
+ std::atomic<bool> flag{false};
+ std::thread thread([&]() {
+ std::this_thread::sleep_for(100ms);
+ android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
+ while (!flag) std::this_thread::yield();
+ android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
+ });
+
+ android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a");
+ flag = true;
+ android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b");
+ thread.join();
+}
diff --git a/libcutils/properties.cpp b/libcutils/properties.cpp
index 43ad574..d2645e6 100644
--- a/libcutils/properties.cpp
+++ b/libcutils/properties.cpp
@@ -129,7 +129,7 @@
void* cookie;
};
-static void trampoline(void* raw_data, const char* name, const char* value) {
+static void trampoline(void* raw_data, const char* name, const char* value, unsigned /*serial*/) {
callback_data* data = reinterpret_cast<callback_data*>(raw_data);
data->callback(name, value, data->cookie);
}