jdk

Форк
0
/
threadIdTable.cpp 
257 строк · 7.9 Кб
1

2
/*
3
* Copyright (c) 2019, 2023, 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 "classfile/javaClasses.inline.hpp"
28
#include "runtime/atomic.hpp"
29
#include "runtime/interfaceSupport.inline.hpp"
30
#include "runtime/javaThread.inline.hpp"
31
#include "runtime/threadSMR.hpp"
32
#include "runtime/timerTrace.hpp"
33
#include "services/threadIdTable.hpp"
34
#include "utilities/concurrentHashTable.inline.hpp"
35
#include "utilities/concurrentHashTableTasks.inline.hpp"
36

37
typedef ConcurrentHashTable<ThreadIdTableConfig, mtInternal> ThreadIdTableHash;
38

39
// 2^24 is max size
40
static const size_t END_SIZE = 24;
41
// Default initial size 256
42
static const size_t DEFAULT_TABLE_SIZE_LOG = 8;
43
// Prefer short chains of avg 2
44
static const double PREF_AVG_LIST_LEN = 2.0;
45
static ThreadIdTableHash* volatile _local_table = nullptr;
46
static volatile size_t _current_size = 0;
47
static volatile size_t _items_count = 0;
48

49
volatile bool ThreadIdTable::_is_initialized = false;
50
volatile bool ThreadIdTable::_has_work = false;
51

52
class ThreadIdTableEntry : public CHeapObj<mtInternal> {
53
private:
54
  jlong _tid;
55
  JavaThread* _java_thread;
56
public:
57
  ThreadIdTableEntry(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 ThreadIdTableConfig : public AllStatic {
65
  public:
66
    typedef ThreadIdTableEntry* Value;
67

68
    static uintx get_hash(Value const& value, bool* is_dead) {
69
      jlong tid = value->tid();
70
      return primitive_hash(tid);
71
    }
72
    static void* allocate_node(void* context, size_t size, Value const& value) {
73
      ThreadIdTable::item_added();
74
      return AllocateHeap(size, mtInternal);
75
    }
76
    static void free_node(void* context, void* memory, Value const& value) {
77
      delete value;
78
      FreeHeap(memory);
79
      ThreadIdTable::item_removed();
80
    }
81
};
82

83
// Lazily creates the table and populates it with the given
84
// thread list
85
void ThreadIdTable::lazy_initialize(const ThreadsList *threads) {
86
  if (!_is_initialized) {
87
    {
88
      // There is no obvious benefit in allowing the thread table
89
      // to be concurrently populated during initialization.
90
      MutexLocker ml(ThreadIdTableCreate_lock);
91
      if (_is_initialized) {
92
        return;
93
      }
94
      create_table(threads->length());
95
      _is_initialized = true;
96
    }
97
    for (uint i = 0; i < threads->length(); i++) {
98
      JavaThread* thread = threads->thread_at(i);
99
      oop tobj = thread->threadObj();
100
      if (tobj != nullptr) {
101
        jlong java_tid = java_lang_Thread::thread_id(tobj);
102
        MutexLocker ml(Threads_lock);
103
        if (!thread->is_exiting()) {
104
          // Must be inside the lock to ensure that we don't add a thread to the table
105
          // that has just passed the removal point in Threads::remove().
106
          add_thread(java_tid, thread);
107
        }
108
      }
109
    }
110
  }
111
}
112

113
void ThreadIdTable::create_table(size_t size) {
114
  assert(_local_table == nullptr, "Thread table is already created");
115
  size_t size_log = ceil_log2(size);
116
  size_t start_size_log =
117
      size_log > DEFAULT_TABLE_SIZE_LOG ? size_log : DEFAULT_TABLE_SIZE_LOG;
118
  _current_size = (size_t)1 << start_size_log;
119
  _local_table =
120
      new ThreadIdTableHash(start_size_log, END_SIZE, ThreadIdTableHash::DEFAULT_GROW_HINT);
121
}
122

123
void ThreadIdTable::item_added() {
124
  Atomic::inc(&_items_count);
125
  log_trace(thread, table) ("Thread entry added");
126
}
127

128
void ThreadIdTable::item_removed() {
129
  Atomic::dec(&_items_count);
130
  log_trace(thread, table) ("Thread entry removed");
131
}
132

133
double ThreadIdTable::get_load_factor() {
134
  return ((double)_items_count) / (double)_current_size;
135
}
136

137
size_t ThreadIdTable::table_size() {
138
  return (size_t)1 << _local_table->get_size_log2(Thread::current());
139
}
140

141
void ThreadIdTable::check_concurrent_work() {
142
  if (_has_work) {
143
    return;
144
  }
145

146
  double load_factor = get_load_factor();
147
  // Resize if we have more items than preferred load factor
148
  if ( load_factor > PREF_AVG_LIST_LEN && !_local_table->is_max_size_reached()) {
149
    log_debug(thread, table)("Concurrent work triggered, load factor: %g",
150
                             load_factor);
151
    trigger_concurrent_work();
152
  }
153
}
154

155
void ThreadIdTable::trigger_concurrent_work() {
156
  MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag);
157
  _has_work = true;
158
  Service_lock->notify_all();
159
}
160

161
void ThreadIdTable::grow(JavaThread* jt) {
162
  ThreadIdTableHash::GrowTask gt(_local_table);
163
  if (!gt.prepare(jt)) {
164
    return;
165
  }
166
  log_trace(thread, table)("Started to grow");
167
  TraceTime timer("Grow", TRACETIME_LOG(Debug, membername, table, perf));
168
  while (gt.do_task(jt)) {
169
    gt.pause(jt);
170
    {
171
      ThreadBlockInVM tbivm(jt);
172
    }
173
    gt.cont(jt);
174
  }
175
  gt.done(jt);
176
  _current_size = table_size();
177
  log_info(thread, table)("Grown to size:" SIZE_FORMAT, _current_size);
178
}
179

180
class ThreadIdTableLookup : public StackObj {
181
private:
182
  jlong _tid;
183
  uintx _hash;
184
public:
185
  ThreadIdTableLookup(jlong tid)
186
    : _tid(tid), _hash(primitive_hash(tid)) {}
187
  uintx get_hash() const {
188
    return _hash;
189
  }
190
  bool equals(ThreadIdTableEntry** value) {
191
    bool equals = primitive_equals(_tid, (*value)->tid());
192
    if (!equals) {
193
      return false;
194
    }
195
    return true;
196
  }
197
  bool is_dead(ThreadIdTableEntry** value) {
198
    return false;
199
  }
200
};
201

202
class ThreadGet : public StackObj {
203
private:
204
  JavaThread* _return;
205
public:
206
  ThreadGet(): _return(nullptr) {}
207
  void operator()(ThreadIdTableEntry** val) {
208
    _return = (*val)->thread();
209
  }
210
  JavaThread* get_res_thread() {
211
    return _return;
212
  }
213
};
214

215
void ThreadIdTable::do_concurrent_work(JavaThread* jt) {
216
  assert(_is_initialized, "Thread table is not initialized");
217
  _has_work = false;
218
  double load_factor = get_load_factor();
219
  log_debug(thread, table)("Concurrent work, load factor: %g", load_factor);
220
  if (load_factor > PREF_AVG_LIST_LEN && !_local_table->is_max_size_reached()) {
221
    grow(jt);
222
  }
223
}
224

225
JavaThread* ThreadIdTable::add_thread(jlong tid, JavaThread* java_thread) {
226
  assert(_is_initialized, "Thread table is not initialized");
227
  Thread* thread = Thread::current();
228
  ThreadIdTableLookup lookup(tid);
229
  ThreadGet tg;
230
  while (true) {
231
    if (_local_table->get(thread, lookup, tg)) {
232
      return tg.get_res_thread();
233
    }
234
    ThreadIdTableEntry* entry = new ThreadIdTableEntry(tid, java_thread);
235
    // The hash table takes ownership of the ThreadTableEntry,
236
    // even if it's not inserted.
237
    if (_local_table->insert(thread, lookup, entry)) {
238
      check_concurrent_work();
239
      return java_thread;
240
    }
241
  }
242
}
243

244
JavaThread* ThreadIdTable::find_thread_by_tid(jlong tid) {
245
  assert(_is_initialized, "Thread table is not initialized");
246
  Thread* thread = Thread::current();
247
  ThreadIdTableLookup lookup(tid);
248
  ThreadGet tg;
249
  _local_table->get(thread, lookup, tg);
250
  return tg.get_res_thread();
251
}
252

253
bool ThreadIdTable::remove_thread(jlong tid) {
254
  assert(_is_initialized, "Thread table is not initialized");
255
  Thread* thread = Thread::current();
256
  ThreadIdTableLookup lookup(tid);
257
  return _local_table->remove(thread, lookup);
258
}
259

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.