Commit 9ce176b1 authored by Maged Michael's avatar Maged Michael Committed by Facebook Github Bot

Dynamic MPMCQueue: Eliminate cases of enqueue indefinite blocking and failure...

Dynamic MPMCQueue: Eliminate cases of enqueue indefinite blocking and failure in the extensible version that impossible under the default pre-allocated version

Summary:
Currently under the extensible version (Dynamic == true), some enqueue operations may block indefinitely or fail (return false) even though such outcomes are impossible under the default (Dynamic == false) pre-allocated version.

This diff eliminates such cases by changing the algorithms for the extensible version. Some of the high-level changes:
- The offset formula for an expansion guarantees that no enqueue operation left behind in a closed array does not have an existing dequeue operation that unblocks it. The old formula was 1 + max(head, tail). The new formula is max(head, current offset) + current capacity.
- Conditional operations validate state after the success of CAS.

Reviewed By: djwatson

Differential Revision: D5701013

fbshipit-source-id: 4917c5b35b7e2a2fddfd2e11fb5aeb478502137c
parent f7ec2efe
This diff is collapsed.
...@@ -748,11 +748,6 @@ void runMtNeverFail(std::vector<int>& nts, int n) { ...@@ -748,11 +748,6 @@ void runMtNeverFail(std::vector<int>& nts, int n) {
} }
} }
// All the never_fail tests are for the non-dynamic version only.
// False positive for dynamic version. Some writeIfNotFull() and
// tryWriteUntil() operations may fail in transient conditions related
// to expansion.
TEST(MPMCQueue, mt_never_fail) { TEST(MPMCQueue, mt_never_fail) {
std::vector<int> nts {1, 3, 100}; std::vector<int> nts {1, 3, 100};
int n = 100000; int n = 100000;
...@@ -765,6 +760,18 @@ TEST(MPMCQueue, mt_never_fail_emulated_futex) { ...@@ -765,6 +760,18 @@ TEST(MPMCQueue, mt_never_fail_emulated_futex) {
runMtNeverFail<EmulatedFutexAtomic>(nts, n); runMtNeverFail<EmulatedFutexAtomic>(nts, n);
} }
TEST(MPMCQueue, mt_never_fail_dynamic) {
std::vector<int> nts{1, 3, 100};
int n = 100000;
runMtNeverFail<std::atomic, true>(nts, n);
}
TEST(MPMCQueue, mt_never_fail_emulated_futex_dynamic) {
std::vector<int> nts{1, 3, 100};
int n = 100000;
runMtNeverFail<EmulatedFutexAtomic, true>(nts, n);
}
template <bool Dynamic = false> template <bool Dynamic = false>
void runMtNeverFailDeterministic(std::vector<int>& nts, int n, long seed) { void runMtNeverFailDeterministic(std::vector<int>& nts, int n, long seed) {
LOG(INFO) << "using seed " << seed; LOG(INFO) << "using seed " << seed;
...@@ -787,6 +794,13 @@ TEST(MPMCQueue, mt_never_fail_deterministic) { ...@@ -787,6 +794,13 @@ TEST(MPMCQueue, mt_never_fail_deterministic) {
runMtNeverFailDeterministic(nts, n, seed); runMtNeverFailDeterministic(nts, n, seed);
} }
TEST(MPMCQueue, mt_never_fail_deterministic_dynamic) {
std::vector<int> nts{3, 10};
long seed = 0; // nowMicro() % 10000;
int n = 1000;
runMtNeverFailDeterministic<true>(nts, n, seed);
}
template <class Clock, template <typename> class Atom, bool Dynamic> template <class Clock, template <typename> class Atom, bool Dynamic>
void runNeverFailUntilThread(int numThreads, void runNeverFailUntilThread(int numThreads,
int n, /*numOps*/ int n, /*numOps*/
...@@ -851,6 +865,12 @@ TEST(MPMCQueue, mt_never_fail_until_system) { ...@@ -851,6 +865,12 @@ TEST(MPMCQueue, mt_never_fail_until_system) {
runMtNeverFailUntilSystem(nts, n); runMtNeverFailUntilSystem(nts, n);
} }
TEST(MPMCQueue, mt_never_fail_until_system_dynamic) {
std::vector<int> nts{1, 3, 100};
int n = 100000;
runMtNeverFailUntilSystem<true>(nts, n);
}
template <bool Dynamic = false> template <bool Dynamic = false>
void runMtNeverFailUntilSteady(std::vector<int>& nts, int n) { void runMtNeverFailUntilSteady(std::vector<int>& nts, int n) {
for (int nt : nts) { for (int nt : nts) {
...@@ -867,6 +887,12 @@ TEST(MPMCQueue, mt_never_fail_until_steady) { ...@@ -867,6 +887,12 @@ TEST(MPMCQueue, mt_never_fail_until_steady) {
runMtNeverFailUntilSteady(nts, n); runMtNeverFailUntilSteady(nts, n);
} }
TEST(MPMCQueue, mt_never_fail_until_steady_dynamic) {
std::vector<int> nts{1, 3, 100};
int n = 100000;
runMtNeverFailUntilSteady<true>(nts, n);
}
enum LifecycleEvent { enum LifecycleEvent {
NOTHING = -1, NOTHING = -1,
DEFAULT_CONSTRUCTOR, DEFAULT_CONSTRUCTOR,
...@@ -1213,7 +1239,7 @@ TEST(MPMCQueue, try_write_until_timeout) { ...@@ -1213,7 +1239,7 @@ TEST(MPMCQueue, try_write_until_timeout) {
testTimeout<false>(queue); testTimeout<false>(queue);
} }
TEST(MPMCQueue, must_fail_try_write_until_dynamic) { TEST(MPMCQueue, try_write_until_timeout_dynamic) {
folly::MPMCQueue<int, std::atomic, true> queue(200, 1, 2); folly::MPMCQueue<int, std::atomic, true> queue(1);
testTimeout<true>(queue); testTimeout<true>(queue);
} }
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