1 2 /* 3 * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * This code is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License version 2 only, as 8 * published by the Free Software Foundation. 9 * 10 * This code is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * version 2 for more details (a copy is included in the LICENSE file that 14 * accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License version 17 * 2 along with this work; if not, write to the Free Software Foundation, 18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 * 24 */ 25 26 #include "precompiled.hpp" 27 #include "runtime/interfaceSupport.inline.hpp" 28 #include "runtime/timerTrace.hpp" 29 #include "runtime/thread.hpp" 30 #include "runtime/threadSMR.hpp" 31 #include "services/threadTable.hpp" 32 #include "utilities/concurrentHashTable.inline.hpp" 33 #include "utilities/concurrentHashTableTasks.inline.hpp" 34 35 36 // 2^24 is max size 37 static const size_t END_SIZE = 24; 38 // Default initial size 256 39 static const size_t DefaultThreadTableSizeLog = 8; 40 // Prefer short chains of avg 2 41 static const double PREF_AVG_LIST_LEN = 2.0; 42 43 typedef ConcurrentHashTable<ThreadTableConfig, mtInternal> ThreadTableHash; 44 45 static ThreadTableHash* volatile _local_table = NULL; 46 47 volatile bool ThreadTable::_is_initialized = false; 48 49 50 static volatile size_t _current_size = 0; 51 static volatile size_t _items_count = 0; 52 53 54 class ThreadTableEntry : public CHeapObj<mtInternal> { 55 private: 56 jlong _tid; 57 JavaThread* _java_thread; 58 public: 59 ThreadTableEntry(jlong tid, JavaThread* java_thread) : 60 _tid(tid),_java_thread(java_thread) {} 61 62 jlong tid() const { return _tid;} 63 JavaThread* thread() const {return _java_thread;} 64 }; 65 66 class ThreadTableConfig : public AllStatic { 67 public: 68 typedef ThreadTableEntry* Value; 69 70 static uintx get_hash(Value const& value, bool* is_dead) { 71 jlong tid = value->tid(); 72 return primitive_hash(tid); 73 } 74 static void* allocate_node(size_t size, Value const& value) { 75 ThreadTable::item_added(); 76 return AllocateHeap(size, mtInternal); 77 } 78 static void free_node(void* memory, Value const& value) { 79 delete value; 80 FreeHeap(memory); 81 ThreadTable::item_removed(); 82 } 83 }; 84 85 static size_t ceil_log2(size_t val) { 86 size_t ret; 87 for (ret = 1; ((size_t)1 << ret) < val; ++ret); 88 return ret; 89 } 90 91 // Lazily creates the table and populates it with the given 92 // thread list 93 void ThreadTable::lazy_initialize(const ThreadsList *threads) { 94 if (!_is_initialized) { 95 { 96 MutexLocker ml(ThreadTableCreate_lock); 97 if (!_is_initialized) { 98 create_table(threads->length()); 99 _is_initialized = true; 100 } 101 } 102 for (uint i = 0; i < threads->length(); i++) { 103 JavaThread* thread = threads->thread_at(i); 104 oop tobj = thread->threadObj(); 105 if (tobj != NULL && !thread->is_exiting()) { 106 jlong java_tid = java_lang_Thread::thread_id(tobj); 107 add_thread(java_tid, thread); 108 } 109 } 110 } 111 } 112 113 void ThreadTable::create_table(size_t size) { 114 assert(_local_table == NULL, "Thread table is already created"); 115 size_t size_log = ceil_log2(size); 116 size_t start_size_log = 117 size_log > DefaultThreadTableSizeLog ? size_log : DefaultThreadTableSizeLog; 118 _current_size = (size_t)1 << start_size_log; 119 _local_table = new ThreadTableHash(start_size_log, END_SIZE); 120 } 121 122 void ThreadTable::item_added() { 123 Atomic::inc(&_items_count); 124 log_trace(thread, table) ("Thread entry added"); 125 } 126 127 void ThreadTable::item_removed() { 128 Atomic::dec(&_items_count); 129 log_trace(thread, table) ("Thread entry removed"); 130 } 131 132 double ThreadTable::get_load_factor() { 133 return ((double)_items_count)/_current_size; 134 } 135 136 size_t ThreadTable::table_size() { 137 return (size_t)1 << _local_table->get_size_log2(Thread::current()); 138 } 139 140 void ThreadTable::grow(JavaThread* jt) { 141 ThreadTableHash::GrowTask gt(_local_table); 142 if (!gt.prepare(jt)) { 143 return; 144 } 145 log_trace(thread, table)("Started to grow"); 146 TraceTime timer("Grow", TRACETIME_LOG(Debug, membername, table, perf)); 147 while (gt.do_task(jt)) { 148 gt.pause(jt); 149 { 150 ThreadBlockInVM tbivm(jt); 151 } 152 gt.cont(jt); 153 } 154 gt.done(jt); 155 _current_size = table_size(); 156 log_info(thread, table)("Grown to size:" SIZE_FORMAT, _current_size); 157 } 158 159 class ThreadTableLookup : public StackObj { 160 private: 161 jlong _tid; 162 uintx _hash; 163 public: 164 ThreadTableLookup(jlong tid) 165 : _tid(tid), _hash(primitive_hash(tid)) {} 166 uintx get_hash() const { 167 return _hash; 168 } 169 bool equals(ThreadTableEntry **value, bool* is_dead) { 170 bool equals = primitive_equals(_tid, (*value)->tid()); 171 if (!equals) { 172 return false; 173 } 174 return true; 175 } 176 }; 177 178 class ThreadGet : public StackObj { 179 private: 180 JavaThread* _return; 181 public: 182 ThreadGet(): _return(NULL) {} 183 void operator()(ThreadTableEntry** val) { 184 _return = (*val)->thread(); 185 } 186 JavaThread* get_res_thread() { 187 return _return; 188 } 189 }; 190 191 void ThreadTable::grow_if_required() { 192 assert(Thread::current()->is_Java_thread(),"Must be Java thread"); 193 assert(_is_initialized, "Thread table is not initialized"); 194 double load_factor = get_load_factor(); 195 log_debug(thread, table)("Concurrent work, load factor: %g", load_factor); 196 if (load_factor > PREF_AVG_LIST_LEN && !_local_table->is_max_size_reached()) { 197 grow(JavaThread::current()); 198 } 199 } 200 201 JavaThread* ThreadTable::add_thread(jlong tid, JavaThread* java_thread) { 202 assert(_is_initialized, "Thread table is not initialized"); 203 Thread* thread = Thread::current(); 204 ThreadTableLookup lookup(tid); 205 ThreadGet tg; 206 while(true) { 207 if (_local_table->get(thread, lookup, tg)) { 208 return tg.get_res_thread(); 209 } 210 ThreadTableEntry* entry = new ThreadTableEntry(tid, java_thread); 211 // The hash table takes ownership of the ThreadTableEntry, 212 // even if it's not inserted. 213 if (_local_table->insert(thread, lookup, entry)) { 214 grow_if_required(); 215 return java_thread; 216 } 217 } 218 } 219 220 JavaThread* ThreadTable::find_thread_by_tid(jlong tid) { 221 assert(_is_initialized, "Thread table is not initialized"); 222 Thread* thread = Thread::current(); 223 ThreadTableLookup lookup(tid); 224 ThreadGet tg; 225 _local_table->get(thread, lookup, tg); 226 return tg.get_res_thread(); 227 } 228 229 bool ThreadTable::remove_thread(jlong tid) { 230 assert(_is_initialized, "Thread table is not initialized"); 231 Thread* thread = Thread::current(); 232 ThreadTableLookup lookup(tid); 233 return _local_table->remove(thread, lookup); 234 }