--- /dev/null 2019-06-28 15:24:43.000000000 -0700 +++ new/src/hotspot/share/services/threadTable.cpp 2019-06-28 15:24:43.000000000 -0700 @@ -0,0 +1,221 @@ + +/* +* 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 "runtime/interfaceSupport.inline.hpp" +#include "runtime/timerTrace.hpp" +#include "runtime/thread.hpp" +#include "services/threadTable.hpp" +#include "utilities/concurrentHashTable.inline.hpp" +#include "utilities/concurrentHashTableTasks.inline.hpp" + + +// 2^24 is max size +static const size_t END_SIZE = 24; +// Initial size 256 +static const size_t ThreadTableSizeLog = 8; +// Prefer short chains of avg 2 +static const double PREF_AVG_LIST_LEN = 2.0; + +typedef ConcurrentHashTable ThreadTableHash; + +static ThreadTableHash* _local_table = NULL; + +volatile bool ThreadTable::_has_work = false; + +static volatile size_t _current_size = 0; +static volatile size_t _items_count = 0; + + +class ThreadTableEntry : public CHeapObj { + private: + jlong _tid; + JavaThread* _java_thread; + public: + ThreadTableEntry(jlong tid, JavaThread* java_thread) : + _tid(tid),_java_thread(java_thread) {} + + jlong tid() const { return _tid;} + JavaThread* thread() const {return _java_thread;} +}; + +class ThreadTableConfig : public AllStatic { + public: + typedef ThreadTableEntry* Value; + + static uintx get_hash(Value const& value, bool* is_dead) { + *is_dead = false; + jlong tid = value->tid(); + return primitive_hash(tid); + } + static void* allocate_node(size_t size, Value const& value) { + ThreadTable::item_added(); + return AllocateHeap(size, mtInternal); + } + static void free_node(void* memory, Value const& value) { + delete value; + FreeHeap(memory); + ThreadTable::item_removed(); + } +}; + +void ThreadTable::create_table() { + _current_size = (size_t)1 << ThreadTableSizeLog; + _local_table = new ThreadTableHash(ThreadTableSizeLog, END_SIZE); +} + +void ThreadTable::item_added() { + Atomic::inc(&_items_count); + log_trace(thread, table) ("Thread entry added"); +} + +void ThreadTable::item_removed() { + Atomic::dec(&_items_count); + log_trace(thread, table) ("Thread entry removed"); +} + +double ThreadTable::get_load_factor() { + return (double)_items_count/_current_size; +} + +size_t ThreadTable::table_size() { + return (size_t)1 << _local_table->get_size_log2(Thread::current()); +} + +void ThreadTable::check_concurrent_work() { + if (_has_work) { + return; + } + + double load_factor = get_load_factor(); + // Resize if we have more items than preferred load factor + if ( load_factor > PREF_AVG_LIST_LEN) { + log_debug(thread, table)("Concurrent work triggered, load factor: %g", + load_factor); + trigger_concurrent_work(); + } +} + +void ThreadTable::trigger_concurrent_work() { + MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag); + _has_work = true; + Service_lock->notify_all(); +} + +void ThreadTable::do_concurrent_work(JavaThread* jt) { + _has_work = false; + double load_factor = get_load_factor(); + log_debug(thread, table)("Concurrent work, load factor: %g", load_factor); + if (load_factor > PREF_AVG_LIST_LEN && !_local_table->is_max_size_reached()) { + grow(jt); + } +} + +void ThreadTable::grow(JavaThread* jt) { + ThreadTableHash::GrowTask gt(_local_table); + if (!gt.prepare(jt)) { + return; + } + log_trace(thread, table)("Started to grow"); + { + TraceTime timer("Grow", TRACETIME_LOG(Debug, membername, table, perf)); + while (gt.do_task(jt)) { + gt.pause(jt); + { + ThreadBlockInVM tbivm(jt); + } + gt.cont(jt); + } + } + gt.done(jt); + _current_size = table_size(); + log_info(thread, table)("Grown to size:" SIZE_FORMAT, _current_size); +} + +class ThreadTableLookup : public StackObj { + private: + jlong _tid; + uintx _hash; + public: + ThreadTableLookup(jlong tid) + : _tid(tid), _hash(primitive_hash(tid)) {} + uintx get_hash() const { + return _hash; + } + bool equals(ThreadTableEntry **value, bool* is_dead) { + *is_dead = false; + bool equals = primitive_equals(_tid, (*value)->tid()); + if (!equals) { + return false; + } + return true; + } +}; + +class ThreadGet : public StackObj { + private: + JavaThread* _return; + public: + ThreadGet():_return(NULL) {} + void operator()(ThreadTableEntry** val) { + _return = (*val)->thread(); + } + JavaThread* get_res_thread() { + return _return; + } +}; + +JavaThread* ThreadTable::find_thread(jlong tid) { + Thread* thread = Thread::current(); + ThreadTableLookup lookup(tid); + ThreadGet tg; + _local_table->get(thread, lookup, tg); + return tg.get_res_thread(); +} + + +JavaThread* ThreadTable::add_thread(jlong tid, JavaThread* java_thread) { + Thread* thread = Thread::current(); + ThreadTableLookup lookup(tid); + ThreadGet tg; + while(true) { + if (_local_table->get(thread, lookup, tg)) { + return tg.get_res_thread(); + } + ThreadTableEntry* entry = new ThreadTableEntry(tid,java_thread); + // The hash table takes ownership of the ThreadTableEntry, + // even if it's not inserted. + if (_local_table->insert(thread, lookup, entry)) { + check_concurrent_work(); + return java_thread; + } + } +} + +bool ThreadTable::remove_thread(jlong tid) { + Thread* thread = Thread::current(); + ThreadTableLookup lookup(tid); + return _local_table->remove(thread,lookup); +}