--- old/src/hotspot/share/memory/universe.cpp 2019-06-28 15:24:39.000000000 -0700 +++ new/src/hotspot/share/memory/universe.cpp 2019-06-28 15:24:38.000000000 -0700 @@ -75,6 +75,7 @@ #include "runtime/timerTrace.hpp" #include "runtime/vmOperations.hpp" #include "services/memoryService.hpp" +#include "services/threadTable.hpp" #include "utilities/align.hpp" #include "utilities/copy.hpp" #include "utilities/debug.hpp" @@ -724,7 +725,7 @@ } ResolvedMethodTable::create_table(); - + ThreadTable::create_table(); return JNI_OK; } --- old/src/hotspot/share/runtime/serviceThread.cpp 2019-06-28 15:24:40.000000000 -0700 +++ new/src/hotspot/share/runtime/serviceThread.cpp 2019-06-28 15:24:40.000000000 -0700 @@ -41,6 +41,7 @@ #include "services/diagnosticFramework.hpp" #include "services/gcNotifier.hpp" #include "services/lowMemoryDetector.hpp" +#include "services/threadTable.hpp" ServiceThread* ServiceThread::_instance = NULL; @@ -124,6 +125,7 @@ bool stringtable_work = false; bool symboltable_work = false; bool resolved_method_table_work = false; + bool thread_table_work = false; bool protection_domain_table_work = false; bool oopstorage_work = false; bool oopstorages_cleanup[oopstorage_count] = {}; // Zero (false) initialize. @@ -151,6 +153,7 @@ (stringtable_work = StringTable::has_work()) | (symboltable_work = SymbolTable::has_work()) | (resolved_method_table_work = ResolvedMethodTable::has_work()) | + (thread_table_work = ThreadTable::has_work()) | (protection_domain_table_work = SystemDictionary::pd_cache_table()->has_work()) | (oopstorage_work = needs_oopstorage_cleanup(oopstorages, oopstorages_cleanup, @@ -194,6 +197,10 @@ ResolvedMethodTable::do_concurrent_work(jt); } + if (thread_table_work) { + ThreadTable::do_concurrent_work(jt); + } + if (protection_domain_table_work) { SystemDictionary::pd_cache_table()->unlink(); } --- old/src/hotspot/share/runtime/threadSMR.cpp 2019-06-28 15:24:41.000000000 -0700 +++ new/src/hotspot/share/runtime/threadSMR.cpp 2019-06-28 15:24:41.000000000 -0700 @@ -26,10 +26,12 @@ #include "logging/logStream.hpp" #include "memory/allocation.inline.hpp" #include "runtime/jniHandles.inline.hpp" +#include "runtime/sharedRuntime.hpp" #include "runtime/thread.inline.hpp" #include "runtime/threadSMR.inline.hpp" #include "runtime/vmOperations.hpp" #include "services/threadService.hpp" +#include "services/threadTable.hpp" #include "utilities/copy.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/ostream.hpp" @@ -607,21 +609,34 @@ return -1; } +#define PMIMORDIAL_JAVA_TID 1 + JavaThread* ThreadsList::find_JavaThread_from_java_tid(jlong java_tid) const { - for (uint i = 0; i < length(); i++) { - JavaThread* thread = thread_at(i); - oop tobj = thread->threadObj(); + JavaThread* java_thread = ThreadTable::find_thread(java_tid); + if (java_thread == NULL && java_tid == PMIMORDIAL_JAVA_TID) { + // ThreadsSMRSupport::add_thread() is not called for the primordial + // thread. Thus, we find this thread with a linear search and add it + // to the thread table. + for (uint i = 0; i < length(); i++) { + JavaThread* thread = thread_at(i); + if (is_valid_java_thread(java_tid,thread)) { + ThreadTable::add_thread(java_tid, thread); + return thread; + } + } + } else if (java_thread != NULL && is_valid_java_thread(java_tid, java_thread)) { + return java_thread; + } + return NULL; +} +bool ThreadsList::is_valid_java_thread(jlong java_tid, JavaThread* java_thread) { + oop tobj = java_thread->threadObj(); // Ignore the thread if it hasn't run yet, has exited // or is starting to exit. - if (tobj != NULL && !thread->is_exiting() && - java_tid == java_lang_Thread::thread_id(tobj)) { - // found a match - return thread; - } - } - return NULL; + return (tobj != NULL && !java_thread->is_exiting() && + java_tid == java_lang_Thread::thread_id(tobj)); } - + void ThreadsList::inc_nested_handle_cnt() { // The increment needs to be MO_SEQ_CST so that the reference counter // update is seen before the subsequent hazard ptr update. @@ -742,6 +757,8 @@ ThreadsList *old_list = xchg_java_thread_list(new_list); free_list(old_list); + jlong tid = SharedRuntime::get_java_tid(thread); + ThreadTable::add_thread(tid, thread); } // set_delete_notify() and clear_delete_notify() are called @@ -909,6 +926,8 @@ } void ThreadsSMRSupport::remove_thread(JavaThread *thread) { + jlong tid = SharedRuntime::get_java_tid(thread); + ThreadTable::remove_thread(tid); ThreadsList *new_list = ThreadsList::remove_thread(ThreadsSMRSupport::get_java_thread_list(), thread); if (EnableThreadSMRStatistics) { ThreadsSMRSupport::inc_java_thread_list_alloc_cnt(); --- old/src/hotspot/share/runtime/threadSMR.hpp 2019-06-28 15:24:42.000000000 -0700 +++ new/src/hotspot/share/runtime/threadSMR.hpp 2019-06-28 15:24:42.000000000 -0700 @@ -179,7 +179,9 @@ static ThreadsList* add_thread(ThreadsList* list, JavaThread* java_thread); static ThreadsList* remove_thread(ThreadsList* list, JavaThread* java_thread); - + + // Used by find_JavaThread_from_java_tid + static bool is_valid_java_thread(jlong java_tid, JavaThread* jt); public: ThreadsList(int entries); ~ThreadsList(); --- /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); +} --- /dev/null 2019-06-28 15:24:44.000000000 -0700 +++ new/src/hotspot/share/services/threadTable.hpp 2019-06-28 15:24:44.000000000 -0700 @@ -0,0 +1,63 @@ + +/* +* 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. +* +*/ + +#ifndef SHARE_SERVICES_THREADTABLE_HPP +#define SHARE_SERVICES_THREADTABLE_HPP + +#include "memory/allocation.hpp" + +class JavaThread; +class ThreadTableConfig; + +class ThreadTable : public AllStatic { + static volatile bool _has_work; + + public: + // Initialization + static void create_table(); + + static size_t table_size(); + + // Lookup and inserts + static JavaThread* find_thread(jlong tid); + static JavaThread* add_thread(jlong tid, JavaThread* thread); + static bool remove_thread(jlong tid); + + // Callbacks + static void item_added(); + static void item_removed(); + + // Growing + static bool has_work() { return _has_work; } + + static void check_concurrent_work(); + static void trigger_concurrent_work(); + static void do_concurrent_work(JavaThread* jt); + + static double get_load_factor(); + static void grow(JavaThread* jt); +}; + +#endif // SHARE_SERVICES_THREADTABLE_HPP