Path: blob/master/test/hotspot/gtest/utilities/test_lockFreeQueue.cpp
41145 views
/*1* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223#include "precompiled.hpp"24#include "memory/allocation.inline.hpp"25#include "runtime/atomic.hpp"26#include "utilities/globalDefinitions.hpp"27#include "utilities/lockFreeQueue.inline.hpp"28#include "utilities/pair.hpp"29#include "threadHelper.inline.hpp"30#include "unittest.hpp"31#include <new>3233class LockFreeQueueTestElement {34typedef LockFreeQueueTestElement Element;3536Element* volatile _entry;37Element* volatile _entry1;38size_t _id;3940static Element* volatile* entry_ptr(Element& e) { return &e._entry; }41static Element* volatile* entry1_ptr(Element& e) { return &e._entry1; }4243public:44class TestQueue: public LockFreeQueue<Element, &entry_ptr> {45public:46Element* pop() {47using Status = LockFreeQueuePopStatus;48while (true) {49Pair<Status, Element*> pop_result = try_pop();50if (pop_result.first == Status::success) {51return pop_result.second;52}53// Retry until success.54}55}56};57class TestQueue1: public LockFreeQueue<Element, &entry1_ptr> {58public:59Element* pop() {60using Status = LockFreeQueuePopStatus;61while (true) {62Pair<Status, Element*> pop_result = try_pop();63if (pop_result.first == Status::success) {64return pop_result.second;65}66// Retry until success.67}68}69};7071LockFreeQueueTestElement(size_t id = 0) : _entry(), _entry1(), _id(id) {}72size_t id() const { return _id; }73void set_id(size_t value) { _id = value; }74Element* next() { return _entry; }75Element* next1() { return _entry1; }76};7778typedef LockFreeQueueTestElement Element;79typedef Element::TestQueue TestQueue;80typedef Element::TestQueue1 TestQueue1;8182static void initialize(Element* elements, size_t size, TestQueue* queue) {83for (size_t i = 0; i < size; ++i) {84elements[i].set_id(i);85}86ASSERT_TRUE(queue->empty());87ASSERT_EQ(0u, queue->length());88ASSERT_TRUE(queue->pop() == NULL);89ASSERT_TRUE(queue->top() == NULL);9091for (size_t id = 0; id < size; ++id) {92ASSERT_EQ(id, queue->length());93Element* e = &elements[id];94ASSERT_EQ(id, e->id());95queue->push(*e);96ASSERT_FALSE(queue->empty());97// top() is always the oldest element.98ASSERT_EQ(&elements[0], queue->top());99}100}101102class LockFreeQueueTestBasics : public ::testing::Test {103public:104LockFreeQueueTestBasics();105106static const size_t nelements = 10;107Element elements[nelements];108TestQueue queue;109};110111const size_t LockFreeQueueTestBasics::nelements;112113LockFreeQueueTestBasics::LockFreeQueueTestBasics() : queue() {114initialize(elements, nelements, &queue);115}116117TEST_F(LockFreeQueueTestBasics, pop) {118for (size_t i = 0; i < nelements; ++i) {119ASSERT_FALSE(queue.empty());120ASSERT_EQ(nelements - i, queue.length());121Element* e = queue.pop();122ASSERT_TRUE(e != NULL);123ASSERT_EQ(&elements[i], e);124ASSERT_EQ(i, e->id());125}126ASSERT_TRUE(queue.empty());127ASSERT_EQ(0u, queue.length());128ASSERT_TRUE(queue.pop() == NULL);129}130131TEST_F(LockFreeQueueTestBasics, append) {132TestQueue other_queue;133ASSERT_TRUE(other_queue.empty());134ASSERT_EQ(0u, other_queue.length());135ASSERT_TRUE(other_queue.top() == NULL);136ASSERT_TRUE(other_queue.pop() == NULL);137138Pair<Element*, Element*> pair = queue.take_all();139other_queue.append(*pair.first, *pair.second);140ASSERT_EQ(nelements, other_queue.length());141ASSERT_TRUE(queue.empty());142ASSERT_EQ(0u, queue.length());143ASSERT_TRUE(queue.pop() == NULL);144ASSERT_TRUE(queue.top() == NULL);145146for (size_t i = 0; i < nelements; ++i) {147ASSERT_EQ(nelements - i, other_queue.length());148Element* e = other_queue.pop();149ASSERT_TRUE(e != NULL);150ASSERT_EQ(&elements[i], e);151ASSERT_EQ(i, e->id());152}153ASSERT_EQ(0u, other_queue.length());154ASSERT_TRUE(other_queue.pop() == NULL);155}156157TEST_F(LockFreeQueueTestBasics, two_queues) {158TestQueue1 queue1;159ASSERT_TRUE(queue1.pop() == NULL);160161for (size_t id = 0; id < nelements; ++id) {162queue1.push(elements[id]);163}164ASSERT_EQ(nelements, queue1.length());165Element* e0 = queue.top();166Element* e1 = queue1.top();167while (true) {168ASSERT_EQ(e0, e1);169if (e0 == NULL) break;170e0 = e0->next();171e1 = e1->next1();172}173174for (size_t i = 0; i < nelements; ++i) {175ASSERT_EQ(nelements - i, queue.length());176ASSERT_EQ(nelements - i, queue1.length());177178Element* e = queue.pop();179ASSERT_TRUE(e != NULL);180ASSERT_EQ(&elements[i], e);181ASSERT_EQ(i, e->id());182183Element* e1 = queue1.pop();184ASSERT_TRUE(e1 != NULL);185ASSERT_EQ(&elements[i], e1);186ASSERT_EQ(i, e1->id());187188ASSERT_EQ(e, e1);189}190ASSERT_EQ(0u, queue.length());191ASSERT_EQ(0u, queue1.length());192ASSERT_TRUE(queue.pop() == NULL);193ASSERT_TRUE(queue1.pop() == NULL);194}195196class LockFreeQueueTestThread : public JavaTestThread {197uint _id;198TestQueue* _from;199TestQueue* _to;200volatile size_t* _processed;201size_t _process_limit;202size_t _local_processed;203volatile bool _ready;204205public:206LockFreeQueueTestThread(Semaphore* post,207uint id,208TestQueue* from,209TestQueue* to,210volatile size_t* processed,211size_t process_limit) :212JavaTestThread(post),213_id(id),214_from(from),215_to(to),216_processed(processed),217_process_limit(process_limit),218_local_processed(0),219_ready(false)220{}221222virtual void main_run() {223Atomic::release_store_fence(&_ready, true);224while (true) {225Element* e = _from->pop();226if (e != NULL) {227_to->push(*e);228Atomic::inc(_processed);229++_local_processed;230} else if (Atomic::load_acquire(_processed) == _process_limit) {231tty->print_cr("thread %u processed " SIZE_FORMAT, _id, _local_processed);232return;233}234}235}236237bool ready() const { return Atomic::load_acquire(&_ready); }238};239240TEST_VM(LockFreeQueueTest, stress) {241Semaphore post;242TestQueue initial_queue;243TestQueue start_queue;244TestQueue middle_queue;245TestQueue final_queue;246volatile size_t stage1_processed = 0;247volatile size_t stage2_processed = 0;248249const size_t nelements = 10000;250Element* elements = NEW_C_HEAP_ARRAY(Element, nelements, mtOther);251for (size_t id = 0; id < nelements; ++id) {252::new (&elements[id]) Element(id);253initial_queue.push(elements[id]);254}255ASSERT_EQ(nelements, initial_queue.length());256257// - stage1 threads pop from start_queue and push to middle_queue.258// - stage2 threads pop from middle_queue and push to final_queue.259// - all threads in a stage count the number of elements processed in260// their corresponding stageN_processed counter.261262const uint stage1_threads = 2;263const uint stage2_threads = 2;264const uint nthreads = stage1_threads + stage2_threads;265LockFreeQueueTestThread* threads[nthreads] = {};266267for (uint i = 0; i < ARRAY_SIZE(threads); ++i) {268TestQueue* from = &start_queue;269TestQueue* to = &middle_queue;270volatile size_t* processed = &stage1_processed;271if (i >= stage1_threads) {272from = &middle_queue;273to = &final_queue;274processed = &stage2_processed;275}276threads[i] =277new LockFreeQueueTestThread(&post, i, from, to, processed, nelements);278threads[i]->doit();279while (!threads[i]->ready()) {} // Wait until ready to start test.280}281282// Transfer elements to start_queue to start test.283Pair<Element*, Element*> pair = initial_queue.take_all();284start_queue.append(*pair.first, *pair.second);285286// Wait for all threads to complete.287for (uint i = 0; i < nthreads; ++i) {288post.wait();289}290291// Verify expected state.292ASSERT_EQ(nelements, stage1_processed);293ASSERT_EQ(nelements, stage2_processed);294ASSERT_EQ(0u, initial_queue.length());295ASSERT_EQ(0u, start_queue.length());296ASSERT_EQ(0u, middle_queue.length());297ASSERT_EQ(nelements, final_queue.length());298while (final_queue.pop() != NULL) {}299300FREE_C_HEAP_ARRAY(Element, elements);301}302303304