Commit 29bc878d authored by Andrii Grynenko's avatar Andrii Grynenko Committed by Facebook GitHub Bot

Make cycle detection FATAL instead of throw and disable it in opt mode

Summary: Cycle detection can be very expensive, so it's better to disable it in opt mode. Because of that we need to make sure we catch such cycles in dbg builds, so we have to replace exceptions with LOG(FATAL).

Reviewed By: joshkirstein

Differential Revision: D29367695

fbshipit-source-id: 9c2038eb5b42f98f4ab997f963b6a131b8d26cf9
parent 45666e3d
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <glog/logging.h> #include <glog/logging.h>
#include <folly/Portability.h>
#include <folly/experimental/observer/detail/Core.h> #include <folly/experimental/observer/detail/Core.h>
#include <folly/experimental/observer/detail/GraphCycleDetector.h> #include <folly/experimental/observer/detail/GraphCycleDetector.h>
#include <folly/fibers/FiberManager.h> #include <folly/fibers/FiberManager.h>
...@@ -132,6 +133,9 @@ class ObserverManager { ...@@ -132,6 +133,9 @@ class ObserverManager {
} }
static void markRefreshDependency(const Core& core) { static void markRefreshDependency(const Core& core) {
if (!kIsDebug) {
return;
}
if (!currentDependencies_) { if (!currentDependencies_) {
return; return;
} }
...@@ -140,12 +144,15 @@ class ObserverManager { ...@@ -140,12 +144,15 @@ class ObserverManager {
bool hasCycle = bool hasCycle =
!cycleDetector.addEdge(&currentDependencies_->core, &core); !cycleDetector.addEdge(&currentDependencies_->core, &core);
if (hasCycle) { if (hasCycle) {
throw std::logic_error("Observer cycle detected."); LOG(FATAL) << "Observer cycle detected.";
} }
}); });
} }
static void unmarkRefreshDependency(const Core& core) { static void unmarkRefreshDependency(const Core& core) {
if (!kIsDebug) {
return;
}
if (!currentDependencies_) { if (!currentDependencies_) {
return; return;
} }
......
...@@ -152,61 +152,33 @@ TEST(Observer, NullValue) { ...@@ -152,61 +152,33 @@ TEST(Observer, NullValue) {
} }
TEST(Observer, Cycle) { TEST(Observer, Cycle) {
SimpleObservable<int> observable(0); if (!folly::kIsDebug) {
auto observer = observable.getObserver(); // Cycle detection is only available in debug builds
folly::Optional<Observer<int>> observerB; return;
}
auto observerA = makeObserver([observer, &observerB]() {
auto value = **observer;
if (value == 1) {
**observerB;
}
return value;
});
observerB = makeObserver([observerA]() { return **observerA; });
auto collectObserver = makeObserver([observer, observerA, &observerB]() {
auto value = **observer;
auto valueA = **observerA;
auto valueB = ***observerB;
if (value == 1) {
if (valueA == 0) {
EXPECT_EQ(0, valueB);
} else {
EXPECT_EQ(1, valueA);
EXPECT_EQ(0, valueB);
}
} else if (value == 2) {
EXPECT_EQ(value, valueA);
EXPECT_TRUE(valueB == 0 || valueB == 2);
} else {
EXPECT_EQ(value, valueA);
EXPECT_EQ(value, valueB);
}
return value; EXPECT_DEATH(
}); [] {
SimpleObservable<bool> observable(false);
folly::Optional<Observer<int>> observerB;
folly::Baton<> baton; auto observerA =
auto waitingObserver = makeObserver([collectObserver, &baton]() { makeObserver([observer = observable.getObserver(), &observerB]() {
*collectObserver; if (**observer) {
baton.post(); return ***observerB;
return folly::Unit(); }
}); return 42;
});
baton.reset(); observerB = makeObserver([observerA]() { return **observerA; });
EXPECT_EQ(0, **collectObserver);
for (size_t i = 1; i <= 3; ++i) { EXPECT_EQ(42, **observerA);
observable.setValue(i); EXPECT_EQ(42, ***observerB);
EXPECT_TRUE(baton.try_wait_for(std::chrono::seconds{1})); observable.setValue(true);
baton.reset(); folly::observer_detail::ObserverManager::waitForAllUpdates();
}(),
EXPECT_EQ(i, **collectObserver); "Observer cycle detected.");
}
} }
TEST(Observer, Stress) { TEST(Observer, Stress) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment