Commit 041d632a authored by Andrii Grynenko's avatar Andrii Grynenko Committed by Facebook GitHub Bot

Extend TSAN symmetric transfer bug workaround to use a thread-local counter

Summary: Many unit tests ended up already depending on the scheduling order. Relax the logic to only reschedule once we reach a high nesting depth.

Reviewed By: iahs

Differential Revision: D23415806

fbshipit-source-id: dde682293ded0061cf331fa5f46e1f6b55928a3e
parent 8dc2d39f
...@@ -48,7 +48,7 @@ class AsyncGeneratorPromise { ...@@ -48,7 +48,7 @@ class AsyncGeneratorPromise {
bool await_ready() noexcept { bool await_ready() noexcept {
return false; return false;
} }
std::experimental::coroutine_handle<> await_suspend( auto await_suspend(
std::experimental::coroutine_handle<AsyncGeneratorPromise> h) noexcept { std::experimental::coroutine_handle<AsyncGeneratorPromise> h) noexcept {
return symmetricTransferMaybeReschedule( return symmetricTransferMaybeReschedule(
h.promise().continuation_, h.promise().clearContext()); h.promise().continuation_, h.promise().clearContext());
......
...@@ -84,7 +84,7 @@ class TaskPromiseBase { ...@@ -84,7 +84,7 @@ class TaskPromiseBase {
} }
template <typename Promise> template <typename Promise>
std::experimental::coroutine_handle<> await_suspend( auto await_suspend(
std::experimental::coroutine_handle<Promise> coro) noexcept { std::experimental::coroutine_handle<Promise> coro) noexcept {
TaskPromiseBase& promise = coro.promise(); TaskPromiseBase& promise = coro.promise();
return symmetricTransferMaybeReschedule( return symmetricTransferMaybeReschedule(
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#pragma once #pragma once
#include <folly/Executor.h> #include <folly/Executor.h>
#include <folly/SingletonThreadLocal.h>
#include <folly/io/async/Request.h> #include <folly/io/async/Request.h>
namespace folly { namespace folly {
...@@ -41,21 +42,46 @@ class UnsafeResumeInlineSemiAwaitable { ...@@ -41,21 +42,46 @@ class UnsafeResumeInlineSemiAwaitable {
Awaitable awaitable_; Awaitable awaitable_;
}; };
class NestingCounter {
public:
struct GuardDestructor {
void operator()(NestingCounter* counter) {
--counter->counter_;
}
};
using Guard = std::unique_ptr<NestingCounter, GuardDestructor>;
Guard guard() {
const size_t maxNestingDepth = 128;
if (counter_ + 1 == maxNestingDepth) {
return nullptr;
}
++counter_;
return Guard{this};
}
private:
size_t counter_{0};
};
using NestingCounterSingleton = SingletonThreadLocal<NestingCounter>;
template <typename Promise> template <typename Promise>
FOLLY_ALWAYS_INLINE folly::conditional_t< FOLLY_ALWAYS_INLINE folly::conditional_t<
kIsSanitizeThread, kIsSanitizeThread,
std::experimental::coroutine_handle<>, void,
std::experimental::coroutine_handle<Promise>> std::experimental::coroutine_handle<Promise>>
symmetricTransferMaybeReschedule( symmetricTransferMaybeReschedule(
std::experimental::coroutine_handle<Promise> ch, std::experimental::coroutine_handle<Promise> ch,
const Executor::KeepAlive<>& ex) { const Executor::KeepAlive<>& ex) {
if constexpr (kIsSanitizeThread) { if constexpr (kIsSanitizeThread) {
if (auto nestingGuard = NestingCounterSingleton::get().guard()) {
ch.resume();
} else {
copy(ex).add([ch, rctx = RequestContext::saveContext()]( copy(ex).add([ch, rctx = RequestContext::saveContext()](
Executor::KeepAlive<>&&) mutable { Executor::KeepAlive<>&&) mutable {
RequestContextScopeGuard guard(std::move(rctx)); RequestContextScopeGuard guard(std::move(rctx));
ch.resume(); ch.resume();
}); });
return std::experimental::noop_coroutine(); }
} else { } else {
return ch; return ch;
} }
......
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