OSDN Git Service

Observer Registry
authorHansong Zhang <hsz@google.com>
Tue, 16 Jul 2019 01:34:34 +0000 (18:34 -0700)
committerHansong Zhang <hsz@google.com>
Thu, 18 Jul 2019 20:37:42 +0000 (13:37 -0700)
Helper for client (observer) to track registration and drop callbacks if
observer is unregistered

Test: bluetooth_test_gd
Change-Id: Icc58d812bccbcb85c8b8142c659503636f498b6a

gd/common/Android.bp
gd/common/observer_registry.h [new file with mode: 0644]
gd/common/observer_registry_test.cc [new file with mode: 0644]

index e9e0b25..8eed81a 100644 (file)
@@ -13,5 +13,6 @@ filegroup {
         "blocking_queue_unittest.cc",
         "class_of_device_unittest.cc",
         "bidi_queue_unittest.cc",
+        "observer_registry_test.cc",
     ],
 }
diff --git a/gd/common/observer_registry.h b/gd/common/observer_registry.h
new file mode 100644 (file)
index 0000000..99de57f
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <array>
+
+#include "common/bind.h"
+#include "common/callback.h"
+#include "os/log.h"
+
+namespace bluetooth {
+namespace common {
+
+// Tracks an observer registration on client (observer) code. Register() returns a wrapped callback object which can
+// be passed to server's register API. Unregister() invalidates the wrapped callback so all callbacks that are posted
+// to the client handler after the client called Unregister() call and before the server processed the Unregister()
+// call on its handler, are dropped.
+// Note: Register() invalidates the previous registration.
+class SingleObserverRegistry {
+ public:
+  template <typename R, typename... T>
+  decltype(auto) Register(Callback<R(T...)> callback) {
+    session_++;
+    return Bind(&SingleObserverRegistry::callback_wrapper<R, T...>, Unretained(this), session_, callback);
+  }
+
+  void Unregister() {
+    session_++;
+  }
+
+ private:
+  template <typename R, typename... T>
+  void callback_wrapper(int session, Callback<R(T...)> callback, T... t) {
+    if (session == session_) {
+      callback.Run(std::forward<T>(t)...);
+    }
+  }
+
+  uint8_t session_ = 0;
+};
+
+// Tracks observer registration for multiple event type. Each event type is represented as an integer in [0, Capacity).
+template <int Capacity = 10>
+class MultipleObserverRegistry {
+ public:
+  template <typename R, typename... T>
+  decltype(auto) Register(int event_type, Callback<R(T...)> callback) {
+    ASSERT(event_type < Capacity);
+    return registry_[event_type].Register(callback);
+  }
+
+  void Unregister(int event_type) {
+    ASSERT(event_type < Capacity);
+    registry_[event_type].Unregister();
+  }
+
+  std::array<SingleObserverRegistry, Capacity> registry_;
+};
+
+}  // namespace common
+}  // namespace bluetooth
diff --git a/gd/common/observer_registry_test.cc b/gd/common/observer_registry_test.cc
new file mode 100644 (file)
index 0000000..f1233a8
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common/observer_registry.h"
+
+#include "common/bind.h"
+#include "gtest/gtest.h"
+
+namespace bluetooth {
+namespace common {
+
+class SingleObserverRegistryTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    registry_ = new SingleObserverRegistry;
+  }
+
+  void TearDown() override {
+    delete registry_;
+  }
+
+  SingleObserverRegistry* registry_;
+};
+
+void Increment(int* count) {
+  (*count)++;
+}
+
+void IncrementBy(int* count, int n) {
+  (*count) += n;
+}
+
+TEST_F(SingleObserverRegistryTest, wrapped_callback) {
+  int count = 0;
+  auto wrapped_callback = registry_->Register(Bind(&Increment, Unretained(&count)));
+  wrapped_callback.Run();
+  EXPECT_EQ(count, 1);
+  wrapped_callback.Run();
+  EXPECT_EQ(count, 2);
+  wrapped_callback.Run();
+  EXPECT_EQ(count, 3);
+  registry_->Unregister();
+}
+
+TEST_F(SingleObserverRegistryTest, unregister) {
+  int count = 0;
+  auto wrapped_callback = registry_->Register(Bind(&Increment, Unretained(&count)));
+  registry_->Unregister();
+  wrapped_callback.Run();
+  EXPECT_EQ(count, 0);
+}
+
+TEST_F(SingleObserverRegistryTest, second_register) {
+  int count = 0;
+  auto wrapped_callback = registry_->Register(Bind(&Increment, Unretained(&count)));
+  registry_->Unregister();
+  auto wrapped_callback2 = registry_->Register(Bind(&Increment, Unretained(&count)));
+  wrapped_callback.Run();
+  EXPECT_EQ(count, 0);
+  wrapped_callback2.Run();
+  EXPECT_EQ(count, 1);
+}
+
+class MultipleObserverRegistryTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    registry_ = new MultipleObserverRegistry<2>;
+  }
+
+  void TearDown() override {
+    delete registry_;
+  }
+
+  MultipleObserverRegistry<2>* registry_;
+};
+
+TEST_F(MultipleObserverRegistryTest, single_wrapped_callback) {
+  int count = 0;
+  auto wrapped_callback = registry_->Register(0, Bind(&Increment, Unretained(&count)));
+  wrapped_callback.Run();
+  EXPECT_EQ(count, 1);
+  wrapped_callback.Run();
+  EXPECT_EQ(count, 2);
+  wrapped_callback.Run();
+  EXPECT_EQ(count, 3);
+  registry_->Unregister(0);
+}
+
+TEST_F(MultipleObserverRegistryTest, multiple_wrapped_callback) {
+  int count = 0;
+  auto wrapped_callback0 = registry_->Register(0, Bind(&Increment, Unretained(&count)));
+  auto wrapped_callback1 = registry_->Register(1, Bind(&IncrementBy, Unretained(&count), 10));
+  wrapped_callback0.Run();
+  EXPECT_EQ(count, 1);
+  wrapped_callback1.Run();
+  EXPECT_EQ(count, 11);
+  registry_->Unregister(0);
+  wrapped_callback0.Run();
+  EXPECT_EQ(count, 11);
+  wrapped_callback1.Run();
+  EXPECT_EQ(count, 21);
+  registry_->Unregister(1);
+  EXPECT_EQ(count, 21);
+}
+
+}  // namespace common
+}  // namespace bluetooth