Skip to content
Permalink
Browse files
8268290: Improve LockFreeQueue<> utility
Reviewed-by: iwalulya, tschatzl
  • Loading branch information
Kim Barrett committed Jun 22, 2021
1 parent 33c23a1 commit 0c693e2f03b1adef0e946ebc32827ac09192f5f0
Showing 7 changed files with 383 additions and 365 deletions.
@@ -45,8 +45,8 @@
#include "runtime/thread.inline.hpp"
#include "runtime/threadSMR.hpp"
#include "utilities/globalCounter.inline.hpp"
#include "utilities/lockFreeQueue.inline.hpp"
#include "utilities/macros.hpp"
#include "utilities/nonblockingQueue.inline.hpp"
#include "utilities/pair.hpp"
#include "utilities/quickSort.hpp"
#include "utilities/ticks.hpp"
@@ -131,12 +131,12 @@ void G1DirtyCardQueueSet::enqueue_completed_buffer(BufferNode* cbn) {
}

// Thread-safe attempt to remove and return the first buffer from
// the _completed queue, using the LockFreeQueue::try_pop() underneath.
// It has a restriction that it may return NULL when there are objects
// the _completed queue, using the NonblockingQueue::try_pop() underneath.
// It has a limitation that it may return NULL when there are objects
// in the queue if there is a concurrent push/append operation.
BufferNode* G1DirtyCardQueueSet::dequeue_completed_buffer() {
using Status = LockFreeQueuePopStatus;
Thread* current_thread = Thread::current();
BufferNode* result = NULL;
while (true) {
// Use GlobalCounter critical section to avoid ABA problem.
// The release of a buffer to its allocator's free list uses
@@ -147,19 +147,7 @@ BufferNode* G1DirtyCardQueueSet::dequeue_completed_buffer() {
// one CS could defer releasing buffer to the free list for reuse,
// leading to excessive allocations.
GlobalCounter::CriticalSection cs(current_thread);
Pair<Status, BufferNode*> pop_result = _completed.try_pop();
switch (pop_result.first) {
case Status::success:
return pop_result.second;
case Status::operation_in_progress:
// Returning NULL instead retrying, in order to mitigate the
// chance of spinning for a long time. In the case of getting a
// buffer to refine, it is also OK to return NULL when there is
// an interfering concurrent push/append operation.
return NULL;
case Status::lost_race:
break; // Try again.
}
if (_completed.try_pop(&result)) return result;
}
}

@@ -177,8 +165,9 @@ BufferNode* G1DirtyCardQueueSet::get_completed_buffer() {
#ifdef ASSERT
void G1DirtyCardQueueSet::verify_num_cards() const {
size_t actual = 0;
BufferNode* cur = _completed.top();
for ( ; cur != NULL; cur = cur->next()) {
for (BufferNode* cur = _completed.first();
!_completed.is_end(cur);
cur = cur->next()) {
actual += buffer_size() - cur->index();
}
assert(actual == Atomic::load(&_num_cards),
@@ -32,7 +32,7 @@
#include "gc/shared/ptrQueue.hpp"
#include "memory/allocation.hpp"
#include "memory/padded.hpp"
#include "utilities/lockFreeQueue.hpp"
#include "utilities/nonblockingQueue.hpp"

class G1ConcurrentRefineThread;
class G1DirtyCardQueueSet;
@@ -164,9 +164,9 @@ class G1DirtyCardQueueSet: public PtrQueueSet {
volatile size_t _num_cards;
DEFINE_PAD_MINUS_SIZE(2, DEFAULT_CACHE_LINE_SIZE, sizeof(size_t));
// Buffers ready for refinement.
// LockFreeQueue has inner padding of one cache line.
LockFreeQueue<BufferNode, &BufferNode::next_ptr> _completed;
// Add a trailer padding after LockFreeQueue.
// NonblockingQueue has inner padding of one cache line.
NonblockingQueue<BufferNode, &BufferNode::next_ptr> _completed;
// Add a trailer padding after NonblockingQueue.
DEFINE_PAD_MINUS_SIZE(3, DEFAULT_CACHE_LINE_SIZE, sizeof(BufferNode*));
// Buffers for which refinement is temporarily paused.
// PausedBuffers has inner padding, including trailer.

This file was deleted.

This file was deleted.

1 comment on commit 0c693e2

@openjdk-notifier
Copy link

@openjdk-notifier openjdk-notifier bot commented on 0c693e2 Jun 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.