/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/BoundedMPSCQueue.h"
#include <thread>
#include <chrono>
using
namespace mozilla;
struct NativeStack {
void* mPCs[
32];
void* mSPs[
32];
size_t mCount;
size_t mTid;
};
void StackWalkCallback(
void* aPC,
void* aSP, NativeStack* nativeStack) {
nativeStack->mSPs[nativeStack->mCount] = aSP;
nativeStack->mPCs[nativeStack->mCount] = aPC;
nativeStack->mCount++;
}
void FillNativeStack(NativeStack* aStack) {
StackWalkCallback((
void*)
0x1234, (
void*)
0x9876, aStack);
StackWalkCallback((
void*)
0x3456, (
void*)
0x5432, aStack);
StackWalkCallback((
void*)
0x7890, (
void*)
0x1098, aStack);
StackWalkCallback((
void*)
0x1234, (
void*)
0x7654, aStack);
StackWalkCallback((
void*)
0x5678, (
void*)
0x3210, aStack);
StackWalkCallback((
void*)
0x9012, (
void*)
0x9876, aStack);
StackWalkCallback((
void*)
0x1334, (
void*)
0x9786, aStack);
StackWalkCallback((
void*)
0x3546, (
void*)
0x5342, aStack);
StackWalkCallback((
void*)
0x7809, (
void*)
0x0198, aStack);
StackWalkCallback((
void*)
0x4123, (
void*)
0x7645, aStack);
StackWalkCallback((
void*)
0x5768, (
void*)
0x3120, aStack);
StackWalkCallback((
void*)
0x9102, (
void*)
0x9867, aStack);
StackWalkCallback((
void*)
0x1243, (
void*)
0x8976, aStack);
StackWalkCallback((
void*)
0x6345, (
void*)
0x4325, aStack);
StackWalkCallback((
void*)
0x8790, (
void*)
0x1908, aStack);
StackWalkCallback((
void*)
0x134, (
void*)
0x654, aStack);
StackWalkCallback((
void*)
0x567, (
void*)
0x320, aStack);
StackWalkCallback((
void*)
0x901, (
void*)
0x976, aStack);
}
template <size_t Capacity>
void BasicAPITestWithStack(BoundedMPSCQueue<NativeStack, Capacity>& aQueue,
size_t aCap) {
MOZ_RELEASE_ASSERT(aQueue.Capacity() == aCap);
NativeStack s = {.mCount =
0};
FillNativeStack(&s);
MOZ_RELEASE_ASSERT(s.mCount ==
18);
int store = -
1;
for (size_t i =
0; i < aCap; ++i) {
store = aQueue.Send(s);
MOZ_RELEASE_ASSERT(store >
0);
}
int retrieve = -
1;
for (size_t i =
0; i < aCap; ++i) {
NativeStack sr{};
retrieve = aQueue.Recv(&sr);
MOZ_RELEASE_ASSERT(retrieve >
0);
MOZ_RELEASE_ASSERT(&s != &sr);
MOZ_RELEASE_ASSERT(s.mCount == sr.mCount);
for (size_t i =
0; i < s.mCount; ++i) {
MOZ_RELEASE_ASSERT(s.mPCs[i] == sr.mPCs[i]);
MOZ_RELEASE_ASSERT(s.mSPs[i] == sr.mSPs[i]);
}
}
}
template <size_t Capacity>
void BasicAPITestMP(BoundedMPSCQueue<NativeStack, Capacity>& aQueue,
size_t aThreads) {
MOZ_RELEASE_ASSERT(aQueue.Capacity() ==
15);
std::thread consumer([&aQueue, aThreads] {
size_t received =
0;
NativeStack v{};
do {
int deq = aQueue.Recv(&v);
if (deq >
0) {
received++;
}
std::this_thread::sleep_for(std::chrono::microseconds(
10));
}
while (received < aThreads);
});
std::thread producers[aThreads];
for (size_t t =
0; t < aThreads; ++t) {
producers[t] = std::thread([&aQueue, t] {
NativeStack s = {.mCount =
0, .mTid = t};
FillNativeStack(&s);
MOZ_RELEASE_ASSERT(s.mCount ==
18);
int sent =
0;
// wrap in a do { } while () because Send() will return 0 on message being
// dropped so we want to retry
do {
std::this_thread::sleep_for(std::chrono::microseconds(
5));
sent = aQueue.Send(s);
}
while (sent ==
0);
});
}
for (size_t t =
0; t < aThreads; ++t) {
producers[t].join();
}
consumer.join();
}
template <size_t Capacity>
bool testBasicApi() {
BoundedMPSCQueue<NativeStack, Capacity> s;
BasicAPITestWithStack(s, Capacity);
return true;
}
template <size_t... Capacity>
void testBasicApiCapacities() {
[[maybe_unused]] std::initializer_list<
bool> _ = {
testBasicApi<Capacity>()...};
}
int main() {
testBasicApiCapacities<
1,
5,
7,
10,
15>();
{
NativeStack e{};
BoundedMPSCQueue<NativeStack,
2> deq;
// Dequeue with nothing should return 0 and not fail later
int retrieve = deq.Recv(&e);
MOZ_RELEASE_ASSERT(retrieve ==
0);
NativeStack real = {.mCount =
0};
FillNativeStack(&real);
MOZ_RELEASE_ASSERT(real.mCount ==
18);
int store = deq.Send(real);
MOZ_RELEASE_ASSERT(store >
0);
store = deq.Send(real);
MOZ_RELEASE_ASSERT(store >
0);
// should be full we should get 0
store = deq.Send(real);
MOZ_RELEASE_ASSERT(store ==
0);
// try to dequeue
NativeStack e1{};
retrieve = deq.Recv(&e1);
MOZ_RELEASE_ASSERT(retrieve >
0);
MOZ_RELEASE_ASSERT(e1.mCount ==
18);
NativeStack e2{};
retrieve = deq.Recv(&e2);
MOZ_RELEASE_ASSERT(retrieve >
0);
MOZ_RELEASE_ASSERT(e2.mCount ==
18);
retrieve = deq.Recv(&e);
MOZ_RELEASE_ASSERT(retrieve ==
0);
}
size_t nbThreads[] = {
8,
16,
64,
128,
512,
1024};
for (
auto threads : nbThreads) {
BoundedMPSCQueue<NativeStack,
15> s;
BasicAPITestMP(s, threads);
}
return 0;
}