| // Protocol Buffers - Google's data interchange format | |
| // Copyright 2023 Google LLC. All rights reserved. | |
| // | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file or at | |
| // https://developers.google.com/open-source/licenses/bsd | |
| #include "hpb/internal/message_lock.h" | |
| #include <atomic> | |
| #include <string> | |
| #include <thread> | |
| #include <gtest/gtest.h> | |
| #include "absl/base/attributes.h" | |
| #include "absl/base/const_init.h" | |
| #include "absl/base/thread_annotations.h" | |
| #include "absl/hash/hash.h" | |
| #include "absl/log/absl_check.h" | |
| #include "absl/synchronization/mutex.h" | |
| #include "hpb_generator/tests/test_model.hpb.h" | |
| #include "hpb/arena.h" | |
| #include "hpb/extension.h" | |
| #include "hpb/hpb.h" | |
| #include "hpb/options.h" | |
| #ifndef ASSERT_OK | |
| #define ASSERT_OK(x) ASSERT_TRUE(x.ok()) | |
| #endif // ASSERT_OK | |
| #ifndef EXPECT_OK | |
| #define EXPECT_OK(x) EXPECT_TRUE(x.ok()) | |
| #endif // EXPECT_OK | |
| namespace hpb_unittest::protos { | |
| namespace { | |
| std::string GenerateTestData() { | |
| TestModel model; | |
| model.set_str1("str"); | |
| ThemeExtension extension1; | |
| extension1.set_ext_name("theme"); | |
| ABSL_CHECK_OK(::hpb::SetExtension(&model, theme, extension1)); | |
| ThemeExtension extension2; | |
| extension2.set_ext_name("theme_extension"); | |
| ABSL_CHECK_OK( | |
| ::hpb::SetExtension(&model, ThemeExtension::theme_extension, extension2)); | |
| hpb::Arena arena; | |
| auto bytes = ::hpb::Serialize(&model, arena); | |
| ABSL_CHECK_OK(bytes); | |
| return std::string(bytes->data(), bytes->size()); | |
| } | |
| ABSL_CONST_INIT absl::Mutex m[8] = { | |
| absl::Mutex(absl::kConstInit), absl::Mutex(absl::kConstInit), | |
| absl::Mutex(absl::kConstInit), absl::Mutex(absl::kConstInit), | |
| absl::Mutex(absl::kConstInit), absl::Mutex(absl::kConstInit), | |
| absl::Mutex(absl::kConstInit), absl::Mutex(absl::kConstInit)}; | |
| void unlock_func(const void* msg) | |
| ABSL_UNLOCK_FUNCTION(m[absl::HashOf(msg) & 0x7]) { | |
| m[absl::HashOf(msg) & 0x7].unlock(); | |
| } | |
| ::hpb::internal::UpbExtensionUnlocker lock_func(const void* msg) | |
| ABSL_EXCLUSIVE_LOCK_FUNCTION(m[absl::HashOf(msg) & 0x7]) { | |
| m[absl::HashOf(msg) & 0x7].lock(); | |
| return &unlock_func; | |
| } | |
| void TestConcurrentExtensionAccess(const ::hpb::ExtensionRegistry& registry) { | |
| ::hpb::internal::upb_extension_locker_global.store(&lock_func, | |
| std::memory_order_release); | |
| const std::string payload = GenerateTestData(); | |
| TestModel parsed_model = | |
| ::hpb::Parse<TestModel>(payload, {.extension_registry = registry}) | |
| .value(); | |
| const auto test_main = [&] { EXPECT_EQ("str", parsed_model.str1()); }; | |
| const auto test_theme = [&] { | |
| ASSERT_TRUE(::hpb::HasExtension(&parsed_model, theme)); | |
| auto ext = hpb::GetExtension(&parsed_model, theme); | |
| ASSERT_OK(ext); | |
| EXPECT_EQ((*ext)->ext_name(), "theme"); | |
| }; | |
| const auto test_theme_extension = [&] { | |
| auto ext = | |
| hpb::GetExtension(&parsed_model, ThemeExtension::theme_extension); | |
| ASSERT_OK(ext); | |
| EXPECT_EQ((*ext)->ext_name(), "theme_extension"); | |
| }; | |
| const auto test_serialize = [&] { | |
| hpb::Arena arena; | |
| EXPECT_OK(::hpb::Serialize(&parsed_model, arena)); | |
| }; | |
| const auto test_copy_constructor = [&] { | |
| TestModel copy_a = parsed_model; | |
| TestModel copy_b = parsed_model; | |
| EXPECT_EQ(copy_a.has_str1(), copy_b.has_str1()); | |
| }; | |
| std::thread t1(test_main); | |
| std::thread t2(test_main); | |
| std::thread t3(test_theme); | |
| std::thread t4(test_theme); | |
| std::thread t5(test_theme_extension); | |
| std::thread t6(test_theme_extension); | |
| std::thread t7(test_serialize); | |
| std::thread t8(test_copy_constructor); | |
| t1.join(); | |
| t2.join(); | |
| t3.join(); | |
| t4.join(); | |
| t5.join(); | |
| t6.join(); | |
| t7.join(); | |
| t8.join(); | |
| test_main(); | |
| test_theme(); | |
| test_theme_extension(); | |
| } | |
| TEST(CppGeneratedCode, ConcurrentAccessDoesNotRaceBothLazy) { | |
| hpb::Arena arena; | |
| hpb::ExtensionRegistry registry(arena); | |
| TestConcurrentExtensionAccess(registry); | |
| } | |
| TEST(CppGeneratedCode, ConcurrentAccessDoesNotRaceOneLazyOneEager) { | |
| hpb::Arena arena; | |
| hpb::ExtensionRegistry r1(arena); | |
| r1.AddExtension(theme); | |
| TestConcurrentExtensionAccess(r1); | |
| hpb::ExtensionRegistry r2(arena); | |
| r2.AddExtension(ThemeExtension::theme_extension); | |
| TestConcurrentExtensionAccess(r2); | |
| } | |
| TEST(CppGeneratedCode, ConcurrentAccessDoesNotRaceBothEager) { | |
| hpb::Arena arena; | |
| hpb::ExtensionRegistry registry(arena); | |
| registry.AddExtension(theme); | |
| registry.AddExtension(ThemeExtension::theme_extension); | |
| TestConcurrentExtensionAccess(registry); | |
| } | |
| TEST(CppGeneratedCode, ConcurrentAccessDoesNotRaceGlobalInstance) { | |
| hpb::Arena arena; | |
| TestConcurrentExtensionAccess(hpb::ExtensionRegistry::generated_registry()); | |
| } | |
| } // namespace | |
| } // namespace hpb_unittest::protos |