--- old/src/hotspot/share/gc/shared/ptrQueue.hpp 2019-01-15 17:45:46.262098459 -0500 +++ new/src/hotspot/share/gc/shared/ptrQueue.hpp 2019-01-15 17:45:45.974083248 -0500 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 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 @@ -25,7 +25,10 @@ #ifndef SHARE_GC_SHARED_PTRQUEUE_HPP #define SHARE_GC_SHARED_PTRQUEUE_HPP +#include "memory/padded.hpp" #include "utilities/align.hpp" +#include "utilities/debug.hpp" +#include "utilities/lockFreeStack.hpp" #include "utilities/sizes.hpp" class Mutex; @@ -214,7 +217,7 @@ class BufferNode { size_t _index; - BufferNode* _next; + BufferNode* volatile _next; void* _buffer[1]; // Pseudo flexible array member. BufferNode() : _index(0), _next(NULL) { } @@ -224,6 +227,8 @@ return offset_of(BufferNode, _buffer); } + static BufferNode* volatile* next_ptr(BufferNode& bn) { return &bn._next; } + AIX_ONLY(public:) // xlC 12 on AIX doesn't implement C++ DR45. // Allocate a new BufferNode with the "buffer" having size elements. static BufferNode* allocate(size_t size); @@ -232,6 +237,8 @@ static void deallocate(BufferNode* node); public: + typedef LockFreeStack Stack; + BufferNode* next() const { return _next; } void set_next(BufferNode* n) { _next = n; } size_t index() const { return _index; } @@ -253,23 +260,52 @@ reinterpret_cast(node) + buffer_offset()); } - // Free-list based allocator. - class Allocator { - size_t _buffer_size; - Mutex* _lock; - BufferNode* _free_list; - volatile size_t _free_count; - - public: - Allocator(size_t buffer_size, Mutex* lock); - ~Allocator(); - - size_t buffer_size() const { return _buffer_size; } - size_t free_count() const; - BufferNode* allocate(); - void release(BufferNode* node); - void reduce_free_list(); - }; + class Allocator; // Free-list based allocator. + class TestSupport; // Unit test support. +}; + +// Allocation is based on a lock-free free list of nodes, linked through +// BufferNode::_next (see BufferNode::Stack). To solve the ABA problem, +// popping a node from the free list is performed within a GlobalCounter +// critical section, and pushing nodes onto the free list is done after +// a GlobalCounter synchronization associated with the nodes to be pushed. +// This is documented behavior so that other parts of the node life-cycle +// can depend on and make use of it too. +class BufferNode::Allocator { + friend class TestSupport; + + // Since we don't expect many instances, and measured >15% speedup + // on stress gtest, padding seems like a good tradeoff here. +#define DECLARE_PADDED_MEMBER(Id, Type, Name) \ + Type Name; DEFINE_PAD_MINUS_SIZE(Id, DEFAULT_CACHE_LINE_SIZE, sizeof(Type)) + + const size_t _buffer_size; + char _name[DEFAULT_CACHE_LINE_SIZE - sizeof(size_t)]; // Use name as padding. + DECLARE_PADDED_MEMBER(1, Stack, _pending_list); + DECLARE_PADDED_MEMBER(2, Stack, _free_list); + DECLARE_PADDED_MEMBER(3, volatile size_t, _pending_count); + DECLARE_PADDED_MEMBER(4, volatile size_t, _free_count); + DECLARE_PADDED_MEMBER(5, volatile bool, _transfer_lock); + +#undef DECLARE_PADDED_MEMBER + + void delete_list(BufferNode* list); + bool try_transfer_pending(); + +public: + Allocator(const char* name, size_t buffer_size); + ~Allocator(); + + const char* name() const { return _name; } + size_t buffer_size() const { return _buffer_size; } + size_t free_count() const; + BufferNode* allocate(); + void release(BufferNode* node); + + // Deallocate some of the available buffers. remove_goal is the target + // number to remove. Returns the number actually deallocated, which may + // be less than the goal if there were fewer available. + size_t reduce_free_list(size_t remove_goal); }; // A PtrQueueSet represents resources common to a set of pointer queues.