/*
- * Copyright 2016 Facebook, Inc.
+ * Copyright 2017 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#pragma once
#include <folly/experimental/observer/detail/Core.h>
+#include <folly/experimental/observer/detail/GraphCycleDetector.h>
#include <folly/futures/Future.h>
namespace folly {
return future;
}
- static void scheduleRefreshNewVersion(Core::Ptr core) {
- if (core->getVersion() == 0) {
- scheduleRefresh(std::move(core), 1).get();
- return;
- }
-
+ static void scheduleRefreshNewVersion(Core::WeakPtr coreWeak) {
auto instance = getInstance();
if (!instance) {
return;
}
- instance->scheduleNext(std::move(core));
+ instance->scheduleNext(std::move(coreWeak));
+ }
+
+ static void initCore(Core::Ptr core) {
+ DCHECK(core->getVersion() == 0);
+ scheduleRefresh(std::move(core), 1).get();
}
class DependencyRecorder {
public:
- using Dependencies = std::unordered_set<Core::Ptr>;
+ using DependencySet = std::unordered_set<Core::Ptr>;
+ struct Dependencies {
+ explicit Dependencies(const Core& core_) : core(core_) {}
- DependencyRecorder() {
+ DependencySet dependencies;
+ const Core& core;
+ };
+
+ explicit DependencyRecorder(const Core& core) : dependencies_(core) {
DCHECK(inManagerThread());
previousDepedencies_ = currentDependencies_;
DCHECK(inManagerThread());
DCHECK(currentDependencies_);
- currentDependencies_->insert(std::move(dependency));
+ currentDependencies_->dependencies.insert(std::move(dependency));
}
- Dependencies release() {
+ static void markRefreshDependency(const Core& core) {
+ if (!currentDependencies_) {
+ return;
+ }
+
+ if (auto instance = getInstance()) {
+ instance->cycleDetector_.withLock([&](CycleDetector& cycleDetector) {
+ bool hasCycle =
+ !cycleDetector.addEdge(¤tDependencies_->core, &core);
+ if (hasCycle) {
+ throw std::logic_error("Observer cycle detected.");
+ }
+ });
+ }
+ }
+
+ static void unmarkRefreshDependency(const Core& core) {
+ if (!currentDependencies_) {
+ return;
+ }
+
+ if (auto instance = getInstance()) {
+ instance->cycleDetector_.withLock([&](CycleDetector& cycleDetector) {
+ cycleDetector.removeEdge(¤tDependencies_->core, &core);
+ });
+ }
+ }
+
+ DependencySet release() {
DCHECK(currentDependencies_ == &dependencies_);
std::swap(currentDependencies_, previousDepedencies_);
previousDepedencies_ = nullptr;
- return std::move(dependencies_);
+ return std::move(dependencies_.dependencies);
}
~DependencyRecorder() {
- if (previousDepedencies_) {
+ if (currentDependencies_ == &dependencies_) {
release();
}
}
struct Singleton;
void scheduleCurrent(Function<void()>);
- void scheduleNext(Core::Ptr);
+ void scheduleNext(Core::WeakPtr);
class CurrentQueue;
class NextQueue;
*/
SharedMutexReadPriority versionMutex_;
std::atomic<size_t> version_{1};
+
+ using CycleDetector = GraphCycleDetector<const Core*>;
+ folly::Synchronized<CycleDetector, std::mutex> cycleDetector_;
};
}
}