aboutsummaryrefslogtreecommitdiffhomepage
path: root/Firestore/core/src/firebase/firestore/util/executor_libdispatch.cc
diff options
context:
space:
mode:
authorGravatar Konstantin Varlamov <var-const@users.noreply.github.com>2018-05-08 19:37:20 -0400
committerGravatar GitHub <noreply@github.com>2018-05-08 19:37:20 -0400
commitdbcd179788a4cce4e9e3ae545e736148438c5c6f (patch)
tree1870fa7f448dfeff0ed6d696cd1feb4a60b5e45d /Firestore/core/src/firebase/firestore/util/executor_libdispatch.cc
parent8409f21830f1282a39c4b7888972011f43d2644a (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.cc296
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