X-Git-Url: https://git.mdrn.pl/wl-app.git/blobdiff_plain/53b27422d140022594fc241cca91c3183be57bca..48b2fe9f7c2dc3d9aeaaa6dbfb27c7da4f3235ff:/iOS/Pods/Realm/include/impl/collection_notifier.hpp diff --git a/iOS/Pods/Realm/include/impl/collection_notifier.hpp b/iOS/Pods/Realm/include/impl/collection_notifier.hpp new file mode 100644 index 0000000..e2104e9 --- /dev/null +++ b/iOS/Pods/Realm/include/impl/collection_notifier.hpp @@ -0,0 +1,354 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2016 Realm Inc. +// +// 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. +// +//////////////////////////////////////////////////////////////////////////// + +#ifndef REALM_BACKGROUND_COLLECTION_HPP +#define REALM_BACKGROUND_COLLECTION_HPP + +#include "impl/collection_change_builder.hpp" + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace realm { +class Realm; +class SharedGroup; +class Table; + +namespace _impl { +class RealmCoordinator; + +struct ListChangeInfo { + size_t table_ndx; + size_t row_ndx; + size_t col_ndx; + CollectionChangeBuilder* changes; +}; + +struct TransactionChangeInfo { + std::vector table_modifications_needed; + std::vector table_moves_needed; + std::vector lists; + std::vector tables; + std::vector> column_indices; + std::vector table_indices; + bool track_all; + bool schema_changed; +}; + +class DeepChangeChecker { +public: + struct OutgoingLink { + size_t col_ndx; + bool is_list; + }; + struct RelatedTable { + size_t table_ndx; + std::vector links; + }; + + DeepChangeChecker(TransactionChangeInfo const& info, Table const& root_table, + std::vector const& related_tables); + + bool operator()(size_t row_ndx); + + // Recursively add `table` and all tables it links to to `out`, along with + // information about the links from them + static void find_related_tables(std::vector& out, Table const& table); + +private: + TransactionChangeInfo const& m_info; + Table const& m_root_table; + const size_t m_root_table_ndx; + IndexSet const* const m_root_modifications; + std::vector m_not_modified; + std::vector const& m_related_tables; + + struct Path { + size_t table; + size_t row; + size_t col; + bool depth_exceeded; + }; + std::array m_current_path; + + bool check_row(Table const& table, size_t row_ndx, size_t depth = 0); + bool check_outgoing_links(size_t table_ndx, Table const& table, + size_t row_ndx, size_t depth = 0); +}; + +// A base class for a notifier that keeps a collection up to date and/or +// generates detailed change notifications on a background thread. This manages +// most of the lifetime-management issues related to sharing an object between +// the worker thread and the collection on the target thread, along with the +// thread-safe callback collection. +class CollectionNotifier { +public: + CollectionNotifier(std::shared_ptr); + virtual ~CollectionNotifier(); + + // ------------------------------------------------------------------------ + // Public API for the collections using this to get notifications: + + // Stop receiving notifications from this background worker + // This must be called in the destructor of the collection + void unregister() noexcept; + + // Add a callback to be called each time the collection changes + // This can only be called from the target collection's thread + // Returns a token which can be passed to remove_callback() + uint64_t add_callback(CollectionChangeCallback callback); + // Remove a previously added token. The token is no longer valid after + // calling this function and must not be used again. This function can be + // called from any thread. + void remove_callback(uint64_t token); + + void suppress_next_notification(uint64_t token); + + // ------------------------------------------------------------------------ + // API for RealmCoordinator to manage running things and calling callbacks + + bool is_for_realm(Realm&) const noexcept; + Realm* get_realm() const noexcept { return m_realm.get(); } + + // Get the SharedGroup version which this collection can attach to (if it's + // in handover mode), or can deliver to (if it's been handed over to the BG worker alredad) + // precondition: RealmCoordinator::m_notifier_mutex is locked + VersionID version() const noexcept { return m_sg_version; } + + // Release references to all core types + // This is called on the worker thread to ensure that non-thread-safe things + // can be destroyed on the correct thread, even if the last reference to the + // CollectionNotifier is released on a different thread + virtual void release_data() noexcept = 0; + + // Prepare to deliver the new collection and call callbacks. + // Returns whether or not it has anything to deliver. + // precondition: RealmCoordinator::m_notifier_mutex is locked + bool package_for_delivery(); + + // Deliver the new state to the target collection using the given SharedGroup + // precondition: RealmCoordinator::m_notifier_mutex is unlocked + virtual void deliver(SharedGroup&) { } + + // Pass the given error to all registered callbacks, then remove them + // precondition: RealmCoordinator::m_notifier_mutex is unlocked + void deliver_error(std::exception_ptr); + + // Call each of the given callbacks with the changesets prepared by package_for_delivery() + // precondition: RealmCoordinator::m_notifier_mutex is unlocked + void before_advance(); + void after_advance(); + + bool is_alive() const noexcept; + + // precondition: RealmCoordinator::m_notifier_mutex is locked *or* is called on worker thread + bool has_run() const noexcept { return m_has_run; } + + // Attach the handed-over query to `sg`. Must not be already attached to a SharedGroup. + // precondition: RealmCoordinator::m_notifier_mutex is locked + void attach_to(SharedGroup& sg); + // Create a new query handover object and stop using the previously attached + // SharedGroup + // precondition: RealmCoordinator::m_notifier_mutex is locked + void detach(); + + // Set `info` as the new ChangeInfo that will be populated by the next + // transaction advance, and register all required information in it + // precondition: RealmCoordinator::m_notifier_mutex is locked + void add_required_change_info(TransactionChangeInfo& info); + + // precondition: RealmCoordinator::m_notifier_mutex is unlocked + virtual void run() = 0; + + // precondition: RealmCoordinator::m_notifier_mutex is locked + void prepare_handover(); + + template + class Handle; + + bool have_callbacks() const noexcept { return m_have_callbacks; } +protected: + void add_changes(CollectionChangeBuilder change); + void set_table(Table const& table); + std::unique_lock lock_target(); + SharedGroup& source_shared_group(); + + std::function get_modification_checker(TransactionChangeInfo const&, Table const&); + +private: + virtual void do_attach_to(SharedGroup&) = 0; + virtual void do_detach_from(SharedGroup&) = 0; + virtual void do_prepare_handover(SharedGroup&) = 0; + virtual bool do_add_required_change_info(TransactionChangeInfo&) = 0; + virtual bool prepare_to_deliver() { return true; } + + mutable std::mutex m_realm_mutex; + std::shared_ptr m_realm; + + VersionID m_sg_version; + SharedGroup* m_sg = nullptr; + + bool m_has_run = false; + bool m_error = false; + std::vector m_related_tables; + + struct Callback { + CollectionChangeCallback fn; + CollectionChangeBuilder accumulated_changes; + CollectionChangeSet changes_to_deliver; + uint64_t token; + bool initial_delivered; + bool skip_next; + }; + + // Currently registered callbacks and a mutex which must always be held + // while doing anything with them or m_callback_index + std::mutex m_callback_mutex; + std::vector m_callbacks; + + // Cached value for if m_callbacks is empty, needed to avoid deadlocks in + // run() due to lock-order inversion between m_callback_mutex and m_target_mutex + // It's okay if this value is stale as at worst it'll result in us doing + // some extra work. + std::atomic m_have_callbacks = {false}; + + // Iteration variable for looping over callbacks + // remove_callback() updates this when needed + size_t m_callback_index = -1; + // The number of callbacks which were present when the notifier was packaged + // for delivery which are still present. + // Updated by packaged_for_delivery and removd_callback(), and used in + // for_each_callback() to avoid calling callbacks registered during delivery. + size_t m_callback_count = -1; + + uint64_t m_next_token = 0; + + template + void for_each_callback(Fn&& fn); + + std::vector::iterator find_callback(uint64_t token); +}; + +// A smart pointer to a CollectionNotifier that unregisters the notifier when +// the pointer is destroyed. Movable. Copying will produce a null Handle. +template +class CollectionNotifier::Handle : public std::shared_ptr { +public: + using std::shared_ptr::shared_ptr; + + Handle() = default; + ~Handle() { reset(); } + + // Copying a Handle produces a null Handle. + Handle(const Handle&) : Handle() { } + Handle& operator=(const Handle& other) + { + if (this != &other) { + reset(); + } + return *this; + } + + Handle(Handle&&) = default; + Handle& operator=(Handle&& other) + { + reset(); + std::shared_ptr::operator=(std::move(other)); + return *this; + } + + Handle& operator=(std::shared_ptr&& other) + { + reset(); + std::shared_ptr::operator=(std::move(other)); + return *this; + } + + void reset() + { + if (*this) { + this->get()->unregister(); + std::shared_ptr::reset(); + } + } +}; + +// A package of CollectionNotifiers for a single Realm instance which is passed +// around to the various places which need to actually trigger the notifications +class NotifierPackage { +public: + NotifierPackage() = default; + NotifierPackage(std::exception_ptr error, + std::vector> notifiers, + RealmCoordinator* coordinator); + + explicit operator bool() { return !m_notifiers.empty(); } + + // Get the version which this package can deliver into, or VersionID{} if + // it has not yet been packaged + util::Optional version() { return m_version; } + + // Package the notifiers for delivery, blocking if they aren't ready for + // the given version. + // No-op if called multiple times + void package_and_wait(util::Optional target_version); + + // Send the before-change notifications + void before_advance(); + // Deliver the payload associated with the contained notifiers and/or the error + void deliver(SharedGroup& sg); + // Send the after-change notifications + void after_advance(); + + void add_notifier(std::shared_ptr notifier); + +private: + util::Optional m_version; + std::vector> m_notifiers; + + RealmCoordinator* m_coordinator = nullptr; + std::exception_ptr m_error; +}; + +// Find which column of the row in the table contains the given container. +// +// LinkViews and Subtables know what row of their parent they're in, but not +// what column, so we have to just check each one. +template +size_t find_container_column(Table& table, size_t row_ndx, T const& expected, int type, U (Table::*getter)(size_t, size_t)) +{ + for (size_t i = 0, count = table.get_column_count(); i != count; ++i) { + if (table.get_column_type(i) == type && (table.*getter)(i, row_ndx) == expected) { + return i; + } + } + REALM_UNREACHABLE(); +} + + +} // namespace _impl +} // namespace realm + +#endif /* REALM_BACKGROUND_COLLECTION_HPP */