123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include "UserTypes.h"
- #include <AzCore/std/parallel/containers/lock_free_stack.h>
- #include <AzCore/std/parallel/containers/lock_free_stamped_stack.h>
- #include <AzCore/std/parallel/containers/lock_free_intrusive_stack.h>
- #include <AzCore/std/parallel/containers/lock_free_intrusive_stamped_stack.h>
- #include <AzCore/std/parallel/thread.h>
- #include <AzCore/std/functional.h>
- namespace UnitTest
- {
- using namespace AZStd;
- using namespace UnitTestInternal;
- class LockFreeStack
- : public LeakDetectionFixture
- {
- public:
-
- #ifdef _DEBUG
- static const int NUM_ITERATIONS = 5000;
- #else
- static const int NUM_ITERATIONS = 50000;
- #endif
- template <class S>
- void Push(S* stack)
- {
- for (int i = 1; i <= NUM_ITERATIONS; ++i)
- {
- stack->push(i);
- m_total.fetch_add(i);
- }
- }
- template <class S>
- void Pop(S* stack)
- {
- int numPopped = 0;
- while (numPopped != NUM_ITERATIONS)
- {
- int value = 0;
- if (stack->pop(&value))
- {
- m_total.fetch_sub(value);
- ++numPopped;
- }
- else
- {
- // for some schedulers we need the yield otherwise we will deadlock
- AZStd::this_thread::yield();
- }
- }
- }
- atomic<int> m_total;
- };
- TEST_F(LockFreeStack, LockFreeStack)
- {
- lock_free_stack<int, MyLockFreeAllocator> stack;
- int result = 0;
- AZ_TEST_ASSERT(stack.empty());
- AZ_TEST_ASSERT(!stack.pop(&result));
- stack.push(20);
- AZ_TEST_ASSERT(!stack.empty());
- AZ_TEST_ASSERT(stack.pop(&result));
- AZ_TEST_ASSERT(result == 20);
- AZ_TEST_ASSERT(stack.empty());
- AZ_TEST_ASSERT(!stack.pop(&result));
- stack.push(20);
- stack.push(30);
- AZ_TEST_ASSERT(!stack.empty());
- AZ_TEST_ASSERT(stack.pop(&result));
- AZ_TEST_ASSERT(result == 30);
- AZ_TEST_ASSERT(!stack.empty());
- AZ_TEST_ASSERT(stack.pop(&result));
- AZ_TEST_ASSERT(result == 20);
- AZ_TEST_ASSERT(stack.empty());
- AZ_TEST_ASSERT(!stack.pop(&result));
- {
- m_total = 0;
- AZStd::thread thread0(AZStd::bind(&LockFreeStack::Push<decltype(stack)>, this, &stack));
- AZStd::thread thread1(AZStd::bind(&LockFreeStack::Pop<decltype(stack)>, this, &stack));
- AZStd::thread thread2(AZStd::bind(&LockFreeStack::Push<decltype(stack)>, this, &stack));
- AZStd::thread thread3(AZStd::bind(&LockFreeStack::Pop<decltype(stack)>, this, &stack));
- thread0.join();
- thread1.join();
- thread2.join();
- thread3.join();
- AZ_TEST_ASSERT(m_total == 0);
- AZ_TEST_ASSERT(stack.empty());
- }
- }
- TEST_F(LockFreeStack, LockFreeStampedStack)
- {
- lock_free_stamped_stack<int, MyLockFreeAllocator> stack;
- int result = 0;
- AZ_TEST_ASSERT(stack.empty());
- AZ_TEST_ASSERT(!stack.pop(&result));
- stack.push(20);
- AZ_TEST_ASSERT(!stack.empty());
- AZ_TEST_ASSERT(stack.pop(&result));
- AZ_TEST_ASSERT(result == 20);
- AZ_TEST_ASSERT(stack.empty());
- AZ_TEST_ASSERT(!stack.pop(&result));
- stack.push(20);
- stack.push(30);
- AZ_TEST_ASSERT(!stack.empty());
- AZ_TEST_ASSERT(stack.pop(&result));
- AZ_TEST_ASSERT(result == 30);
- AZ_TEST_ASSERT(!stack.empty());
- AZ_TEST_ASSERT(stack.pop(&result));
- AZ_TEST_ASSERT(result == 20);
- AZ_TEST_ASSERT(stack.empty());
- AZ_TEST_ASSERT(!stack.pop(&result));
- {
- m_total = 0;
- AZStd::thread thread0(AZStd::bind(&LockFreeStack::Push<decltype(stack)>, this, &stack));
- AZStd::thread thread1(AZStd::bind(&LockFreeStack::Pop<decltype(stack)>, this, &stack));
- AZStd::thread thread2(AZStd::bind(&LockFreeStack::Push<decltype(stack)>, this, &stack));
- AZStd::thread thread3(AZStd::bind(&LockFreeStack::Pop<decltype(stack)>, this, &stack));
- thread0.join();
- thread1.join();
- thread2.join();
- thread3.join();
- AZ_TEST_ASSERT(m_total == 0);
- AZ_TEST_ASSERT(stack.empty());
- }
- }
- struct MyStackItem
- : public lock_free_intrusive_stack_node<MyStackItem>
- {
- public:
- MyStackItem() {}
- MyStackItem(int data)
- : m_data(data) {}
- lock_free_intrusive_stack_node<MyStackItem> m_listHook; // Public member hook.
- int m_data; // This is left public only for testing purpose.
- };
-
- class LockFreeIntrusiveStack
- : public LeakDetectionFixture
- {
- public:
- #ifdef _DEBUG
- static const int NUM_ITERATIONS = 5000;
- #else
- static const int NUM_ITERATIONS = 50000;
- #endif
- template <class S>
- void Push(S* stack, vector<MyStackItem>* items)
- {
- for (int i = 0; i < NUM_ITERATIONS; ++i)
- {
- stack->push((*items)[i]);
- m_total.fetch_add((*items)[i].m_data);
- }
- }
- template <class S>
- void Pop(S* stack)
- {
- int numPopped = 0;
- while (numPopped != NUM_ITERATIONS)
- {
- MyStackItem* item = stack->pop();
- if (item)
- {
- m_total.fetch_sub(item->m_data);
- ++numPopped;
- }
- }
- }
- atomic<int> m_total;
- };
- typedef lock_free_intrusive_stack<MyStackItem,
- lock_free_intrusive_stack_base_hook<MyStackItem> > MyIntrusiveStackBase;
- typedef lock_free_intrusive_stack<MyStackItem,
- lock_free_intrusive_stack_member_hook<MyStackItem, & MyStackItem::m_listHook> > MyIntrusiveStackMember;
- typedef lock_free_intrusive_stamped_stack<MyStackItem,
- lock_free_intrusive_stack_base_hook<MyStackItem> > MyIntrusiveStampedStackBase;
- typedef lock_free_intrusive_stamped_stack<MyStackItem,
- lock_free_intrusive_stack_member_hook<MyStackItem, & MyStackItem::m_listHook> > MyIntrusiveStampedStackMember;
- TEST_F(LockFreeIntrusiveStack, MyIntrusiveStackBase)
- {
- MyIntrusiveStackBase stack;
- MyStackItem item1(100);
- MyStackItem item2(200);
- AZ_TEST_ASSERT(stack.empty());
- AZ_TEST_ASSERT(!stack.pop());
- stack.push(item1);
- AZ_TEST_ASSERT(!stack.empty());
- AZ_TEST_ASSERT(stack.pop() == &item1);
- AZ_TEST_ASSERT(stack.empty());
- AZ_TEST_ASSERT(!stack.pop());
- stack.push(item1);
- stack.push(item2);
- AZ_TEST_ASSERT(!stack.empty());
- AZ_TEST_ASSERT(stack.pop() == &item2);
- AZ_TEST_ASSERT(!stack.empty());
- AZ_TEST_ASSERT(stack.pop() == &item1);
- AZ_TEST_ASSERT(stack.empty());
- AZ_TEST_ASSERT(!stack.pop());
- vector<MyStackItem> items;
- items.reserve(NUM_ITERATIONS);
- for (int i = 1; i <= NUM_ITERATIONS; ++i)
- {
- items.push_back(MyStackItem(i));
- }
- {
- m_total = 0;
- AZStd::thread thread0(AZStd::bind(&LockFreeIntrusiveStack::Push<decltype(stack)>, this, &stack, &items));
- AZStd::thread thread1(AZStd::bind(&LockFreeIntrusiveStack::Pop<decltype(stack)>, this, &stack));
- thread0.join();
- thread1.join();
- AZ_TEST_ASSERT(m_total == 0);
- AZ_TEST_ASSERT(stack.empty());
- }
- }
-
- TEST_F(LockFreeIntrusiveStack, MyIntrusiveStackMember)
- {
- MyIntrusiveStackMember stack;
- MyStackItem item1(100);
- MyStackItem item2(200);
- AZ_TEST_ASSERT(stack.empty());
- AZ_TEST_ASSERT(!stack.pop());
- stack.push(item1);
- AZ_TEST_ASSERT(!stack.empty());
- AZ_TEST_ASSERT(stack.pop() == &item1);
- AZ_TEST_ASSERT(stack.empty());
- AZ_TEST_ASSERT(!stack.pop());
- stack.push(item1);
- stack.push(item2);
- AZ_TEST_ASSERT(!stack.empty());
- AZ_TEST_ASSERT(stack.pop() == &item2);
- AZ_TEST_ASSERT(!stack.empty());
- AZ_TEST_ASSERT(stack.pop() == &item1);
- AZ_TEST_ASSERT(stack.empty());
- AZ_TEST_ASSERT(!stack.pop());
- vector<MyStackItem> items;
- items.reserve(NUM_ITERATIONS);
- for (int i = 1; i <= NUM_ITERATIONS; ++i)
- {
- items.push_back(MyStackItem(i));
- }
- {
- m_total = 0;
- AZStd::thread thread0(AZStd::bind(&LockFreeIntrusiveStack::Push<decltype(stack)>, this, &stack, &items));
- AZStd::thread thread1(AZStd::bind(&LockFreeIntrusiveStack::Pop<decltype(stack)>, this, &stack));
- thread0.join();
- thread1.join();
- AZ_TEST_ASSERT(m_total == 0);
- AZ_TEST_ASSERT(stack.empty());
- }
- }
- TEST_F(LockFreeIntrusiveStack, MyIntrusiveStampedStackBase)
- {
- MyIntrusiveStampedStackBase stack;
- MyStackItem item1(100);
- MyStackItem item2(200);
- AZ_TEST_ASSERT(stack.empty());
- AZ_TEST_ASSERT(!stack.pop());
- stack.push(item1);
- AZ_TEST_ASSERT(!stack.empty());
- AZ_TEST_ASSERT(stack.pop() == &item1);
- AZ_TEST_ASSERT(stack.empty());
- AZ_TEST_ASSERT(!stack.pop());
- stack.push(item1);
- stack.push(item2);
- AZ_TEST_ASSERT(!stack.empty());
- AZ_TEST_ASSERT(stack.pop() == &item2);
- AZ_TEST_ASSERT(!stack.empty());
- AZ_TEST_ASSERT(stack.pop() == &item1);
- AZ_TEST_ASSERT(stack.empty());
- AZ_TEST_ASSERT(!stack.pop());
- vector<MyStackItem> items;
- items.reserve(NUM_ITERATIONS);
- for (int i = 1; i <= NUM_ITERATIONS; ++i)
- {
- items.push_back(MyStackItem(i));
- }
- {
- m_total = 0;
- AZStd::thread thread0(AZStd::bind(&LockFreeIntrusiveStack::Push<decltype(stack)>, this, &stack, &items));
- AZStd::thread thread1(AZStd::bind(&LockFreeIntrusiveStack::Pop<decltype(stack)>, this, &stack));
- thread0.join();
- thread1.join();
- AZ_TEST_ASSERT(m_total == 0);
- AZ_TEST_ASSERT(stack.empty());
- }
- }
- TEST_F(LockFreeIntrusiveStack, MyIntrusiveStampedStackMember)
- {
- MyIntrusiveStampedStackMember stack;
- MyStackItem item1(100);
- MyStackItem item2(200);
- AZ_TEST_ASSERT(stack.empty());
- AZ_TEST_ASSERT(!stack.pop());
- stack.push(item1);
- AZ_TEST_ASSERT(!stack.empty());
- AZ_TEST_ASSERT(stack.pop() == &item1);
- AZ_TEST_ASSERT(stack.empty());
- AZ_TEST_ASSERT(!stack.pop());
- stack.push(item1);
- stack.push(item2);
- AZ_TEST_ASSERT(!stack.empty());
- AZ_TEST_ASSERT(stack.pop() == &item2);
- AZ_TEST_ASSERT(!stack.empty());
- AZ_TEST_ASSERT(stack.pop() == &item1);
- AZ_TEST_ASSERT(stack.empty());
- AZ_TEST_ASSERT(!stack.pop());
- vector<MyStackItem> items;
- items.reserve(NUM_ITERATIONS);
- for (int i = 1; i <= NUM_ITERATIONS; ++i)
- {
- items.push_back(MyStackItem(i));
- }
- {
- m_total = 0;
- AZStd::thread thread0(AZStd::bind(&LockFreeIntrusiveStack::Push<decltype(stack)>, this, &stack, &items));
- AZStd::thread thread1(AZStd::bind(&LockFreeIntrusiveStack::Pop<decltype(stack)>, this, &stack));
- thread0.join();
- thread1.join();
- AZ_TEST_ASSERT(m_total == 0);
- AZ_TEST_ASSERT(stack.empty());
- }
- }
- }
|