/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * 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. */ #pragma once #include #include #include #include #include #include #include #include #include namespace folly { namespace observer_detail { #define DEFINE_HAS_MEMBER_FUNC(Member) \ template > \ struct Has_##Member##T : std::false_type {}; \ template \ struct Has_##Member##T().Member())>> \ : std::true_type {}; \ template \ constexpr bool Has_##Member##T_v = Has_##Member##T::value; DEFINE_HAS_MEMBER_FUNC(getName) class ObserverManager; /** * Core stores the current version of the object held by Observer. It also keeps * all dependencies and dependents of the Observer. */ class Core : public std::enable_shared_from_this { public: using Ptr = std::shared_ptr; using WeakPtr = std::weak_ptr; struct CreatorContext { // type info for the creator function const std::type_info* typeInfo; // type info for the return type of the creator function const std::type_info* invokeResultTypeInfo; std::string name; template static CreatorContext create(const F& creator) { CreatorContext context; context.typeInfo = &typeid(F); context.invokeResultTypeInfo = &typeid(std::invoke_result_t); if constexpr (Has_getNameT_v) { context.name = creator.getName(); } return context; } }; /** * Blocks until creator is successfully run by ObserverManager */ static Ptr create( folly::Function()> creator, CreatorContext creatorContext); /** * View of the observed object as well as its version and created time */ struct VersionedData { using TimePoint = std::chrono::system_clock::time_point; VersionedData() {} VersionedData(std::shared_ptr dat, size_t ver, TimePoint timeC) : data(std::move(dat)), version(ver), timeCreated(timeC) {} std::shared_ptr data; size_t version{0}; TimePoint timeCreated; }; /** * Gets current view of the observed object. * This is safe to call from any thread. If this is called from other Observer * functor then that Observer is marked as dependent on current Observer. */ VersionedData getData(); /** * Gets the version of the observed object. */ size_t getVersion() const { return version_; } /** * Get the last version at which the observed object was actually changed. */ size_t getVersionLastChange() { return versionLastChange_; } /** * Check if the observed object needs to be re-computed. Returns the version * of last change. * * This should be only called from ObserverManager thread. */ size_t refresh(size_t version); /** * Force the next call to refresh to unconditionally re-compute the observed * object, even if dependencies didn't change. */ void setForceRefresh(); const CreatorContext& getCreatorContext() const { return creatorContext_; } ~Core(); private: Core( folly::Function()> creator, CreatorContext creatorContext); void addDependent(Core::WeakPtr dependent); void maybeRemoveStaleDependents(); struct Dependents { size_t numPotentiallyExpiredDependents{0}; std::vector deps; }; using Dependencies = std::unordered_set; folly::Synchronized dependents_; folly::Synchronized dependencies_; std::atomic version_{0}; std::atomic versionLastChange_{0}; folly::Synchronized data_; folly::Function()> creator_; CreatorContext creatorContext_; mutable SharedMutex refreshMutex_; bool forceRefresh_{false}; public: Dependencies getSnapshotOfDependencies() const { return dependencies_.copy(); } }; } // namespace observer_detail } // namespace folly