diff options
author | Konstantin Varlamov <var-const@users.noreply.github.com> | 2018-05-08 19:37:20 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-05-08 19:37:20 -0400 |
commit | dbcd179788a4cce4e9e3ae545e736148438c5c6f (patch) | |
tree | 1870fa7f448dfeff0ed6d696cd1feb4a60b5e45d /Firestore/core/src/firebase/firestore/util/executor_libdispatch.cc | |
parent | 8409f21830f1282a39c4b7888972011f43d2644a (diff) |
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.
Diffstat (limited to 'Firestore/core/src/firebase/firestore/util/executor_libdispatch.cc')
-rw-r--r-- | Firestore/core/src/firebase/firestore/util/executor_libdispatch.cc | 296 |
1 files changed, 0 insertions, 296 deletions
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<void()>&& work) { - if (executor->IsCurrentExecutor()) { - work(); - } else { - DispatchSync(executor->dispatch_queue(), std::move(work)); - } -} - -} // namespace - -void DispatchAsync(const dispatch_queue_t queue, std::function<void()>&& 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<void()>{std::move(work)}; - - dispatch_async_f(queue, wrap, [](void* const raw_work) { - const auto unwrap = static_cast<std::function<void()>*>(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<const Executor::Operation*>(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<std::chrono::system_clock, - Executor::Milliseconds>; - - 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<Executor::Milliseconds>( - 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<TimeSlot*>(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<chr::nanoseconds>(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<Executor::TaggedOperation> -ExecutorLibdispatch::PopFromSchedule() { - absl::optional<Executor::TaggedOperation> 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 |