// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_KEYED_SERVICE_CORE_DEPENDENCY_MANAGER_H_
#define COMPONENTS_KEYED_SERVICE_CORE_DEPENDENCY_MANAGER_H_

#include <set>
#include <string>

#include "base/dcheck_is_on.h"
#include "base/memory/raw_ptr.h"
#include "components/keyed_service/core/dependency_graph.h"
#include "components/keyed_service/core/features_buildflags.h"
#include "components/keyed_service/core/keyed_service_export.h"

class KeyedServiceBaseFactory;

namespace user_prefs {
class PrefRegistrySyncable;
}

// DependencyManager manages dependency between KeyedServiceBaseFactory
// broadcasting the context creation and destruction to each factory in
// a safe order based on the stated dependencies.
class KEYED_SERVICE_EXPORT DependencyManager {
 public:
  DependencyManager(const DependencyManager&) = delete;
  DependencyManager& operator=(const DependencyManager&) = delete;

  // Shuts down all keyed services managed by two
  // DependencyManagers (DMs), then destroys them. The order of execution is:
  // - Shutdown services in DM1
  // - Shutdown services in DM2
  // - Destroy services in DM1
  // - Destroy services in DM2
  static void PerformInterlockedTwoPhaseShutdown(
      DependencyManager* dependency_manager1,
      void* context1,
      DependencyManager* dependency_manager2,
      void* context2);

  // Returns the dependency graph for Keyed Services Factory testing purposes.
  DependencyGraph& GetDependencyGraphForTesting();

  // After this function is called, any KeyedServiceFactory trying to register
  // itself will cause a DCHECK. It should have been registered in the
  // appropriate `EnsureBrowserContextKeyedServiceFactoriesBuilt()` function.
  // `registration_function_name` param is used to display the right
  // registration method in the error message.
  void DisallowKeyedServiceFactoryRegistration(
      const std::string& registration_function_name_error_message);

 protected:
  DependencyManager();
  virtual ~DependencyManager();

  // Adds/Removes a component from our list of live components. Removing will
  // also remove live dependency links.
  void AddComponent(KeyedServiceBaseFactory* component);
  void RemoveComponent(KeyedServiceBaseFactory* component);

  // Adds a dependency between two factories.
  void AddEdge(KeyedServiceBaseFactory* depended,
               KeyedServiceBaseFactory* dependee);

  // Registers preferences for all services via |registry|.
  void RegisterPrefsForServices(user_prefs::PrefRegistrySyncable* registry);

  // Called upon creation of |context| to create services that want to be
  // started at the creation of a context and register service-related
  // preferences.
  //
  // To have a KeyedService started when a context is created the method
  // KeyedServiceBaseFactory::ServiceIsCreatedWithContext() must be overridden
  // to return true.
  //
  // If |is_testing_context| then the service will not be started unless the
  // method KeyedServiceBaseFactory::ServiceIsNULLWhileTesting() return false.
  void CreateContextServices(void* context, bool is_testing_context);

  // Called upon destruction of |context| to destroy all services associated
  // with it.
  void DestroyContextServices(void* context);

  // Runtime assertion called as a part of GetServiceForContext() to check if
  // |context| is considered stale. This will NOTREACHED() to avoid a potential
  // use-after-free from services created after context destruction.
  void AssertContextWasntDestroyed(void* context) const;

  // Marks |context| as live (i.e., not stale). This method can be called as a
  // safeguard against |AssertContextWasntDestroyed()| checks going off due to
  // |context| aliasing an instance from a prior construction (i.e., 0xWhatever
  // might be created, be destroyed, and then a new object might be created at
  // 0xWhatever).
  void MarkContextLive(void* context);

  // Marks |context| as dead (i.e., stale). Calls passing |context| to
  // |AssertContextWasntDestroyed()| will flag an error until that context is
  // marked as live again with MarkContextLive().
  void MarkContextDead(void* context);

 private:
  friend class KeyedServiceBaseFactory;

  // An ordered container of pointers to KeyedServiceBaseFactory. The order
  // depends on the operation to perform (initialisation, destruction, ...).
  using OrderedFactories =
      std::vector<raw_ptr<KeyedServiceBaseFactory, VectorExperimental>>;

  // Returns the list of factories in the order they should be initialised.
  OrderedFactories GetConstructionOrder();

  // Returns the list of factories in the order they should be destroyed.
  OrderedFactories GetDestructionOrder();

  // Invokes `ContextShutdown(context)` for all factories in the order
  // specified by `factories`.
  static void ShutdownFactoriesInOrder(void* context,
                                       const OrderedFactories& factories);

  // Invokes `ContextDestroyed(context)` for all factories in the order
  // specified by `factories`.
  static void DestroyFactoriesInOrder(void* context,
                                      const OrderedFactories& factories);

  DependencyGraph dependency_graph_;

  // A list of context objects that have gone through the Shutdown() phase.
  // These pointers are most likely invalid, but we keep track of their
  // locations in memory so we can nicely assert if we're asked to do anything
  // with them.
  std::set<raw_ptr<void, SetExperimental>> dead_context_pointers_;

#if DCHECK_IS_ON()
#if BUILDFLAG(KEYED_SERVICE_HAS_TIGHT_REGISTRATION)
  // Used to count the number of `context` that have been created. This is used
  // to prevent registering KeyedServiceFactories while any context exist, while
  // still allowing to register/unregister factories during unit tests.
  size_t context_created_count_ = 0;
#else
  // Used to record whether any `context` has been created. This is used
  // to prevent registering KeyedServiceFactories after the creation of
  // a context.
  bool any_context_created_ = false;
#endif
#endif

  bool disallow_factory_registration_ = false;
  std::string registration_function_name_error_message_;
};

#endif  // COMPONENTS_KEYED_SERVICE_CORE_DEPENDENCY_MANAGER_H_
