From dbcd179788a4cce4e9e3ae545e736148438c5c6f Mon Sep 17 00:00:00 2001 From: Konstantin Varlamov Date: Tue, 8 May 2018 19:37:20 -0400 Subject: Firestore C++: compile ExecutorLibdispatch in Objective-C++ mode (#1237) dispatch_queue_t is defined differently in libdispatch depending on whether the library header is being include from Objective-C (or Objective-C++) code, or else from C or C++ code. Make sure that all source files in Firestore that include executor_libdispatch.h are compiled in the same mode (Objective-C++) to avoid linker errors. --- .../src/firebase/firestore/util/CMakeLists.txt | 2 +- .../firestore/util/executor_libdispatch.cc | 296 --------------------- .../firebase/firestore/util/executor_libdispatch.h | 10 +- .../firestore/util/executor_libdispatch.mm | 296 +++++++++++++++++++++ .../test/firebase/firestore/util/CMakeLists.txt | 6 +- .../firestore/util/async_queue_libdispatch_test.mm | 87 ++++++ .../firestore/util/async_queue_std_test.cc | 41 +++ .../firestore/util/async_queue_test_libdispatch.cc | 86 ------ .../firestore/util/async_queue_test_std.cc | 41 --- .../firestore/util/executor_libdispatch_test.cc | 43 --- .../firestore/util/executor_libdispatch_test.mm | 43 +++ 11 files changed, 479 insertions(+), 472 deletions(-) delete mode 100644 Firestore/core/src/firebase/firestore/util/executor_libdispatch.cc create mode 100644 Firestore/core/src/firebase/firestore/util/executor_libdispatch.mm create mode 100644 Firestore/core/test/firebase/firestore/util/async_queue_libdispatch_test.mm create mode 100644 Firestore/core/test/firebase/firestore/util/async_queue_std_test.cc delete mode 100644 Firestore/core/test/firebase/firestore/util/async_queue_test_libdispatch.cc delete mode 100644 Firestore/core/test/firebase/firestore/util/async_queue_test_std.cc delete mode 100644 Firestore/core/test/firebase/firestore/util/executor_libdispatch_test.cc create mode 100644 Firestore/core/test/firebase/firestore/util/executor_libdispatch_test.mm (limited to 'Firestore/core') diff --git a/Firestore/core/src/firebase/firestore/util/CMakeLists.txt b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt index 29d91c7..b2b015b 100644 --- a/Firestore/core/src/firebase/firestore/util/CMakeLists.txt +++ b/Firestore/core/src/firebase/firestore/util/CMakeLists.txt @@ -130,7 +130,7 @@ if(HAVE_LIBDISPATCH) cc_library( firebase_firestore_util_executor_libdispatch SOURCES - executor_libdispatch.cc + executor_libdispatch.mm executor_libdispatch.h executor.h DEPENDS diff --git a/Firestore/core/src/firebase/firestore/util/executor_libdispatch.cc b/Firestore/core/src/firebase/firestore/util/executor_libdispatch.cc deleted file mode 100644 index b40f0dd..0000000 --- a/Firestore/core/src/firebase/firestore/util/executor_libdispatch.cc +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Copyright 2018 Google - * - * 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 "Firestore/core/src/firebase/firestore/util/executor_libdispatch.h" - -namespace firebase { -namespace firestore { -namespace util { -namespace internal { - -namespace { - -absl::string_view StringViewFromDispatchLabel(const char* const label) { - // Make sure string_view's data is not null, because it's used for logging. - return label ? absl::string_view{label} : absl::string_view{""}; -} - -void RunSynchronized(const ExecutorLibdispatch* const executor, - std::function&& work) { - if (executor->IsCurrentExecutor()) { - work(); - } else { - DispatchSync(executor->dispatch_queue(), std::move(work)); - } -} - -} // namespace - -void DispatchAsync(const dispatch_queue_t queue, std::function&& work) { - // Dynamically allocate the function to make sure the object is valid by the - // time libdispatch gets to it. - const auto wrap = new std::function{std::move(work)}; - - dispatch_async_f(queue, wrap, [](void* const raw_work) { - const auto unwrap = static_cast*>(raw_work); - (*unwrap)(); - delete unwrap; - }); -} - -void DispatchSync(const dispatch_queue_t queue, Executor::Operation work) { - // Unlike dispatch_async_f, dispatch_sync_f blocks until the work passed to it - // is done, so passing a reference to a local variable is okay. - dispatch_sync_f(queue, &work, [](void* const raw_work) { - const auto unwrap = static_cast(raw_work); - (*unwrap)(); - }); -} - -// Represents a "busy" time slot on the schedule. -// -// Since libdispatch doesn't provide a way to cancel a scheduled operation, once -// a slot is created, it will always stay in the schedule until the time is -// past. Consequently, it is more useful to think of a time slot than -// a particular scheduled operation -- by the time the slot comes, operation may -// or may not be there (imagine getting to a meeting and finding out it's been -// canceled). -// -// Precondition: all member functions, including the constructor, are *only* -// invoked on the Firestore queue. -// -// Ownership: -// -// - `TimeSlot` is exclusively owned by libdispatch; -// - `ExecutorLibdispatch` contains non-owning pointers to `TimeSlot`s; -// - invariant: if the executor contains a pointer to a `TimeSlot`, it is -// a valid object. It is achieved because when libdispatch invokes -// a `TimeSlot`, it always removes it from the executor before deleting it. -// The reverse is not true: a canceled time slot is removed from the executor, -// but won't be destroyed until its original due time is past. - -class TimeSlot { - public: - TimeSlot(ExecutorLibdispatch* executor, - Executor::Milliseconds delay, - Executor::TaggedOperation&& operation); - - // Returns the operation that was scheduled for this time slot and turns the - // slot into a no-op. - Executor::TaggedOperation Unschedule(); - - bool operator<(const TimeSlot& rhs) const { - return target_time_ < rhs.target_time_; - } - bool operator==(const Executor::Tag tag) const { - return tagged_.tag == tag; - } - - void MarkDone() { - done_ = true; - } - - static void InvokedByLibdispatch(void* const raw_self); - - private: - void Execute(); - void RemoveFromSchedule(); - - using TimePoint = std::chrono::time_point; - - ExecutorLibdispatch* const executor_; - const TimePoint target_time_; // Used for sorting - Executor::TaggedOperation tagged_; - - // True if the operation has either been run or canceled. - // - // Note on thread-safety: because the precondition is that all member - // functions of this class are executed on the dispatch queue, no - // synchronization is required for `done_`. - bool done_ = false; -}; - -TimeSlot::TimeSlot(ExecutorLibdispatch* const executor, - const Executor::Milliseconds delay, - Executor::TaggedOperation&& operation) - : executor_{executor}, - target_time_{std::chrono::time_point_cast( - std::chrono::system_clock::now()) + - delay}, - tagged_{std::move(operation)} { -} - -Executor::TaggedOperation TimeSlot::Unschedule() { - if (!done_) { - RemoveFromSchedule(); - } - return std::move(tagged_); -} - -void TimeSlot::InvokedByLibdispatch(void* const raw_self) { - auto const self = static_cast(raw_self); - self->Execute(); - delete self; -} - -void TimeSlot::Execute() { - if (done_) { - // `done_` might mean that the executor is already destroyed, so don't call - // `RemoveFromSchedule`. - return; - } - - RemoveFromSchedule(); - - FIREBASE_ASSERT_MESSAGE(tagged_.operation, - "TimeSlot contains an invalid function object"); - tagged_.operation(); -} - -void TimeSlot::RemoveFromSchedule() { - executor_->RemoveFromSchedule(this); -} - -// ExecutorLibdispatch - -ExecutorLibdispatch::ExecutorLibdispatch(const dispatch_queue_t dispatch_queue) - : dispatch_queue_{dispatch_queue} { -} -ExecutorLibdispatch::ExecutorLibdispatch() - : ExecutorLibdispatch{dispatch_queue_create("com.google.firebase.firestore", - DISPATCH_QUEUE_SERIAL)} { -} - -ExecutorLibdispatch::~ExecutorLibdispatch() { - // Turn any operations that might still be in the queue into no-ops, lest - // they try to access `ExecutorLibdispatch` after it gets destroyed. Because - // the queue is serial, by the time libdispatch gets to the newly-enqueued - // work, the pending operations that might have been in progress would have - // already finished. - RunSynchronized(this, [this] { - for (auto slot : schedule_) { - slot->MarkDone(); - } - }); -} - -bool ExecutorLibdispatch::IsCurrentExecutor() const { - return GetCurrentQueueLabel().data() == GetTargetQueueLabel().data(); -} -std::string ExecutorLibdispatch::CurrentExecutorName() const { - return GetCurrentQueueLabel().data(); -} -std::string ExecutorLibdispatch::Name() const { - return GetTargetQueueLabel().data(); -} - -void ExecutorLibdispatch::Execute(Operation&& operation) { - DispatchAsync(dispatch_queue(), std::move(operation)); -} -void ExecutorLibdispatch::ExecuteBlocking(Operation&& operation) { - DispatchSync(dispatch_queue(), std::move(operation)); -} - -DelayedOperation ExecutorLibdispatch::Schedule(const Milliseconds delay, - TaggedOperation&& operation) { - namespace chr = std::chrono; - const dispatch_time_t delay_ns = dispatch_time( - DISPATCH_TIME_NOW, chr::duration_cast(delay).count()); - - // Ownership is fully transferred to libdispatch -- because it's impossible - // to truly cancel work after it's been dispatched, libdispatch is - // guaranteed to outlive the executor, and it's possible for work to be - // invoked by libdispatch after the executor is destroyed. Executor only - // stores an observer pointer to the operation. - - auto const time_slot = new TimeSlot{this, delay, std::move(operation)}; - dispatch_after_f(delay_ns, dispatch_queue(), time_slot, - TimeSlot::InvokedByLibdispatch); - RunSynchronized(this, [this, time_slot] { schedule_.push_back(time_slot); }); - return DelayedOperation{[this, time_slot] { - // `time_slot` might be destroyed by the time cancellation function runs. - // Therefore, don't access any methods on `time_slot`, only use it as - // a handle to remove from `schedule_`. - RemoveFromSchedule(time_slot); - }}; -} - -void ExecutorLibdispatch::RemoveFromSchedule(const TimeSlot* const to_remove) { - RunSynchronized(this, [this, to_remove] { - const auto found = std::find_if( - schedule_.begin(), schedule_.end(), - [to_remove](const TimeSlot* op) { return op == to_remove; }); - // It's possible for the operation to be missing if libdispatch gets to run - // it after it was force-run, for example. - if (found != schedule_.end()) { - (*found)->MarkDone(); - schedule_.erase(found); - } - }); -} - -// GetLabel functions are guaranteed to never return a "null" string_view -// (i.e. data() != nullptr). -absl::string_view ExecutorLibdispatch::GetCurrentQueueLabel() const { - // Note: dispatch_queue_get_label may return nullptr if the queue wasn't - // initialized with a label. - return StringViewFromDispatchLabel( - dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)); -} - -absl::string_view ExecutorLibdispatch::GetTargetQueueLabel() const { - return StringViewFromDispatchLabel( - dispatch_queue_get_label(dispatch_queue())); -} - -// Test-only methods - -bool ExecutorLibdispatch::IsScheduled(const Tag tag) const { - bool result = false; - RunSynchronized(this, [this, tag, &result] { - result = std::find_if(schedule_.begin(), schedule_.end(), - [&tag](const TimeSlot* const operation) { - return *operation == tag; - }) != schedule_.end(); - }); - return result; -} - -absl::optional -ExecutorLibdispatch::PopFromSchedule() { - absl::optional result; - - RunSynchronized(this, [this, &result] { - if (schedule_.empty()) { - return; - } - // Sorting upon each call to `PopFromSchedule` is inefficient, which is - // consciously ignored because this function is only ever called from tests. - std::sort( - schedule_.begin(), schedule_.end(), - [](const TimeSlot* lhs, const TimeSlot* rhs) { return *lhs < *rhs; }); - const auto nearest = schedule_.begin(); - result = (*nearest)->Unschedule(); - }); - - return result; -} - -} // namespace internal -} // namespace util -} // namespace firestore -} // namespace firebase diff --git a/Firestore/core/src/firebase/firestore/util/executor_libdispatch.h b/Firestore/core/src/firebase/firestore/util/executor_libdispatch.h index b32dbff..85c34f8 100644 --- a/Firestore/core/src/firebase/firestore/util/executor_libdispatch.h +++ b/Firestore/core/src/firebase/firestore/util/executor_libdispatch.h @@ -17,7 +17,6 @@ #ifndef FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_EXECUTOR_LIBDISPATCH_H_ #define FIRESTORE_CORE_SRC_FIREBASE_FIRESTORE_UTIL_EXECUTOR_LIBDISPATCH_H_ -#include #include // NOLINT(build/c++11) #include #include @@ -30,6 +29,13 @@ #include "Firestore/core/src/firebase/firestore/util/firebase_assert.h" #include "absl/strings/string_view.h" +#if !defined(__OBJC__) +// `dispatch_queue_t` gets defined to different types when compiled in C++ or +// Objective-C mode. Source files including this header should all be compiled +// in the same mode to avoid linker errors. +#error "This header only supports Objective-C++ (see comment for more info)." +#endif // !defined(__OBJC__) + namespace firebase { namespace firestore { namespace util { @@ -78,7 +84,7 @@ class ExecutorLibdispatch : public Executor { absl::string_view GetCurrentQueueLabel() const; absl::string_view GetTargetQueueLabel() const; - std::atomic dispatch_queue_; + dispatch_queue_t dispatch_queue_; // Stores non-owned pointers to `TimeSlot`s. // Invariant: if a `TimeSlot` is in `schedule_`, it's a valid pointer. std::vector schedule_; diff --git a/Firestore/core/src/firebase/firestore/util/executor_libdispatch.mm b/Firestore/core/src/firebase/firestore/util/executor_libdispatch.mm new file mode 100644 index 0000000..b40f0dd --- /dev/null +++ b/Firestore/core/src/firebase/firestore/util/executor_libdispatch.mm @@ -0,0 +1,296 @@ +/* + * Copyright 2018 Google + * + * 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 "Firestore/core/src/firebase/firestore/util/executor_libdispatch.h" + +namespace firebase { +namespace firestore { +namespace util { +namespace internal { + +namespace { + +absl::string_view StringViewFromDispatchLabel(const char* const label) { + // Make sure string_view's data is not null, because it's used for logging. + return label ? absl::string_view{label} : absl::string_view{""}; +} + +void RunSynchronized(const ExecutorLibdispatch* const executor, + std::function&& work) { + if (executor->IsCurrentExecutor()) { + work(); + } else { + DispatchSync(executor->dispatch_queue(), std::move(work)); + } +} + +} // namespace + +void DispatchAsync(const dispatch_queue_t queue, std::function&& work) { + // Dynamically allocate the function to make sure the object is valid by the + // time libdispatch gets to it. + const auto wrap = new std::function{std::move(work)}; + + dispatch_async_f(queue, wrap, [](void* const raw_work) { + const auto unwrap = static_cast*>(raw_work); + (*unwrap)(); + delete unwrap; + }); +} + +void DispatchSync(const dispatch_queue_t queue, Executor::Operation work) { + // Unlike dispatch_async_f, dispatch_sync_f blocks until the work passed to it + // is done, so passing a reference to a local variable is okay. + dispatch_sync_f(queue, &work, [](void* const raw_work) { + const auto unwrap = static_cast(raw_work); + (*unwrap)(); + }); +} + +// Represents a "busy" time slot on the schedule. +// +// Since libdispatch doesn't provide a way to cancel a scheduled operation, once +// a slot is created, it will always stay in the schedule until the time is +// past. Consequently, it is more useful to think of a time slot than +// a particular scheduled operation -- by the time the slot comes, operation may +// or may not be there (imagine getting to a meeting and finding out it's been +// canceled). +// +// Precondition: all member functions, including the constructor, are *only* +// invoked on the Firestore queue. +// +// Ownership: +// +// - `TimeSlot` is exclusively owned by libdispatch; +// - `ExecutorLibdispatch` contains non-owning pointers to `TimeSlot`s; +// - invariant: if the executor contains a pointer to a `TimeSlot`, it is +// a valid object. It is achieved because when libdispatch invokes +// a `TimeSlot`, it always removes it from the executor before deleting it. +// The reverse is not true: a canceled time slot is removed from the executor, +// but won't be destroyed until its original due time is past. + +class TimeSlot { + public: + TimeSlot(ExecutorLibdispatch* executor, + Executor::Milliseconds delay, + Executor::TaggedOperation&& operation); + + // Returns the operation that was scheduled for this time slot and turns the + // slot into a no-op. + Executor::TaggedOperation Unschedule(); + + bool operator<(const TimeSlot& rhs) const { + return target_time_ < rhs.target_time_; + } + bool operator==(const Executor::Tag tag) const { + return tagged_.tag == tag; + } + + void MarkDone() { + done_ = true; + } + + static void InvokedByLibdispatch(void* const raw_self); + + private: + void Execute(); + void RemoveFromSchedule(); + + using TimePoint = std::chrono::time_point; + + ExecutorLibdispatch* const executor_; + const TimePoint target_time_; // Used for sorting + Executor::TaggedOperation tagged_; + + // True if the operation has either been run or canceled. + // + // Note on thread-safety: because the precondition is that all member + // functions of this class are executed on the dispatch queue, no + // synchronization is required for `done_`. + bool done_ = false; +}; + +TimeSlot::TimeSlot(ExecutorLibdispatch* const executor, + const Executor::Milliseconds delay, + Executor::TaggedOperation&& operation) + : executor_{executor}, + target_time_{std::chrono::time_point_cast( + std::chrono::system_clock::now()) + + delay}, + tagged_{std::move(operation)} { +} + +Executor::TaggedOperation TimeSlot::Unschedule() { + if (!done_) { + RemoveFromSchedule(); + } + return std::move(tagged_); +} + +void TimeSlot::InvokedByLibdispatch(void* const raw_self) { + auto const self = static_cast(raw_self); + self->Execute(); + delete self; +} + +void TimeSlot::Execute() { + if (done_) { + // `done_` might mean that the executor is already destroyed, so don't call + // `RemoveFromSchedule`. + return; + } + + RemoveFromSchedule(); + + FIREBASE_ASSERT_MESSAGE(tagged_.operation, + "TimeSlot contains an invalid function object"); + tagged_.operation(); +} + +void TimeSlot::RemoveFromSchedule() { + executor_->RemoveFromSchedule(this); +} + +// ExecutorLibdispatch + +ExecutorLibdispatch::ExecutorLibdispatch(const dispatch_queue_t dispatch_queue) + : dispatch_queue_{dispatch_queue} { +} +ExecutorLibdispatch::ExecutorLibdispatch() + : ExecutorLibdispatch{dispatch_queue_create("com.google.firebase.firestore", + DISPATCH_QUEUE_SERIAL)} { +} + +ExecutorLibdispatch::~ExecutorLibdispatch() { + // Turn any operations that might still be in the queue into no-ops, lest + // they try to access `ExecutorLibdispatch` after it gets destroyed. Because + // the queue is serial, by the time libdispatch gets to the newly-enqueued + // work, the pending operations that might have been in progress would have + // already finished. + RunSynchronized(this, [this] { + for (auto slot : schedule_) { + slot->MarkDone(); + } + }); +} + +bool ExecutorLibdispatch::IsCurrentExecutor() const { + return GetCurrentQueueLabel().data() == GetTargetQueueLabel().data(); +} +std::string ExecutorLibdispatch::CurrentExecutorName() const { + return GetCurrentQueueLabel().data(); +} +std::string ExecutorLibdispatch::Name() const { + return GetTargetQueueLabel().data(); +} + +void ExecutorLibdispatch::Execute(Operation&& operation) { + DispatchAsync(dispatch_queue(), std::move(operation)); +} +void ExecutorLibdispatch::ExecuteBlocking(Operation&& operation) { + DispatchSync(dispatch_queue(), std::move(operation)); +} + +DelayedOperation ExecutorLibdispatch::Schedule(const Milliseconds delay, + TaggedOperation&& operation) { + namespace chr = std::chrono; + const dispatch_time_t delay_ns = dispatch_time( + DISPATCH_TIME_NOW, chr::duration_cast(delay).count()); + + // Ownership is fully transferred to libdispatch -- because it's impossible + // to truly cancel work after it's been dispatched, libdispatch is + // guaranteed to outlive the executor, and it's possible for work to be + // invoked by libdispatch after the executor is destroyed. Executor only + // stores an observer pointer to the operation. + + auto const time_slot = new TimeSlot{this, delay, std::move(operation)}; + dispatch_after_f(delay_ns, dispatch_queue(), time_slot, + TimeSlot::InvokedByLibdispatch); + RunSynchronized(this, [this, time_slot] { schedule_.push_back(time_slot); }); + return DelayedOperation{[this, time_slot] { + // `time_slot` might be destroyed by the time cancellation function runs. + // Therefore, don't access any methods on `time_slot`, only use it as + // a handle to remove from `schedule_`. + RemoveFromSchedule(time_slot); + }}; +} + +void ExecutorLibdispatch::RemoveFromSchedule(const TimeSlot* const to_remove) { + RunSynchronized(this, [this, to_remove] { + const auto found = std::find_if( + schedule_.begin(), schedule_.end(), + [to_remove](const TimeSlot* op) { return op == to_remove; }); + // It's possible for the operation to be missing if libdispatch gets to run + // it after it was force-run, for example. + if (found != schedule_.end()) { + (*found)->MarkDone(); + schedule_.erase(found); + } + }); +} + +// GetLabel functions are guaranteed to never return a "null" string_view +// (i.e. data() != nullptr). +absl::string_view ExecutorLibdispatch::GetCurrentQueueLabel() const { + // Note: dispatch_queue_get_label may return nullptr if the queue wasn't + // initialized with a label. + return StringViewFromDispatchLabel( + dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)); +} + +absl::string_view ExecutorLibdispatch::GetTargetQueueLabel() const { + return StringViewFromDispatchLabel( + dispatch_queue_get_label(dispatch_queue())); +} + +// Test-only methods + +bool ExecutorLibdispatch::IsScheduled(const Tag tag) const { + bool result = false; + RunSynchronized(this, [this, tag, &result] { + result = std::find_if(schedule_.begin(), schedule_.end(), + [&tag](const TimeSlot* const operation) { + return *operation == tag; + }) != schedule_.end(); + }); + return result; +} + +absl::optional +ExecutorLibdispatch::PopFromSchedule() { + absl::optional result; + + RunSynchronized(this, [this, &result] { + if (schedule_.empty()) { + return; + } + // Sorting upon each call to `PopFromSchedule` is inefficient, which is + // consciously ignored because this function is only ever called from tests. + std::sort( + schedule_.begin(), schedule_.end(), + [](const TimeSlot* lhs, const TimeSlot* rhs) { return *lhs < *rhs; }); + const auto nearest = schedule_.begin(); + result = (*nearest)->Unschedule(); + }); + + return result; +} + +} // namespace internal +} // namespace util +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/util/CMakeLists.txt b/Firestore/core/test/firebase/firestore/util/CMakeLists.txt index 2e1e2f9..ea80ea2 100644 --- a/Firestore/core/test/firebase/firestore/util/CMakeLists.txt +++ b/Firestore/core/test/firebase/firestore/util/CMakeLists.txt @@ -80,7 +80,7 @@ if(HAVE_LIBDISPATCH) SOURCES executor_test.h executor_test.cc - executor_libdispatch_test.cc + executor_libdispatch_test.mm async_tests_util.h DEPENDS firebase_firestore_util_executor_libdispatch @@ -94,7 +94,7 @@ cc_test( SOURCES async_queue_test.h async_queue_test.cc - async_queue_test_std.cc + async_queue_std_test.cc async_tests_util.h DEPENDS firebase_firestore_util_executor_std @@ -107,7 +107,7 @@ if(HAVE_LIBDISPATCH) SOURCES async_queue_test.h async_queue_test.cc - async_queue_test_libdispatch.cc + async_queue_libdispatch_test.mm async_tests_util.h DEPENDS firebase_firestore_util_executor_libdispatch diff --git a/Firestore/core/test/firebase/firestore/util/async_queue_libdispatch_test.mm b/Firestore/core/test/firebase/firestore/util/async_queue_libdispatch_test.mm new file mode 100644 index 0000000..5452266 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/util/async_queue_libdispatch_test.mm @@ -0,0 +1,87 @@ +/* + * Copyright 2018 Google + * + * 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 "Firestore/core/test/firebase/firestore/util/async_queue_test.h" + +#include + +#include "Firestore/core/src/firebase/firestore/util/executor_libdispatch.h" +#include "absl/memory/memory.h" +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace util { + +namespace { + +dispatch_queue_t CreateDispatchQueue() { + return dispatch_queue_create("AsyncQueueTests", DISPATCH_QUEUE_SERIAL); +} + +std::unique_ptr CreateExecutorFromQueue( + const dispatch_queue_t queue) { + return absl::make_unique(queue); +} + +std::unique_ptr CreateExecutorLibdispatch() { + return CreateExecutorFromQueue(CreateDispatchQueue()); +} + +} // namespace + +INSTANTIATE_TEST_CASE_P(AsyncQueueLibdispatch, + AsyncQueueTest, + ::testing::Values(CreateExecutorLibdispatch)); + +class AsyncQueueTestLibdispatchOnly : public TestWithTimeoutMixin, + public ::testing::Test { + public: + AsyncQueueTestLibdispatchOnly() + : underlying_queue{CreateDispatchQueue()}, + queue{CreateExecutorFromQueue(underlying_queue)} { + } + + dispatch_queue_t underlying_queue; + AsyncQueue queue; +}; + +// Additional tests to see how libdispatch-based version of `AsyncQueue` +// interacts with raw usage of libdispatch. + +TEST_F(AsyncQueueTestLibdispatchOnly, SameQueueIsAllowedForUnownedActions) { + internal::DispatchAsync(underlying_queue, [this] { + queue.Enqueue([this] { signal_finished(); }); + }); + EXPECT_TRUE(WaitForTestToFinish()); +} + +TEST_F(AsyncQueueTestLibdispatchOnly, + VerifyIsCurrentQueueRequiresOperationInProgress) { + internal::DispatchSync(underlying_queue, [this] { + EXPECT_ANY_THROW(queue.VerifyIsCurrentQueue()); + }); +} + +TEST_F(AsyncQueueTestLibdispatchOnly, + VerifyIsCurrentQueueRequiresBeingCalledAsync) { + ASSERT_NE(underlying_queue, dispatch_get_main_queue()); + EXPECT_ANY_THROW(queue.VerifyIsCurrentQueue()); +} + +} // namespace util +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/util/async_queue_std_test.cc b/Firestore/core/test/firebase/firestore/util/async_queue_std_test.cc new file mode 100644 index 0000000..9e69ad0 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/util/async_queue_std_test.cc @@ -0,0 +1,41 @@ +/* + * Copyright 2018 Google + * + * 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 "Firestore/core/test/firebase/firestore/util/async_queue_test.h" + +#include "Firestore/core/src/firebase/firestore/util/executor_std.h" + +#include "absl/memory/memory.h" +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace util { + +namespace { + +std::unique_ptr ExecutorFactory() { + return absl::make_unique(); +} + +} // namespace + +INSTANTIATE_TEST_CASE_P(AsyncQueueStd, + AsyncQueueTest, + ::testing::Values(ExecutorFactory)); +} // namespace util +} // namespace firestore +} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/util/async_queue_test_libdispatch.cc b/Firestore/core/test/firebase/firestore/util/async_queue_test_libdispatch.cc deleted file mode 100644 index b4b9c63..0000000 --- a/Firestore/core/test/firebase/firestore/util/async_queue_test_libdispatch.cc +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2018 Google - * - * 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 "Firestore/core/test/firebase/firestore/util/async_queue_test.h" - -#include "Firestore/core/src/firebase/firestore/util/executor_libdispatch.h" - -#include "absl/memory/memory.h" -#include "gtest/gtest.h" - -namespace firebase { -namespace firestore { -namespace util { - -namespace { - -dispatch_queue_t CreateDispatchQueue() { - return dispatch_queue_create("AsyncQueueTests", DISPATCH_QUEUE_SERIAL); -} - -std::unique_ptr CreateExecutorFromQueue( - const dispatch_queue_t queue) { - return absl::make_unique(queue); -} - -std::unique_ptr CreateExecutorLibdispatch() { - return CreateExecutorFromQueue(CreateDispatchQueue()); -} - -} // namespace - -INSTANTIATE_TEST_CASE_P(AsyncQueueLibdispatch, - AsyncQueueTest, - ::testing::Values(CreateExecutorLibdispatch)); - -class AsyncQueueTestLibdispatchOnly : public TestWithTimeoutMixin, - public ::testing::Test { - public: - AsyncQueueTestLibdispatchOnly() - : underlying_queue{CreateDispatchQueue()}, - queue{CreateExecutorFromQueue(underlying_queue)} { - } - - dispatch_queue_t underlying_queue; - AsyncQueue queue; -}; - -// Additional tests to see how libdispatch-based version of `AsyncQueue` -// interacts with raw usage of libdispatch. - -TEST_F(AsyncQueueTestLibdispatchOnly, SameQueueIsAllowedForUnownedActions) { - internal::DispatchAsync(underlying_queue, [this] { - queue.Enqueue([this] { signal_finished(); }); - }); - EXPECT_TRUE(WaitForTestToFinish()); -} - -TEST_F(AsyncQueueTestLibdispatchOnly, - VerifyIsCurrentQueueRequiresOperationInProgress) { - internal::DispatchSync(underlying_queue, [this] { - EXPECT_ANY_THROW(queue.VerifyIsCurrentQueue()); - }); -} - -TEST_F(AsyncQueueTestLibdispatchOnly, - VerifyIsCurrentQueueRequiresBeingCalledAsync) { - ASSERT_NE(underlying_queue, dispatch_get_main_queue()); - EXPECT_ANY_THROW(queue.VerifyIsCurrentQueue()); -} - -} // namespace util -} // namespace firestore -} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/util/async_queue_test_std.cc b/Firestore/core/test/firebase/firestore/util/async_queue_test_std.cc deleted file mode 100644 index 9e69ad0..0000000 --- a/Firestore/core/test/firebase/firestore/util/async_queue_test_std.cc +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2018 Google - * - * 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 "Firestore/core/test/firebase/firestore/util/async_queue_test.h" - -#include "Firestore/core/src/firebase/firestore/util/executor_std.h" - -#include "absl/memory/memory.h" -#include "gtest/gtest.h" - -namespace firebase { -namespace firestore { -namespace util { - -namespace { - -std::unique_ptr ExecutorFactory() { - return absl::make_unique(); -} - -} // namespace - -INSTANTIATE_TEST_CASE_P(AsyncQueueStd, - AsyncQueueTest, - ::testing::Values(ExecutorFactory)); -} // namespace util -} // namespace firestore -} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/util/executor_libdispatch_test.cc b/Firestore/core/test/firebase/firestore/util/executor_libdispatch_test.cc deleted file mode 100644 index 0167c83..0000000 --- a/Firestore/core/test/firebase/firestore/util/executor_libdispatch_test.cc +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2018 Google - * - * 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 "Firestore/core/test/firebase/firestore/util/executor_test.h" - -#include - -#include "Firestore/core/src/firebase/firestore/util/executor_libdispatch.h" -#include "absl/memory/memory.h" -#include "gtest/gtest.h" - -namespace firebase { -namespace firestore { -namespace util { - -namespace { - -std::unique_ptr ExecutorFactory() { - return absl::make_unique(); -} - -} // namespace - -INSTANTIATE_TEST_CASE_P(ExecutorTestLibdispatch, - ExecutorTest, - ::testing::Values(ExecutorFactory)); - -} // namespace util -} // namespace firestore -} // namespace firebase diff --git a/Firestore/core/test/firebase/firestore/util/executor_libdispatch_test.mm b/Firestore/core/test/firebase/firestore/util/executor_libdispatch_test.mm new file mode 100644 index 0000000..0167c83 --- /dev/null +++ b/Firestore/core/test/firebase/firestore/util/executor_libdispatch_test.mm @@ -0,0 +1,43 @@ +/* + * Copyright 2018 Google + * + * 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 "Firestore/core/test/firebase/firestore/util/executor_test.h" + +#include + +#include "Firestore/core/src/firebase/firestore/util/executor_libdispatch.h" +#include "absl/memory/memory.h" +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace util { + +namespace { + +std::unique_ptr ExecutorFactory() { + return absl::make_unique(); +} + +} // namespace + +INSTANTIATE_TEST_CASE_P(ExecutorTestLibdispatch, + ExecutorTest, + ::testing::Values(ExecutorFactory)); + +} // namespace util +} // namespace firestore +} // namespace firebase -- cgit v1.2.3