/*
* Copyright ( c ) 2019 , Oracle and / or its affiliates . All rights reserved .
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER .
*
* This code is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 only , as
* published by the Free Software Foundation .
*
* This code is distributed in the hope that it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License
* version 2 for more details ( a copy is included in the LICENSE file that
* accompanied this code ) .
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work ; if not , write to the Free Software Foundation ,
* Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA .
*
* Please contact Oracle , 500 Oracle Parkway , Redwood Shores , CA 94065 USA
* or visit www . oracle . com if you need additional information or have any
* questions .
*/
#include "precompiled.hpp"
#include "memory/allocation.inline.hpp"
#include "runtime/atomic.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/lockFreeStack.hpp"
#include "threadHelper.inline.hpp"
#include "unittest.hpp"
#include <new >
class LockFreeStackTestElement {
typedef LockFreeStackTestElement Element;
Element* volatile _entry;
Element* volatile _entry1;
size_t _id;
static Element* volatile * entry_ptr(Element& e) { return &e._entry; }
static Element* volatile * entry1_ptr(Element& e) { return &e._entry1; }
public :
LockFreeStackTestElement(size_t id = 0 ) : _entry(), _entry1(), _id(id) {}
size_t id() const { return _id; }
void set_id(size_t value) { _id = value; }
typedef LockFreeStack<Element, &entry_ptr> TestStack;
typedef LockFreeStack<Element, &entry1_ptr> TestStack1;
};
typedef LockFreeStackTestElement Element;
typedef Element::TestStack TestStack;
typedef Element::TestStack1 TestStack1;
static void initialize_ids(Element* elements, size_t size) {
for (size_t i = 0 ; i < size; ++i) {
elements[i].set_id(i);
}
}
class LockFreeStackTestBasics : public ::testing::Test {
public :
LockFreeStackTestBasics();
static const size_t nelements = 10 ;
Element elements[nelements];
TestStack stack;
private :
void initialize();
};
const size_t LockFreeStackTestBasics::nelements;
LockFreeStackTestBasics::LockFreeStackTestBasics() : stack() {
initialize_ids(elements, nelements);
initialize();
}
void LockFreeStackTestBasics::initialize() {
ASSERT_TRUE(stack.empty());
ASSERT_EQ(0 u, stack.length());
ASSERT_TRUE(stack.pop() == NULL);
ASSERT_TRUE(stack.top() == NULL);
for (size_t id = 0 ; id < nelements; ++id) {
ASSERT_EQ(id, stack.length());
Element* e = &elements[id];
ASSERT_EQ(id, e->id());
stack.push(*e);
ASSERT_FALSE(stack.empty());
ASSERT_EQ(e, stack.top());
}
}
TEST_F(LockFreeStackTestBasics, push_pop) {
for (size_t i = nelements; i > 0 ; ) {
ASSERT_FALSE(stack.empty());
ASSERT_EQ(i, stack.length());
--i;
Element* e = stack.pop();
ASSERT_TRUE(e != NULL);
ASSERT_EQ(&elements[i], e);
ASSERT_EQ(i, e->id());
}
ASSERT_TRUE(stack.empty());
ASSERT_EQ(0 u, stack.length());
ASSERT_TRUE(stack.pop() == NULL);
}
TEST_F(LockFreeStackTestBasics, prepend_one) {
TestStack other_stack;
ASSERT_TRUE(other_stack.empty());
ASSERT_TRUE(other_stack.pop() == NULL);
ASSERT_EQ(0 u, other_stack.length());
ASSERT_TRUE(other_stack.top() == NULL);
ASSERT_TRUE(other_stack.pop() == NULL);
other_stack.prepend(*stack.pop_all());
ASSERT_EQ(nelements, other_stack.length());
ASSERT_TRUE(stack.empty());
ASSERT_EQ(0 u, stack.length());
ASSERT_TRUE(stack.pop() == NULL);
ASSERT_TRUE(stack.top() == NULL);
for (size_t i = nelements; i > 0 ; ) {
ASSERT_EQ(i, other_stack.length());
--i;
Element* e = other_stack.pop();
ASSERT_TRUE(e != NULL);
ASSERT_EQ(&elements[i], e);
ASSERT_EQ(i, e->id());
}
ASSERT_EQ(0 u, other_stack.length());
ASSERT_TRUE(other_stack.pop() == NULL);
}
TEST_F(LockFreeStackTestBasics, prepend_two) {
TestStack other_stack;
ASSERT_TRUE(other_stack.empty());
ASSERT_EQ(0 u, other_stack.length());
ASSERT_TRUE(other_stack.top() == NULL);
ASSERT_TRUE(other_stack.pop() == NULL);
Element* top = stack.pop_all();
ASSERT_EQ(top, &elements[nelements - 1 ]);
other_stack.prepend(*top, elements[0 ]);
for (size_t i = nelements; i > 0 ; ) {
ASSERT_EQ(i, other_stack.length());
--i;
Element* e = other_stack.pop();
ASSERT_TRUE(e != NULL);
ASSERT_EQ(&elements[i], e);
ASSERT_EQ(i, e->id());
}
ASSERT_EQ(0 u, other_stack.length());
ASSERT_TRUE(other_stack.pop() == NULL);
}
TEST_F(LockFreeStackTestBasics, two_stacks) {
TestStack1 stack1;
ASSERT_TRUE(stack1.pop() == NULL);
for (size_t id = 0 ; id < nelements; ++id) {
stack1.push(elements[id]);
}
ASSERT_EQ(nelements, stack1.length());
Element* e0 = stack.top();
Element* e1 = stack1.top();
while (true ) {
ASSERT_EQ(e0, e1);
if (e0 == NULL) break ;
e0 = stack.next(*e0);
e1 = stack1.next(*e1);
}
for (size_t i = nelements; i > 0 ; ) {
ASSERT_EQ(i, stack.length());
ASSERT_EQ(i, stack1.length());
--i;
Element* e = stack.pop();
ASSERT_TRUE(e != NULL);
ASSERT_EQ(&elements[i], e);
ASSERT_EQ(i, e->id());
Element* e1 = stack1.pop();
ASSERT_TRUE(e1 != NULL);
ASSERT_EQ(&elements[i], e1);
ASSERT_EQ(i, e1->id());
ASSERT_EQ(e, e1);
}
ASSERT_EQ(0 u, stack.length());
ASSERT_EQ(0 u, stack1.length());
ASSERT_TRUE(stack.pop() == NULL);
ASSERT_TRUE(stack1.pop() == NULL);
}
class LockFreeStackTestThread : public JavaTestThread {
uint _id;
TestStack* _from;
TestStack* _to;
volatile size_t* _processed;
size_t _process_limit;
size_t _local_processed;
volatile bool _ready;
public :
LockFreeStackTestThread(Semaphore* post,
uint id,
TestStack* from,
TestStack* to,
volatile size_t* processed,
size_t process_limit) :
JavaTestThread(post),
_id(id),
_from(from),
_to(to),
_processed(processed),
_process_limit(process_limit),
_local_processed(0 ),
_ready(false )
{}
virtual void main_run() {
Atomic::release_store_fence(&_ready, true );
while (true ) {
Element* e = _from->pop();
if (e != NULL) {
_to->push(*e);
Atomic::inc(_processed);
++_local_processed;
} else if (Atomic::load_acquire(_processed) == _process_limit) {
tty->print_cr("thread %u processed " SIZE_FORMAT, _id, _local_processed);
return ;
}
}
}
bool ready() const { return Atomic::load_acquire(&_ready); }
};
TEST_VM(LockFreeStackTest, stress) {
Semaphore post;
TestStack initial_stack;
TestStack start_stack;
TestStack middle_stack;
TestStack final_stack;
volatile size_t stage1_processed = 0 ;
volatile size_t stage2_processed = 0 ;
const size_t nelements = 10000 ;
Element* elements = NEW_C_HEAP_ARRAY(Element, nelements, mtOther);
for (size_t id = 0 ; id < nelements; ++id) {
::new (&elements[id]) Element(id);
initial_stack.push(elements[id]);
}
ASSERT_EQ(nelements, initial_stack.length());
// - stage1 threads pop from start_stack and push to middle_stack.
// - stage2 threads pop from middle_stack and push to final_stack.
// - all threads in a stage count the number of elements processed in
// their corresponding stageN_processed counter.
const uint stage1_threads = 2 ;
const uint stage2_threads = 2 ;
const uint nthreads = stage1_threads + stage2_threads;
LockFreeStackTestThread* threads[nthreads] = {};
for (uint i = 0 ; i < ARRAY_SIZE(threads); ++i) {
TestStack* from = &start_stack;
TestStack* to = &middle_stack;
volatile size_t* processed = &stage1_processed;
if (i >= stage1_threads) {
from = &middle_stack;
to = &final_stack;
processed = &stage2_processed;
}
threads[i] =
new LockFreeStackTestThread(&post, i, from, to, processed, nelements);
threads[i]->doit();
while (!threads[i]->ready()) {} // Wait until ready to start test.
}
// Transfer elements to start_stack to start test.
start_stack.prepend(*initial_stack.pop_all());
// Wait for all threads to complete.
for (uint i = 0 ; i < nthreads; ++i) {
post.wait();
}
// Verify expected state.
ASSERT_EQ(nelements, stage1_processed);
ASSERT_EQ(nelements, stage2_processed);
ASSERT_EQ(0 u, initial_stack.length());
ASSERT_EQ(0 u, start_stack.length());
ASSERT_EQ(0 u, middle_stack.length());
ASSERT_EQ(nelements, final_stack.length());
while (final_stack.pop() != NULL) {}
FREE_C_HEAP_ARRAY(Element, elements);
}
Messung V0.5 in Prozent C=90 H=92 G=90
¤ Dauer der Verarbeitung: 0.5 Sekunden
¤
*© Formatika GbR, Deutschland