Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openj9
Path: blob/master/runtime/compiler/env/ClassLoaderTable.cpp
6000 views
1
/*******************************************************************************
2
* Copyright (c) 2000, 2021 IBM Corp. and others
3
*
4
* This program and the accompanying materials are made available under
5
* the terms of the Eclipse Public License 2.0 which accompanies this
6
* distribution and is available at https://www.eclipse.org/legal/epl-2.0/
7
* or the Apache License, Version 2.0 which accompanies this distribution and
8
* is available at https://www.apache.org/licenses/LICENSE-2.0.
9
*
10
* This Source Code may also be made available under the following
11
* Secondary Licenses when the conditions for such availability set
12
* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
13
* General Public License, version 2 with the GNU Classpath
14
* Exception [1] and GNU General Public License, version 2 with the
15
* OpenJDK Assembly Exception [2].
16
*
17
* [1] https://www.gnu.org/software/classpath/license.html
18
* [2] http://openjdk.java.net/legal/assembly-exception.html
19
*
20
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception
21
*******************************************************************************/
22
23
#include "AtomicSupport.hpp"
24
#include "control/CompilationThread.hpp"
25
#include "env/ClassLoaderTable.hpp"
26
#include "env/J9SharedCache.hpp"
27
#include "env/VerboseLog.hpp"
28
#include "infra/MonitorTable.hpp"
29
30
31
enum TableKind { Loader, Chain, Name };
32
33
// To make the three-way map between class loaders, class chains, and class names more efficient, this
34
// struct is linked into three linked lists - one for each hash table in TR_PersistentClassLoaderTable.
35
struct TR_ClassLoaderInfo
36
{
37
TR_PERSISTENT_ALLOC(TR_Memory::PersistentCHTable)
38
39
TR_ClassLoaderInfo(void *loader, void *chain) :
40
_loader(loader), _loaderTableNext(NULL),
41
_chain(chain), _chainTableNext(NULL)
42
#if defined(J9VM_OPT_JITSERVER)
43
, _nameTableNext(NULL)
44
#endif /* defined(J9VM_OPT_JITSERVER) */
45
{ }
46
47
const J9UTF8 *name(TR_J9SharedCache *sharedCache) const
48
{
49
return J9ROMCLASS_CLASSNAME(sharedCache->startingROMClassOfClassChain((uintptr_t *)_chain));
50
}
51
52
template<TableKind T> TR_ClassLoaderInfo *&next();
53
template<TableKind T> bool equals(const void *key) const;
54
55
void *const _loader;
56
TR_ClassLoaderInfo *_loaderTableNext;
57
void *const _chain;
58
TR_ClassLoaderInfo *_chainTableNext;
59
#if defined(J9VM_OPT_JITSERVER)
60
TR_ClassLoaderInfo *_nameTableNext;
61
#endif /* defined(J9VM_OPT_JITSERVER) */
62
};
63
64
65
template<TableKind T> static size_t hash(const void *key);
66
67
template<TableKind T> static TR_ClassLoaderInfo *
68
lookup(TR_ClassLoaderInfo *const *table, size_t &index, TR_ClassLoaderInfo *&prev, const void *key)
69
{
70
index = hash<T>(key) % CLASSLOADERTABLE_SIZE;
71
TR_ClassLoaderInfo *info = table[index];
72
prev = NULL;
73
while (info && !info->equals<T>(key))
74
{
75
prev = info;
76
info = info->next<T>();
77
}
78
return info;
79
}
80
81
template<TableKind T> static void
82
insert(TR_ClassLoaderInfo *info, TR_ClassLoaderInfo **table, size_t index)
83
{
84
info->next<T>() = table[index];
85
// Write barrier guarantees that a reader thread traversing the list will read
86
// the new list head only after its next field is already set to the old head.
87
VM_AtomicSupport::writeBarrier();
88
table[index] = info;
89
}
90
91
template<TableKind T> static void
92
remove(TR_ClassLoaderInfo *info, TR_ClassLoaderInfo *prev, TR_ClassLoaderInfo **table, size_t index)
93
{
94
if (prev)
95
prev->next<T>() = info->next<T>();
96
else
97
table[index] = info->next<T>();
98
}
99
100
101
template<> TR_ClassLoaderInfo *&TR_ClassLoaderInfo::next<Loader>() { return _loaderTableNext; }
102
template<> bool TR_ClassLoaderInfo::equals<Loader>(const void *loader) const { return loader == _loader; }
103
// Remove trailing zero bits in aligned pointer for better hash distribution
104
template<> size_t hash<Loader>(const void *loader) { return (uintptr_t)loader >> 3; }
105
106
template<> TR_ClassLoaderInfo *&TR_ClassLoaderInfo::next<Chain>() { return _chainTableNext; }
107
template<> bool TR_ClassLoaderInfo::equals<Chain>(const void *chain) const { return chain == _chain; }
108
// Remove trailing zero bits in aligned pointer for better hash distribution
109
template<> size_t hash<Chain>(const void *chain) { return (uintptr_t)chain >> 3; }
110
111
112
#if defined(J9VM_OPT_JITSERVER)
113
114
template<> TR_ClassLoaderInfo *&TR_ClassLoaderInfo::next<Name>() { return _nameTableNext; }
115
116
struct NameKey
117
{
118
const uint8_t *_data;
119
size_t _length;
120
TR_J9SharedCache *_sharedCache;
121
};
122
123
template<> bool
124
TR_ClassLoaderInfo::equals<Name>(const void *keyPtr) const
125
{
126
auto key = (const NameKey *)keyPtr;
127
const J9UTF8 *str = name(key->_sharedCache);
128
return J9UTF8_DATA_EQUALS(J9UTF8_DATA(str), J9UTF8_LENGTH(str), key->_data, key->_length);
129
}
130
131
template<> size_t
132
hash<Name>(const void *keyPtr)
133
{
134
auto key = (const NameKey *)keyPtr;
135
size_t h = 0;
136
for (size_t i = 0; i < key->_length; ++i)
137
h = (h << 5) - h + key->_data[i];
138
return h;
139
}
140
141
#endif /* defined(J9VM_OPT_JITSERVER) */
142
143
144
TR_PersistentClassLoaderTable::TR_PersistentClassLoaderTable(TR_PersistentMemory *persistentMemory) :
145
_persistentMemory(persistentMemory), _sharedCache(NULL)
146
{
147
memset(_loaderTable, 0, sizeof(_loaderTable));
148
memset(_chainTable, 0, sizeof(_chainTable));
149
#if defined(J9VM_OPT_JITSERVER)
150
memset(_nameTable, 0, sizeof(_nameTable));
151
#endif /* defined(J9VM_OPT_JITSERVER) */
152
}
153
154
155
//NOTE: Class loader table doesn't require any additional locking for synchronization.
156
// Writers are always mutually exclusive with each other. Readers cannot be concurrent
157
// with the writers that remove entries from the table. Traversing linked lists in hash
158
// buckets can be concurrent with inserting new entries (which only needs a write barrier).
159
160
static bool
161
hasSharedVMAccess(J9VMThread *vmThread)
162
{
163
return (vmThread->publicFlags & J9_PUBLIC_FLAGS_VM_ACCESS) && !vmThread->omrVMThread->exclusiveCount;
164
}
165
166
167
void
168
TR_PersistentClassLoaderTable::associateClassLoaderWithClass(J9VMThread *vmThread, void *loader,
169
TR_OpaqueClassBlock *clazz)
170
{
171
// Since current thread has shared VM access and holds the classTableMutex,
172
// no other thread can be modifying the table at the same time.
173
TR_ASSERT(hasSharedVMAccess(vmThread), "Must have shared VM access");
174
TR_ASSERT(TR::MonitorTable::get()->getClassTableMutex()->owned_by_self(), "Must hold classTableMutex");
175
if (!_sharedCache)
176
return;
177
178
// Lookup by class loader and check if it already has an associated class
179
size_t index;
180
TR_ClassLoaderInfo *prev;
181
TR_ClassLoaderInfo *info = lookup<Loader>(_loaderTable, index, prev, loader);
182
if (info)
183
return;
184
185
#if defined(J9VM_OPT_JITSERVER)
186
bool useAOTCache = _persistentMemory->getPersistentInfo()->getJITServerUseAOTCache();
187
const J9UTF8 *nameStr = J9ROMCLASS_CLASSNAME(((J9Class *)clazz)->romClass);
188
const uint8_t *name = J9UTF8_DATA(nameStr);
189
uint16_t nameLength = J9UTF8_LENGTH(nameStr);
190
#endif /* defined(J9VM_OPT_JITSERVER) */
191
192
void *chain = _sharedCache->rememberClass(clazz);
193
if (!chain)
194
{
195
#if defined(J9VM_OPT_JITSERVER)
196
if (useAOTCache && TR::Options::getVerboseOption(TR_VerboseJITServer))
197
TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer,
198
"ERROR: Failed to get class chain for %.*s loaded by %p",
199
nameLength, (const char *)name, loader
200
);
201
#endif /* defined(J9VM_OPT_JITSERVER) */
202
return;
203
}
204
TR_ASSERT(_sharedCache->isPointerInSharedCache(chain), "Class chain must be in SCC");
205
206
info = new (_persistentMemory) TR_ClassLoaderInfo(loader, chain);
207
if (!info)
208
{
209
// This is a bad situation because we can't associate the right class with this class loader.
210
// Probably not critical if multiple class loaders aren't routinely loading the exact same class.
211
//TODO: Prevent this class loader from associating with a different class
212
#if defined(J9VM_OPT_JITSERVER)
213
if (useAOTCache && TR::Options::getVerboseOption(TR_VerboseJITServer))
214
TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer,
215
"ERROR: Failed to associate class %.*s chain %p with loader %p",
216
nameLength, (const char *)name, chain, loader
217
);
218
#endif /* defined(J9VM_OPT_JITSERVER) */
219
return;
220
}
221
insert<Loader>(info, _loaderTable, index);
222
223
// Lookup by class chain and check if was already associated with another class loader
224
TR_ClassLoaderInfo *otherInfo = lookup<Chain>(_chainTable, index, prev, chain);
225
if (otherInfo)
226
{
227
// There is more than one class loader with identical first loaded class.
228
// Current heuristic doesn't work in this scenario, but it doesn't break
229
// correctness, and in the worst case can only result in failed AOT loads.
230
// We have added this loader to _classLoaderTable, which has a nice side
231
// benefit that we won't keep trying to add it, so leave it there.
232
#if defined(J9VM_OPT_JITSERVER)
233
if (useAOTCache && TR::Options::getVerboseOption(TR_VerboseJITServer))
234
TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer,
235
"ERROR: Class %.*s chain %p already associated with loader %p != %p",
236
nameLength, (const char *)name, chain, otherInfo->_loader, loader
237
);
238
#endif /* defined(J9VM_OPT_JITSERVER) */
239
return;
240
}
241
insert<Chain>(info, _chainTable, index);
242
243
#if defined(J9VM_OPT_JITSERVER)
244
if (useAOTCache)
245
{
246
// Lookup by class name and check if it was already associated with another class loader
247
NameKey key { name, nameLength, _sharedCache };
248
otherInfo = lookup<Name>(_nameTable, index, prev, &key);
249
if (otherInfo)
250
{
251
// There is more than one class loader with the same name of the first loaded
252
// class (but the classes themselves are not identical). Current AOT cache
253
// heuristic doesn't work in this scenario, but it doesn't break correctness,
254
// and in the worst case can only result in failed AOT method deserialization.
255
if (TR::Options::getVerboseOption(TR_VerboseJITServer))
256
TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer,
257
"ERROR: Class name %.*s already associated with loader %p != %p",
258
nameLength, (const char *)name, otherInfo->_loader, loader
259
);
260
return;
261
}
262
insert<Name>(info, _nameTable, index);
263
264
if (TR::Options::getVerboseOption(TR_VerboseJITServer))
265
TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer,
266
"Associated class loader %p with class %.*s chain %p",
267
loader, nameLength, (const char *)name, chain
268
);
269
}
270
#endif /* defined(J9VM_OPT_JITSERVER) */
271
}
272
273
274
static void
275
assertCurrentThreadCanRead()
276
{
277
// To guarantee that reading the table is not concurrent with class loader removal during GC,
278
// current thread must either have shared VM access or hold the classUnloadMonitor for reading.
279
#if defined(DEBUG) || defined(PROD_WITH_ASSUMES)
280
TR::Compilation *comp = TR::comp();
281
TR_ASSERT(hasSharedVMAccess(comp->j9VMThread()) ||
282
(TR::MonitorTable::get()->getClassUnloadMonitorHoldCount(comp->getCompThreadID()) > 0),
283
"Must either have shared VM access of hold classUnloadMonitor for reading");
284
#endif /* defined(DEBUG) || defined(PROD_WITH_ASSUMES) */
285
}
286
287
void *
288
TR_PersistentClassLoaderTable::lookupClassChainAssociatedWithClassLoader(void *loader) const
289
{
290
assertCurrentThreadCanRead();
291
if (!_sharedCache)
292
return NULL;
293
294
size_t index;
295
TR_ClassLoaderInfo *prev;
296
TR_ClassLoaderInfo *info = lookup<Loader>(_loaderTable, index, prev, loader);
297
return info ? info->_chain : NULL;
298
}
299
300
void *
301
TR_PersistentClassLoaderTable::lookupClassLoaderAssociatedWithClassChain(void *chain) const
302
{
303
assertCurrentThreadCanRead();
304
if (!_sharedCache)
305
return NULL;
306
307
size_t index;
308
TR_ClassLoaderInfo *prev;
309
TR_ClassLoaderInfo *info = lookup<Chain>(_chainTable, index, prev, chain);
310
return info ? info->_loader : NULL;
311
}
312
313
#if defined(J9VM_OPT_JITSERVER)
314
315
std::pair<void *, void *>
316
TR_PersistentClassLoaderTable::lookupClassLoaderAndChainAssociatedWithClassName(const uint8_t *data, size_t length) const
317
{
318
assertCurrentThreadCanRead();
319
if (!_sharedCache)
320
return { NULL, NULL };
321
322
NameKey key { data, length, _sharedCache };
323
size_t index;
324
TR_ClassLoaderInfo *prev;
325
TR_ClassLoaderInfo *info = lookup<Name>(_nameTable, index, prev, &key);
326
if (!info)
327
return { NULL, NULL };
328
return { info->_loader, info->_chain };
329
}
330
331
#endif /* defined(J9VM_OPT_JITSERVER) */
332
333
334
void
335
TR_PersistentClassLoaderTable::removeClassLoader(J9VMThread *vmThread, void *loader)
336
{
337
// Since current thread has exclusive VM access and holds the classUnloadMonitor
338
// for writing (NOTE: we don't have an assertion for that due to lack of API),
339
// no other thread can be modifying the table at the same time.
340
TR_ASSERT((vmThread->publicFlags & J9_PUBLIC_FLAGS_VM_ACCESS) && vmThread->omrVMThread->exclusiveCount,
341
"Must have exclusive VM access");
342
if (!_sharedCache)
343
return;
344
345
// Remove from the table indexed by class loader
346
size_t index;
347
TR_ClassLoaderInfo *prev;
348
TR_ClassLoaderInfo *info = lookup<Loader>(_loaderTable, index, prev, loader);
349
if (!info)
350
return;
351
remove<Loader>(info, prev, _loaderTable, index);
352
353
// Remove from the table indexed by class chain
354
TR_ClassLoaderInfo *otherInfo = lookup<Chain>(_chainTable, index, prev, info->_chain);
355
if (otherInfo == info)// Otherwise the entry belongs to another class loader
356
remove<Chain>(info, prev, _chainTable, index);
357
358
#if defined(J9VM_OPT_JITSERVER)
359
if (_persistentMemory->getPersistentInfo()->getJITServerUseAOTCache())
360
{
361
// Remove from the table indexed by class name
362
const J9UTF8 *nameStr = info->name(_sharedCache);
363
const uint8_t *name = J9UTF8_DATA(nameStr);
364
uint16_t nameLength = J9UTF8_LENGTH(nameStr);
365
366
NameKey key { name, nameLength, _sharedCache };
367
otherInfo = lookup<Name>(_nameTable, index, prev, &key);
368
if (otherInfo == info)// Otherwise the entry belongs to another class loader
369
remove<Name>(info, prev, _nameTable, index);
370
371
if (TR::Options::getVerboseOption(TR_VerboseJITServer))
372
TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer,
373
"Removed class loader %p associated with class %.*s chain %p",
374
loader, nameLength, (const char *)name, info->_chain
375
);
376
}
377
#endif /* defined(J9VM_OPT_JITSERVER) */
378
379
_persistentMemory->freePersistentMemory(info);
380
}
381
382