From 2f2c08c317ced069229f577c0261855abbcd5d7b Mon Sep 17 00:00:00 2001 From: Timothy Hutchins Date: Mon, 7 Aug 2023 15:16:43 -0500 Subject: [PATCH] Implemented submittable_executor and some other missed files A file was named wrong. It was changed. --- .../implementation/linux/device_info_test.cc | 129 +++++++++ .../linux/{mutext_test.cc => mutex_test.cc} | 0 .../linux/scheduled_executor_test.cc | 179 +++++++++++++ .../linux/submittable_executor.cc | 63 +++++ .../linux/submittable_executor.h | 53 ++++ .../linux/submittable_executor_test.cc | 250 ++++++++++++++++++ .../implementation/linux/thread_pool_test.cc | 71 +++++ 7 files changed, 745 insertions(+) create mode 100644 internal/platform/implementation/linux/device_info_test.cc rename internal/platform/implementation/linux/{mutext_test.cc => mutex_test.cc} (100%) create mode 100644 internal/platform/implementation/linux/scheduled_executor_test.cc create mode 100644 internal/platform/implementation/linux/submittable_executor.cc create mode 100644 internal/platform/implementation/linux/submittable_executor.h create mode 100644 internal/platform/implementation/linux/submittable_executor_test.cc create mode 100644 internal/platform/implementation/linux/thread_pool_test.cc diff --git a/internal/platform/implementation/linux/device_info_test.cc b/internal/platform/implementation/linux/device_info_test.cc new file mode 100644 index 00000000..9168a7b4 --- /dev/null +++ b/internal/platform/implementation/linux/device_info_test.cc @@ -0,0 +1,129 @@ +// Copyright 2021 Google LLC +// +// 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 +// +// https://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 "internal/platform/implementation/linux/device_info.h" + +#include +#include + +#include "gtest/gtest.h" +#include "absl/synchronization/notification.h" +#include "internal/platform/implementation/device_info.h" + +namespace nearby { +namespace linux { +namespace { + +TEST(DeviceInfo, DISABLED_GetComputerName) { + EXPECT_TRUE(DeviceInfo().GetOsDeviceName().has_value()); +} + +TEST(DeviceInfo, DISABLED_GetDeviceType) { + EXPECT_EQ(DeviceInfo().GetDeviceType(), api::DeviceInfo::DeviceType::kLaptop); +} + +TEST(DeviceInfo, GetOsType) { + EXPECT_EQ(DeviceInfo().GetOsType(), api::DeviceInfo::OsType::kLinux); +} + +TEST(DeviceInfo, DISABLED_GetFullName) { + EXPECT_TRUE(DeviceInfo().GetFullName().has_value()); +} + +TEST(DeviceInfo, DISABLED_GetGivenName) { + EXPECT_TRUE(DeviceInfo().GetGivenName().has_value()); +} + +TEST(DeviceInfo, DISABLED_GetLastName) { + EXPECT_TRUE(DeviceInfo().GetLastName().has_value()); +} + +TEST(DeviceInfo, DISABLED_GetProfileUserName) { + EXPECT_TRUE(DeviceInfo().GetProfileUserName().has_value()); +} + +TEST(DeviceInfo, DISABLED_GetLocalAppDataPath) { + EXPECT_TRUE(DeviceInfo().GetLocalAppDataPath().has_value()); +} + +TEST(DeviceInfo, DISABLED_GetDownloadPath) { + EXPECT_TRUE(DeviceInfo().GetDownloadPath().has_value()); +} + +TEST(DeviceInfo, DISABLED_GetTemporaryPath) { + EXPECT_TRUE(DeviceInfo().GetTemporaryPath().has_value()); +} + +TEST(DeviceInfo, DISABLED_IsScreenLocked) { + EXPECT_FALSE(DeviceInfo().IsScreenLocked()); +} + +TEST(DeviceInfo, DISABLED_RegisterScreenLockedListener) { + std::function listener_1 = + [](api::DeviceInfo::ScreenStatus) {}; + std::function listener_2 = + [](api::DeviceInfo::ScreenStatus) {}; + + DeviceInfo device_info; + EXPECT_EQ(device_info.screen_locked_listeners_.size(), 0); + + device_info.RegisterScreenLockedListener("listener_1", listener_1); + EXPECT_EQ(device_info.screen_locked_listeners_.size(), 1); + + device_info.RegisterScreenLockedListener("listener_2", listener_2); + EXPECT_EQ(device_info.screen_locked_listeners_.size(), 2); +} + +TEST(DeviceInfo, DISABLED_UnregisterScreenLockedListener) { + std::function listener_1 = + [](api::DeviceInfo::ScreenStatus) {}; + std::function listener_2 = + [](api::DeviceInfo::ScreenStatus) {}; + + DeviceInfo device_info; + EXPECT_EQ(device_info.screen_locked_listeners_.size(), 0); + + device_info.RegisterScreenLockedListener("listener_1", listener_1); + device_info.RegisterScreenLockedListener("listener_2", listener_2); + EXPECT_EQ(device_info.screen_locked_listeners_.size(), 2); + + device_info.UnregisterScreenLockedListener("listener_1"); + EXPECT_EQ(device_info.screen_locked_listeners_.size(), 1); + + device_info.UnregisterScreenLockedListener("listener_2"); + EXPECT_EQ(device_info.screen_locked_listeners_.size(), 0); +} + +TEST(DeviceInfo, DISABLED_UpdateScreenLockedListener) { + absl::Notification notification; + + api::DeviceInfo::ScreenStatus screen_locked_tracker = + api::DeviceInfo::ScreenStatus::kUndetermined; + + std::function listener = + [&screen_locked_tracker, + ¬ification](api::DeviceInfo::ScreenStatus status) { + screen_locked_tracker = api::DeviceInfo::ScreenStatus::kLocked; + notification.Notify(); + }; + + DeviceInfo device_info; + device_info.RegisterScreenLockedListener("listener", listener); + EXPECT_TRUE(notification.WaitForNotificationWithTimeout(absl::Seconds(5))); + EXPECT_EQ(screen_locked_tracker, api::DeviceInfo::ScreenStatus::kLocked); +} + +} // namespace +} // namespace linux +} // namespace nearby diff --git a/internal/platform/implementation/linux/mutext_test.cc b/internal/platform/implementation/linux/mutex_test.cc similarity index 100% rename from internal/platform/implementation/linux/mutext_test.cc rename to internal/platform/implementation/linux/mutex_test.cc diff --git a/internal/platform/implementation/linux/scheduled_executor_test.cc b/internal/platform/implementation/linux/scheduled_executor_test.cc new file mode 100644 index 00000000..2c9e804f --- /dev/null +++ b/internal/platform/implementation/linux/scheduled_executor_test.cc @@ -0,0 +1,179 @@ +// Copyright 2021 Google LLC +// +// 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 +// +// https://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 "internal/platform/implementation/linux/scheduled_executor.h" + +#include +#include + +#include "gtest/gtest.h" +#include "absl/synchronization/notification.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "internal/platform/implementation/linux/test_data.h" + +namespace nearby { +namespace linux { +namespace { + +TEST(ScheduledExecutorTests, ExecuteSucceeds) { + absl::Notification notification; + // Arrange + std::string expected(RUNNABLE_0_TEXT.c_str()); + + auto submittableExecutor = std::make_unique(); + std::string output = std::string(); + // Container to note threads that ran + std::unique_ptr> threadIds = + std::make_unique>(); + + threadIds->push_back(std::this_thread::get_id()); + + // Act + submittableExecutor->Execute([&]() { + threadIds->push_back(std::this_thread::get_id()); + output.append(RUNNABLE_0_TEXT.c_str()); + notification.Notify(); + }); + + ASSERT_TRUE( + notification.WaitForNotificationWithTimeout(absl::Milliseconds(200))); + submittableExecutor->Shutdown(); + + // Assert + // We should've run 1 time on the main thread, and 1 times on the + // workerThread + ASSERT_EQ(threadIds->size(), 2); + // We should still be on the main thread + ASSERT_EQ(std::this_thread::get_id(), threadIds->at(0)); + // We should've run all runnables on the worker thread + ASSERT_EQ(output, expected); +} + +TEST(ScheduledExecutorTests, ScheduleSucceeds) { + absl::Notification notification; + // Arrange + std::string expected(RUNNABLE_0_TEXT.c_str()); + + auto submittableExecutor = std::make_unique(); + std::string output = std::string(); + // Container to note threads that ran + std::unique_ptr> threadIds = + std::make_unique>(); + + threadIds->push_back(std::this_thread::get_id()); + + std::chrono::system_clock::time_point timeNow = + std::chrono::system_clock::now(); + std::chrono::system_clock::time_point timeExecuted; + + // Act + submittableExecutor->Schedule( + [&]() { + timeExecuted = std::chrono::system_clock::now(); + threadIds->push_back(std::this_thread::get_id()); + output.append(RUNNABLE_0_TEXT.c_str()); + notification.Notify(); + }, + absl::Milliseconds(50)); + + ASSERT_TRUE( + notification.WaitForNotificationWithTimeout(absl::Milliseconds(200))); + submittableExecutor->Shutdown(); + + ASSERT_EQ(threadIds->size(), 2); + // We should still be on the main thread + ASSERT_EQ(std::this_thread::get_id(), threadIds->at(0)); + // We should've run all runnables on the worker thread + ASSERT_EQ(output, expected); +} + +TEST(ScheduledExecutorTests, CancelSucceeds) { + absl::Notification notification; + // Arrange + std::string expected(""); + + auto submittableExecutor = std::make_unique(); + std::string output = std::string(); + // Container to note threads that ran + std::unique_ptr> threadIds = + std::make_unique>(); + + threadIds->push_back(std::this_thread::get_id()); + + // Act + auto cancelable = submittableExecutor->Schedule( + [&]() { + threadIds->push_back(std::this_thread::get_id()); + output.append(RUNNABLE_0_TEXT.c_str()); + notification.Notify(); + }, + absl::Milliseconds(1000)); + + auto actual = cancelable->Cancel(); + + EXPECT_FALSE( + notification.WaitForNotificationWithTimeout(absl::Milliseconds(2000))); + submittableExecutor->Shutdown(); + + // Assert + ASSERT_TRUE(actual); + ASSERT_EQ(threadIds->size(), 1); + // We should still be on the main thread + ASSERT_EQ(std::this_thread::get_id(), threadIds->at(0)); + // We should've run all runnables on the worker thread + ASSERT_EQ(output, expected); +} + +TEST(ScheduledExecutorTests, CancelAfterStartedFails) { + absl::Notification notification; + // Arrange + std::string expected(RUNNABLE_0_TEXT.c_str()); + + auto submittableExecutor = std::make_unique(); + std::string output = std::string(); + // Container to note threads that ran + std::unique_ptr> threadIds = + std::make_unique>(); + + threadIds->push_back(std::this_thread::get_id()); + + // Act + auto cancelable = submittableExecutor->Schedule( + [&]() { + threadIds->push_back(std::this_thread::get_id()); + output.append(RUNNABLE_0_TEXT.c_str()); + notification.Notify(); + }, + absl::Milliseconds(100)); + + absl::SleepFor(absl::Milliseconds(200)); + auto actual = cancelable->Cancel(); + + ASSERT_TRUE( + notification.WaitForNotificationWithTimeout(absl::Milliseconds(2000))); + submittableExecutor->Shutdown(); + + // Assert + ASSERT_FALSE(actual); + ASSERT_EQ(threadIds->size(), 2); + // We should still be on the main thread + ASSERT_EQ(std::this_thread::get_id(), threadIds->at(0)); + // We should've run all runnables on the worker thread + ASSERT_EQ(output, expected); +} + +} // namespace +} // namespace linux +} // namespace nearby diff --git a/internal/platform/implementation/linux/submittable_executor.cc b/internal/platform/implementation/linux/submittable_executor.cc new file mode 100644 index 00000000..3b721522 --- /dev/null +++ b/internal/platform/implementation/linux/submittable_executor.cc @@ -0,0 +1,63 @@ +// Copyright 2021 Google LLC +// +// 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 +// +// https://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 "internal/platform/implementation/linux/submittable_executor.h" + +#include "internal/platform/implementation/linux/executor.h" +#include "internal/platform/logging.h" + +namespace nearby { +namespace linux { + +SubmittableExecutor::SubmittableExecutor() : SubmittableExecutor(1) {} + +SubmittableExecutor::SubmittableExecutor(int32_t max_concurrancy) + : executor_(std::make_unique(max_concurrancy)), + shut_down_(false) {} + +bool SubmittableExecutor::DoSubmit(Runnable&& wrapped_callable) { + if (!shut_down_) { + executor_->Execute(std::move(wrapped_callable)); + return true; + } + + NEARBY_LOGS(ERROR) << "Error: " << __func__ + << ": Attempt to DoSubmit on a shutdown executor."; + + return false; +} + +// https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html#execute-java.lang.Runnable- +void SubmittableExecutor::Execute(Runnable&& runnable) { + if (!shut_down_) { + executor_->Execute(std::move(runnable)); + } else { + NEARBY_LOGS(ERROR) << "Error: " << __func__ + << ": Attempt to Execute on a shutdown executor."; + } +} + +// https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html#shutdown-- +void SubmittableExecutor::Shutdown() { + if (!shut_down_) { + executor_->Shutdown(); + shut_down_ = true; + } + + NEARBY_LOGS(ERROR) << "Error: " << __func__ + << ": Attempt to Shutdown on a shutdown executor."; +} + +} // namespace linux +} // namespace nearby diff --git a/internal/platform/implementation/linux/submittable_executor.h b/internal/platform/implementation/linux/submittable_executor.h new file mode 100644 index 00000000..63b1ef0b --- /dev/null +++ b/internal/platform/implementation/linux/submittable_executor.h @@ -0,0 +1,53 @@ +// Copyright 2020 Google LLC +// +// 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 +// +// https://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. + +#ifndef PLATFORM_IMPL_LINUX_SUBMITTABLE_EXECUTOR_H_ +#define PLATFORM_IMPL_LINUX_SUBMITTABLE_EXECUTOR_H_ + +#include "internal/platform/implementation/submittable_executor.h" +#include "internal/platform/implementation/linux/executor.h" + +namespace nearby { +namespace linux { + +// Main interface to be used by platform as a base class for +// - MultiThreadExecutorWrapper +// - SingleThreadExecutorWrapper +// Platform must override bool submit(absl::AnyInvocable) method. +class SubmittableExecutor : public api::SubmittableExecutor { + public: + SubmittableExecutor(); + SubmittableExecutor(int32_t maxConcurrancy); + ~SubmittableExecutor() override = default; + + // Submit a callable (with no delay). + // Returns true, if callable was submitted, false otherwise. + // Callable is not submitted if shutdown is in progress. + bool DoSubmit(Runnable&& wrapped_callable) override; + + // https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html#execute-java.lang.Runnable- + void Execute(Runnable&& runnable) override; + + // https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html#shutdown-- + void Shutdown() override; + + private: + std::unique_ptr executor_; + std::atomic_bool shut_down_ = false; +}; + +} // namespace linux +} // namespace nearby + +#endif // PLATFORM_IMPL_LINUX_SUBMITTABLE_EXECUTOR_H_ diff --git a/internal/platform/implementation/linux/submittable_executor_test.cc b/internal/platform/implementation/linux/submittable_executor_test.cc new file mode 100644 index 00000000..b3cf11d6 --- /dev/null +++ b/internal/platform/implementation/linux/submittable_executor_test.cc @@ -0,0 +1,250 @@ +// Copyright 2021 Google LLC +// +// 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 +// +// https://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 "internal/platform/implementation/linux/submittable_executor.h" + +#include +#include + +#include "gtest/gtest.h" +#include "absl/synchronization/blocking_counter.h" +#include "absl/synchronization/notification.h" +#include "absl/time/time.h" +#include "internal/platform/implementation/linux/test_data.h" + +namespace nearby { +namespace linux { +namespace { + +constexpr absl::Duration kWaitTimeout = absl::Milliseconds(200); + +TEST(SubmittableExecutorTests, SingleThreadedExecuteSucceeds) { + absl::Notification notification; + // Arrange + std::string expected(RUNNABLE_0_TEXT.c_str()); + + auto submittableExecutor = std::make_unique(); + std::string output = std::string(); + // Container to note threads that ran + auto threadIds = std::make_unique>(); + + threadIds->push_back(std::this_thread::get_id()); + + // Act + submittableExecutor->Execute([&]() { + threadIds->push_back(std::this_thread::get_id()); + output.append(RUNNABLE_0_TEXT.c_str()); + notification.Notify(); + }); + + ASSERT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout)); + submittableExecutor->Shutdown(); + + // Assert + // We should've run 1 time on the main thread, and 1 times on the + // workerThread + ASSERT_EQ(threadIds->size(), 2); + // We should still be on the main thread + ASSERT_EQ(std::this_thread::get_id(), threadIds->at(0)); + // We should've run all runnables on the worker thread + ASSERT_EQ(output, expected); +} + +TEST(SubmittableExecutorTests, SingleThreadedExecuteAfterShutdownFails) { + // Arrange + std::string expected(""); + + auto submittableExecutor = std::make_unique(); + std::string output = std::string(); + // Container to note threads that ran + auto threadIds = std::make_unique>(); + + threadIds->push_back(std::this_thread::get_id()); + + submittableExecutor->Shutdown(); + + // Act + submittableExecutor->Execute([&output, &threadIds]() { + threadIds->push_back(std::this_thread::get_id()); + output.append(RUNNABLE_0_TEXT.c_str()); + }); + + // Assert + // We should've run 1 time on the main thread, and 0 times on the + // workerThread + ASSERT_EQ(threadIds->size(), 1); + // We should still be on the main thread + ASSERT_EQ(std::this_thread::get_id(), threadIds->at(0)); + // We should've run all runnables on the worker thread + ASSERT_EQ(output, expected); +} + +TEST(SubmittableExecutorTests, SingleThreadedDoSubmitSucceeds) { + absl::Notification notification; + // Arrange + std::string expected(RUNNABLE_0_TEXT.c_str()); + + auto submittableExecutor = std::make_unique(); + std::string output = std::string(); + // Container to note threads that ran + auto threadIds = std::make_unique>(); + + threadIds->push_back(std::this_thread::get_id()); + + // Act + auto result = submittableExecutor->DoSubmit([&]() { + threadIds->push_back(std::this_thread::get_id()); + output.append(RUNNABLE_0_TEXT.c_str()); + notification.Notify(); + }); + + ASSERT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout)); + submittableExecutor->Shutdown(); + + // Assert + // We should've said we were going to run this one + ASSERT_TRUE(result); + // We should've run 1 time on the main thread, and 1 times on the + // workerThread + ASSERT_EQ(threadIds->size(), 2); + // We should still be on the main thread + ASSERT_EQ(std::this_thread::get_id(), threadIds->at(0)); + // We should've run all runnables on the worker thread + ASSERT_EQ(output, expected); +} + +TEST(SubmittableExecutorTests, + SingleThreadedDoSubmitAfterShutdownReturnsFalse) { + // Arrange + std::string expected(""); + + auto submittableExecutor = std::make_unique(); + std::unique_ptr output = std::make_unique(); + // Container to note threads that ran + auto threadIds = std::make_unique>(); + + threadIds->push_back(std::this_thread::get_id()); + + submittableExecutor->Shutdown(); + + // Act + auto result = submittableExecutor->DoSubmit([&output, &threadIds]() { + threadIds->push_back(std::this_thread::get_id()); + output->append(RUNNABLE_0_TEXT.c_str()); + }); + + // Assert + // We should've said we were going to run this one + ASSERT_FALSE(result); + // We should've run 1 time on the main thread, and 1 times on the + // workerThread + ASSERT_EQ(threadIds->size(), 1); + // We should still be on the main thread + ASSERT_EQ(std::this_thread::get_id(), threadIds->at(0)); + // We should've run all runnables on the worker thread + ASSERT_EQ(*output.get(), expected); +} + +TEST(SubmittableExecutorTests, SingleThreadedExecuteMultipleTasksSucceeds) { + absl::BlockingCounter blocking_counter(5); + + // Arrange + std::string expected(RUNNABLE_ALL_TEXT.c_str()); + + auto submittableExecutor = std::make_unique(); + std::unique_ptr output = std::make_unique(); + // Container to note threads that ran + auto threadIds = std::make_unique>(); + + threadIds->push_back(std::this_thread::get_id()); + + // Act + for (int index = 0; index < 5; index++) { + submittableExecutor->Execute([&, index]() { + threadIds->push_back(std::this_thread::get_id()); + char buffer[128]; + snprintf(buffer, sizeof(buffer), "%s%d, ", RUNNABLE_TEXT.c_str(), index); + output->append(std::string(buffer)); + blocking_counter.DecrementCount(); + }); + } + + blocking_counter.Wait(); + submittableExecutor->Shutdown(); + + // Assert + // We should've run 1 time on the main thread, and 5 times on the + // workerThread + ASSERT_EQ(threadIds->size(), 6); + // We should still be on the main thread + ASSERT_EQ(std::this_thread::get_id(), threadIds->at(0)); + // We should've run all runnables on the worker thread + auto workerThreadId = threadIds->at(1); + for (int index = 1; index < threadIds->size(); index++) { + ASSERT_EQ(threadIds->at(index), workerThreadId); + } + + // We should of run them in the order submitted + ASSERT_EQ(*output.get(), expected); +} + +TEST(SubmittableExecutorTests, SingleThreadedDoSubmitMultipleTasksSucceeds) { + absl::BlockingCounter blocking_counter(5); + + // Arrange + std::string expected(RUNNABLE_ALL_TEXT.c_str()); + + auto submittableExecutor = std::make_unique(); + std::unique_ptr output = std::make_unique(); + // Container to note threads that ran + auto threadIds = std::make_unique>(); + + threadIds->push_back(std::this_thread::get_id()); + + // Act + bool result = true; + for (int index = 0; index < 5; index++) { + result &= submittableExecutor->DoSubmit([&, index]() { + threadIds->push_back(std::this_thread::get_id()); + char buffer[128]; + snprintf(buffer, sizeof(buffer), "%s%d, ", RUNNABLE_TEXT.c_str(), index); + output->append(std::string(buffer)); + blocking_counter.DecrementCount(); + }); + } + + blocking_counter.Wait(); + submittableExecutor->Shutdown(); + + // Assert + // All of these should have submitted + ASSERT_TRUE(result); + // We should've run 1 time on the main thread, and 5 times on the + // workerThread + ASSERT_EQ(threadIds->size(), 6); + // We should still be on the main thread + ASSERT_EQ(std::this_thread::get_id(), threadIds->at(0)); + // We should've run all runnables on the worker thread + auto workerThreadId = threadIds->at(1); + for (int index = 1; index < threadIds->size(); index++) { + ASSERT_EQ(threadIds->at(index), workerThreadId); + } + + // We should of run them in the order submitted + ASSERT_EQ(*output.get(), expected); +} + +} // namespace +} // namespace linux +} // namespace nearby diff --git a/internal/platform/implementation/linux/thread_pool_test.cc b/internal/platform/implementation/linux/thread_pool_test.cc new file mode 100644 index 00000000..f2c8327a --- /dev/null +++ b/internal/platform/implementation/linux/thread_pool_test.cc @@ -0,0 +1,71 @@ +// Copyright 2022 Google LLC +// +// 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 +// +// https://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 "internal/platform/implementation/linux/thread_pool.h" + +#include + +#include "gtest/gtest.h" +#include "absl/synchronization/blocking_counter.h" +#include "absl/synchronization/notification.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" + +namespace nearby { +namespace linux { +namespace { + +constexpr int kTaskCount = 10; + +TEST(ThreadPool, TasksInSingleThreadRunInSequence) { + absl::BlockingCounter blocking_counter(kTaskCount); + auto pool = ThreadPool::Create(1); + std::vector completed_tasks; + std::vector expected_tasks; + + for (int i = 0; i < kTaskCount; ++i) { + expected_tasks.push_back(i); + pool->Run([&, i]() { + absl::SleepFor(absl::Milliseconds(200)); + completed_tasks.push_back(i); + blocking_counter.DecrementCount(); + }); + } + + blocking_counter.Wait(); + EXPECT_EQ(completed_tasks, expected_tasks); + pool->ShutDown(); +} + +TEST(ThreadPool, TasksInMultipleThreadsRunInParallel) { + absl::BlockingCounter blocking_counter(kTaskCount); + absl::Time start_time = absl::Now(); + + auto pool = ThreadPool::Create(2); + + for (int i = 0; i < kTaskCount; ++i) { + pool->Run([&]() { + absl::SleepFor(absl::Milliseconds(200)); + blocking_counter.DecrementCount(); + }); + } + + blocking_counter.Wait(); + EXPECT_TRUE(absl::Now() - start_time < absl::Milliseconds(1500)); + pool->ShutDown(); +} + +} // namespace +} // namespace linux +} // namespace nearby