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 "services/threadTable.hpp"
  31 #include "utilities/concurrentHashTable.inline.hpp"
  32 #include "utilities/concurrentHashTableTasks.inline.hpp"
  33 
  34 
  35 // 2^24 is max size
  36 static const size_t END_SIZE = 24;
  37 // Initial size 256
  38 static const size_t ThreadTableSizeLog = 8;
  39 // Prefer short chains of avg 2
  40 static const double PREF_AVG_LIST_LEN = 2.0;
  41 
  42 typedef ConcurrentHashTable<ThreadTableConfig, mtInternal> ThreadTableHash;
  43 
  44 static ThreadTableHash* _local_table = NULL;
  45 
  46 volatile bool ThreadTable::_has_work = false;
  47 
  48 static volatile size_t _current_size = 0;
  49 static volatile size_t _items_count = 0;
  50 
  51 
  52 class ThreadTableEntry : public CHeapObj<mtInternal> {
  53   private:
  54     jlong _tid;
  55     JavaThread* _java_thread;
  56   public:
  57     ThreadTableEntry(jlong tid, JavaThread* java_thread) :
  58     _tid(tid),_java_thread(java_thread) {}
  59 
  60     jlong tid() const { return _tid;}
  61     JavaThread* thread() const {return _java_thread;}
  62 };
  63 
  64 class ThreadTableConfig : public AllStatic {
  65   public:
  66     typedef ThreadTableEntry* Value;
  67 
  68     static uintx get_hash(Value const& value, bool* is_dead) {
  69         *is_dead = false;
  70         jlong tid = value->tid();
  71         return primitive_hash(tid);
  72     }
  73     static void* allocate_node(size_t size, Value const& value) {
  74         ThreadTable::item_added();
  75         return AllocateHeap(size, mtInternal);
  76     }
  77     static void free_node(void* memory, Value const& value) {
  78         delete value;
  79         FreeHeap(memory);
  80         ThreadTable::item_removed();
  81     }
  82 };
  83 
  84 void ThreadTable::create_table() {
  85     _current_size = (size_t)1 << ThreadTableSizeLog;
  86     _local_table = new ThreadTableHash(ThreadTableSizeLog, END_SIZE);
  87 }
  88 
  89 void ThreadTable::item_added() {
  90     Atomic::inc(&_items_count);
  91     log_trace(thread, table) ("Thread entry added");
  92 }
  93 
  94 void ThreadTable::item_removed() {
  95     Atomic::dec(&_items_count);
  96     log_trace(thread, table) ("Thread entry removed");
  97 }
  98 
  99 double ThreadTable::get_load_factor() {
 100     return (double)_items_count/_current_size;
 101 }
 102 
 103 size_t ThreadTable::table_size() {
 104     return (size_t)1 << _local_table->get_size_log2(Thread::current());
 105 }
 106 
 107 void ThreadTable::check_concurrent_work() {
 108     if (_has_work) {
 109         return;
 110     }
 111     
 112     double load_factor = get_load_factor();
 113     // Resize if we have more items than preferred load factor
 114     if ( load_factor > PREF_AVG_LIST_LEN) {
 115         log_debug(thread, table)("Concurrent work triggered, load factor: %g",
 116                                      load_factor);
 117         trigger_concurrent_work();
 118     }
 119 }
 120 
 121 void ThreadTable::trigger_concurrent_work() {
 122     MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag);
 123     _has_work = true;
 124     Service_lock->notify_all();
 125 }
 126 
 127 void ThreadTable::do_concurrent_work(JavaThread* jt) {
 128     _has_work = false;
 129     double load_factor = get_load_factor();
 130     log_debug(thread, table)("Concurrent work, load factor: %g", load_factor);
 131     if (load_factor > PREF_AVG_LIST_LEN && !_local_table->is_max_size_reached()) {
 132         grow(jt);
 133     }
 134 }
 135 
 136 void ThreadTable::grow(JavaThread* jt) {
 137     ThreadTableHash::GrowTask gt(_local_table);
 138     if (!gt.prepare(jt)) {
 139         return;
 140     }
 141     log_trace(thread, table)("Started to grow");
 142     {
 143         TraceTime timer("Grow", TRACETIME_LOG(Debug, membername, table, perf));
 144         while (gt.do_task(jt)) {
 145             gt.pause(jt);
 146             {
 147                 ThreadBlockInVM tbivm(jt);
 148             }
 149             gt.cont(jt);
 150         }
 151     }
 152     gt.done(jt);
 153     _current_size = table_size();
 154     log_info(thread, table)("Grown to size:" SIZE_FORMAT, _current_size);
 155 }
 156 
 157 class ThreadTableLookup : public StackObj {
 158   private:
 159     jlong          _tid;
 160     uintx         _hash;
 161   public:
 162     ThreadTableLookup(jlong tid)
 163     : _tid(tid), _hash(primitive_hash(tid)) {}
 164     uintx get_hash() const {
 165         return _hash;
 166     }
 167     bool equals(ThreadTableEntry **value, bool* is_dead) {
 168         *is_dead = false;
 169         bool equals = primitive_equals(_tid, (*value)->tid());
 170         if (!equals) {
 171             return false;
 172         }
 173         return true;
 174     }
 175 };
 176 
 177 class ThreadGet : public StackObj {
 178   private:
 179     JavaThread* _return;
 180   public:
 181     ThreadGet():_return(NULL) {}
 182     void operator()(ThreadTableEntry** val) {
 183         _return = (*val)->thread();
 184     }
 185     JavaThread* get_res_thread() {
 186         return _return;
 187     }
 188 };
 189 
 190 JavaThread* ThreadTable::find_thread(jlong tid) {
 191     Thread* thread = Thread::current();
 192     ThreadTableLookup lookup(tid);
 193     ThreadGet tg;
 194     _local_table->get(thread, lookup, tg);
 195     return tg.get_res_thread();
 196 }
 197 
 198 
 199 JavaThread* ThreadTable::add_thread(jlong tid, JavaThread* java_thread) {
 200     Thread* thread = Thread::current();
 201     ThreadTableLookup lookup(tid);
 202     ThreadGet tg;
 203     while(true) {
 204         if (_local_table->get(thread, lookup, tg)) {
 205             return tg.get_res_thread();
 206         }
 207         ThreadTableEntry* entry = new ThreadTableEntry(tid,java_thread);
 208         // The hash table takes ownership of the ThreadTableEntry,
 209         // even if it's not inserted.
 210         if (_local_table->insert(thread, lookup, entry)) {
 211             check_concurrent_work();
 212             return java_thread;
 213         }
 214     }
 215 }
 216 
 217 bool ThreadTable::remove_thread(jlong tid) {
 218     Thread* thread = Thread::current();
 219     ThreadTableLookup lookup(tid);
 220     return _local_table->remove(thread,lookup);
 221 }