Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openj9
Path: blob/master/runtime/bcutil/StringInternTable.cpp
5985 views
1
/*******************************************************************************
2
* Copyright (c) 2001, 2019 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
* StringInternTable.cpp
24
*/
25
26
#include "StringInternTable.hpp"
27
28
#include "j9.h"
29
#include "j9port.h"
30
#include "j9protos.h"
31
#include "j9consts.h"
32
#include "j9modron.h"
33
#include "ut_j9bcu.h"
34
#include "bcutil_internal.h"
35
#include "SCStringInternHelpers.h"
36
37
/*
38
* If you set VERIFY_ON_EVERY_OPERATION to 1, then the intern table
39
* will be verified before and after every API operation.
40
*/
41
#define VERIFY_ON_EVERY_OPERATION 0
42
43
#define J9INTERN_ENTRY_SET_PREVNODE(base, value) SRP_SET((base)->prevNode, value)
44
#define J9INTERN_ENTRY_GET_PREVNODE(base) J9SHAREDINTERNSRPHASHTABLEENTRY_PREVNODE(base)
45
#define J9INTERN_ENTRY_SET_NEXTNODE(base, value) SRP_SET((base)->nextNode, value)
46
#define J9INTERN_ENTRY_GET_NEXTNODE(base) J9SHAREDINTERNSRPHASHTABLEENTRY_NEXTNODE(base)
47
48
#define MAX_INTERN_NODE_WEIGHT 0xFFFF
49
50
#if VERIFY_ON_EVERY_OPERATION
51
#define VERIFY_ENTER() verify(__FILE__, __LINE__)
52
#define VERIFY_EXIT() verify(__FILE__, __LINE__)
53
#else
54
#define VERIFY_ENTER()
55
#define VERIFY_EXIT()
56
#endif
57
58
59
/* Query struct for searching the string intern hash table. */
60
typedef struct J9InternHashTableQuery {
61
J9UTF8 *utf8; /* Must be parallel to J9InternHashTableEntry.utf8 - always NULL */
62
J9ClassLoader *classLoader; /* Must be parallel to J9InternHashTableEntry.classLoader */
63
UDATA length;
64
U_8 *data;
65
} J9InternHashTableQuery;
66
67
68
extern "C" {
69
70
static UDATA
71
internHashEqualFn(void *leftKey, void *rightKey, void *userData)
72
{
73
UDATA leftUtf8Length, rightUtf8Length;
74
U_8 *leftUtf8Data, *rightUtf8Data;
75
76
J9InternHashTableEntry *left = (J9InternHashTableEntry*)leftKey;
77
J9InternHashTableEntry *right = (J9InternHashTableEntry*)rightKey;
78
79
if (left->classLoader != right->classLoader) {
80
return 0;
81
}
82
83
/* NOTE: Making this a function to get the fields kills performance - don't do it! */
84
if (NULL != left->utf8) {
85
leftUtf8Length = UDATA(J9UTF8_LENGTH(left->utf8));
86
leftUtf8Data = J9UTF8_DATA(left->utf8);
87
} else {
88
leftUtf8Length = ((J9InternHashTableQuery*)left)->length;
89
leftUtf8Data = ((J9InternHashTableQuery*)left)->data;
90
}
91
92
if (NULL != right->utf8) {
93
rightUtf8Length = UDATA(J9UTF8_LENGTH(right->utf8));
94
rightUtf8Data = J9UTF8_DATA(right->utf8);
95
} else {
96
rightUtf8Length = ((J9InternHashTableQuery*)right)->length;
97
rightUtf8Data = ((J9InternHashTableQuery*)right)->data;
98
}
99
100
return J9UTF8_DATA_EQUALS(leftUtf8Data, leftUtf8Length, rightUtf8Data, rightUtf8Length);
101
}
102
103
static UDATA
104
internHashFn(void *key, void *userData)
105
{
106
UDATA length;
107
U_8 *data;
108
109
J9InternHashTableEntry *node = (J9InternHashTableEntry*)key;
110
111
if (NULL != node->utf8) {
112
length = UDATA(J9UTF8_LENGTH(node->utf8));
113
data = J9UTF8_DATA(node->utf8);
114
} else {
115
length = ((J9InternHashTableQuery*)node)->length;
116
data = ((J9InternHashTableQuery*)node)->data;
117
}
118
119
UDATA hash = UDATA(node->classLoader);
120
for (UDATA i = 0; i < length; i++) {
121
hash = (hash << 5) - hash + data[i];
122
}
123
124
return hash;
125
}
126
127
#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)
128
static void
129
internHashClassLoadersUnloadHook(J9HookInterface **vmHooks, UDATA eventNum, void *eventData, void *userData)
130
{
131
J9VMClassLoadersUnloadEvent *event = (J9VMClassLoadersUnloadEvent*)eventData;
132
StringInternTable *stringInternTable = (StringInternTable*)userData;
133
Trc_Assert_BCU_mustHaveExclusiveVMAccess(event->currentThread);
134
stringInternTable->removeLocalNodesWithDeadClassLoaders();
135
}
136
#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */
137
138
} /* extern "C" */
139
140
StringInternTable::StringInternTable(J9JavaVM *vm, J9PortLibrary *portLibrary, UDATA maximumNodeCount) :
141
_vm(vm),
142
_portLibrary(portLibrary),
143
_internHashTable(NULL),
144
_headNode(NULL),
145
_tailNode(NULL),
146
_nodeCount(0),
147
_maximumNodeCount(maximumNodeCount)
148
{
149
if (0 != maximumNodeCount) {
150
_internHashTable = hashTableNew(OMRPORT_FROM_J9PORT(_portLibrary), J9_GET_CALLSITE(),
151
U_32(maximumNodeCount + 1), sizeof(J9InternHashTableEntry), sizeof(char *), 0,
152
J9MEM_CATEGORY_CLASSES, internHashFn, internHashEqualFn, NULL, vm);
153
154
#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)
155
if ((NULL != _vm) && (NULL != _internHashTable)) {
156
J9HookInterface **vmHooks = _vm->internalVMFunctions->getVMHookInterface(vm);
157
if (0 != (*vmHooks)->J9HookRegisterWithCallSite(vmHooks, J9HOOK_VM_CLASS_LOADERS_UNLOAD, internHashClassLoadersUnloadHook, OMR_GET_CALLSITE(), this)) {
158
/* Failed to register the hook. Kill the hash table so that isOK() returns false. */
159
hashTableFree(_internHashTable);
160
_internHashTable = NULL;
161
}
162
}
163
#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */
164
}
165
166
if (isOK()) {
167
if (maximumNodeCount == 0) {
168
Trc_BCU_stringInternTableNotCreated();
169
} else {
170
Trc_BCU_stringInternTableCreated(maximumNodeCount);
171
}
172
} else {
173
Trc_BCU_stringInternTableCreationFailed(maximumNodeCount);
174
}
175
}
176
177
StringInternTable::~StringInternTable()
178
{
179
if (NULL != _internHashTable) {
180
hashTableFree(_internHashTable);
181
182
#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)
183
if (NULL != _vm) {
184
J9HookInterface **vmHooks = _vm->internalVMFunctions->getVMHookInterface(_vm);
185
(*vmHooks)->J9HookUnregister(vmHooks, J9HOOK_VM_CLASS_LOADERS_UNLOAD, internHashClassLoadersUnloadHook, this);
186
}
187
#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */
188
}
189
}
190
191
bool
192
StringInternTable::findUtf8(J9InternSearchInfo *searchInfo, J9SharedInvariantInternTable *sharedInternTable, bool isSharedROMClass, J9InternSearchResult *result)
193
{
194
195
if (NULL == _internHashTable) {
196
return false;
197
}
198
199
VERIFY_ENTER();
200
201
#if defined(J9VM_OPT_SHARED_CLASSES)
202
/**
203
* Every UTF8s in shared string intern table is shared UTF8 in the shared cache.
204
* If ROM class is shared, then UTF8 can be used without SRP range check. (isSharedCacheInRange = SC_COMPLETELY_IN_THE_SRP_RANGE)
205
*
206
* If ROM class is local, then UTF8 can be used only if it is in the SRP range.
207
* In this case, SRP range check is required depending on the value of isSharedCacheInRange.
208
* 1 (SC_COMPLETELY_OUT_OF_THE_SRP_RANGE): Shared cache is not in the SRP range. UTF8 can not be used by local ROM class.
209
* 2 (SC_COMPLETELY_IN_THE_SRP_RANGE): Shared cache is in the SRP range. UTF8 can be used by local ROM class safely without need for SRP range check.
210
* 3 (SC_PARTIALLY_IN_THE_SRP_RANGE): Part of the shared cache is in the SRP range. UTF8 can be used if it is in the SRP range of local ROM class. SRP range check is required.
211
*
212
*/
213
if ((SC_COMPLETELY_IN_THE_SRP_RANGE == searchInfo->sharedCacheSRPRangeInfo) || (SC_PARTIALLY_IN_THE_SRP_RANGE == searchInfo->sharedCacheSRPRangeInfo)) {
214
if (NULL != sharedInternTable) {
215
J9SharedInternHashTableQuery sharedQuery;
216
217
sharedQuery.length = searchInfo->stringLength;
218
sharedQuery.data = searchInfo->stringData;
219
220
J9SharedInternSRPHashTableEntry *sharedNode = (J9SharedInternSRPHashTableEntry*)srpHashTableFind(sharedInternTable->sharedInvariantSRPHashtable, &sharedQuery);
221
if (NULL != sharedNode) {
222
223
J9UTF8 *utf8 = J9SHAREDINTERNSRPHASHTABLEENTRY_UTF8SRP(sharedNode);
224
bool isUTF8InSRPRange = true;
225
#if defined(J9VM_ENV_DATA64)
226
if (SC_PARTIALLY_IN_THE_SRP_RANGE == searchInfo->sharedCacheSRPRangeInfo) {
227
if (!areAddressesInSRPRange(utf8, searchInfo->romClassBaseAddr) ||
228
!areAddressesInSRPRange(utf8, searchInfo->romClassEndAddr)) {
229
isUTF8InSRPRange = false;
230
}
231
}
232
#endif
233
if (isUTF8InSRPRange) {
234
Trc_BCU_Assert_True(NULL != utf8);
235
result->utf8 = utf8;
236
result->node = sharedNode;
237
result->isSharedNode = true;
238
VERIFY_EXIT();
239
return true;
240
}
241
}
242
}
243
}
244
#endif
245
246
/* Otherwise, search the local hash table. */
247
J9InternHashTableQuery query;
248
249
query.utf8 = NULL;
250
query.classLoader = searchInfo->classloader;
251
query.length = searchInfo->stringLength;
252
query.data = searchInfo->stringData;
253
254
J9InternHashTableEntry *node = (J9InternHashTableEntry*)hashTableFind(_internHashTable, &query);
255
256
/* If the node was not matched, try searching for the utf8 with the systemClassLoader. */
257
if ((NULL == node) && (NULL != _vm) && (query.classLoader != _vm->systemClassLoader)) {
258
query.classLoader = _vm->systemClassLoader;
259
node = (J9InternHashTableEntry*)hashTableFind(_internHashTable, &query);
260
}
261
262
if (NULL != node) {
263
bool sharedUtf8 = (STRINGINTERNTABLES_NODE_FLAG_UTF8_IS_SHARED == (node->flags & STRINGINTERNTABLES_NODE_FLAG_UTF8_IS_SHARED));
264
/**
265
* node->utf8 can be used in 3 conditions out of 4.
266
* 1. sharedUTF8 && isSharedROMClass - node->utf8 can be used and no SRP range check is required for 64 bit environment.
267
* (isSharedCacheInRange = SC_COMPLETELY_IN_THE_SRP_RANGE)
268
* 2. sharedUTF8 && !isSharedROMClass - node->utf8 maybe used if it is in SRP range.
269
* (isSharedCacheInRange = SC_COMPLETELY_OUT_OF_THE_SRP_RANGE, SC_COMPLETELY_IN_THE_SRP_RANGE or SC_PARTIALLY_IN_THE_SRP_RANGE)
270
* 3. !sharedUTF8 && isSharedROMClass - node->utf8 can not be used.
271
* ROM classes in shared cache can only use sharedUTF8s in the shared cache.
272
* 4. !sharedUTF8 && !isSharedROMClass - node->utf8 maybe used if it if in SRP range.
273
*
274
*
275
*/
276
if (sharedUtf8 || !isSharedROMClass) {
277
/* Condition 1, 2 or 4 */
278
bool isUTF8InSRPRange = true;
279
#if defined(J9VM_ENV_DATA64)
280
if (!isSharedROMClass) {
281
/* Condition 2 or 4 */
282
if ((sharedUtf8 && (SC_PARTIALLY_IN_THE_SRP_RANGE == searchInfo->sharedCacheSRPRangeInfo)) || !sharedUtf8) {
283
if (!areAddressesInSRPRange(node->utf8, searchInfo->romClassBaseAddr) ||
284
!areAddressesInSRPRange(node->utf8, searchInfo->romClassEndAddr)) {
285
isUTF8InSRPRange = false;
286
}
287
} else if ((sharedUtf8 && (searchInfo->sharedCacheSRPRangeInfo == SC_COMPLETELY_OUT_OF_THE_SRP_RANGE))) {
288
isUTF8InSRPRange = false;
289
}
290
}
291
#endif
292
if (isUTF8InSRPRange) {
293
result->utf8 = node->utf8;
294
result->node = node;
295
result->isSharedNode = false;
296
VERIFY_EXIT();
297
return true;
298
}
299
}
300
}
301
302
VERIFY_EXIT();
303
return false;
304
}
305
void
306
StringInternTable::markNodeAsUsed(J9InternSearchResult *result, J9SharedInvariantInternTable *sharedTable)
307
{
308
VERIFY_ENTER();
309
310
#if defined(J9VM_OPT_SHARED_CLASSES)
311
if (NULL != sharedTable) {
312
313
if (result->isSharedNode) {
314
if (0 == (sharedTable->flags & J9AVLTREE_DISABLE_SHARED_TREE_UPDATES)) {
315
J9SharedInternSRPHashTableEntry *sharedNode = (J9SharedInternSRPHashTableEntry *)result->node;
316
updateSharedNodeWeight(sharedTable, sharedNode);
317
promoteSharedNodeToHead(sharedTable, sharedNode);
318
}
319
} else { /* local node */
320
J9InternHashTableEntry *node = (J9InternHashTableEntry*)result->node;
321
322
bool promoteToShared = false;
323
324
updateLocalNodeWeight(node);
325
326
/* we don't allocate new shared nodes, so there must be an existing shared node in order to promote */
327
if (NULL != sharedTable->tailNode) {
328
J9SharedInternSRPHashTableEntry *sharedTail = sharedTable->tailNode;
329
promoteToShared = testNodePromotionWeight(sharedTable, node, sharedTail);
330
}
331
332
if (promoteToShared) {
333
swapLocalNodeWithTailSharedNode(node, sharedTable);
334
} else {
335
promoteNodeToHead(node);
336
}
337
}
338
VERIFY_EXIT();
339
return;
340
}
341
#endif
342
343
Trc_BCU_Assert_False(result->isSharedNode);
344
345
J9InternHashTableEntry *node = (J9InternHashTableEntry*)result->node;
346
promoteNodeToHead(node);
347
348
VERIFY_EXIT();
349
}
350
void
351
StringInternTable::internUtf8(J9UTF8 *utf8, J9ClassLoader *classLoader, bool fromSharedROMClass, J9SharedInvariantInternTable *sharedInternTable)
352
{
353
Trc_BCU_Assert_True(NULL != utf8);
354
355
if (NULL == _internHashTable) {
356
return;
357
}
358
359
VERIFY_ENTER();
360
361
#if defined(J9VM_OPT_SHARED_CLASSES)
362
if ((NULL != sharedInternTable) && (0 == (sharedInternTable->flags & J9AVLTREE_DISABLE_SHARED_TREE_UPDATES)) && fromSharedROMClass) {
363
J9SharedInternSRPHashTableEntry *insertedEntry = insertSharedNode(sharedInternTable, utf8, 0, STRINGINTERNTABLES_NODE_FLAG_UTF8_IS_SHARED, /* promoteIfExistingFound = */ true);
364
365
if (NULL != insertedEntry) {
366
VERIFY_EXIT();
367
return;
368
} else {
369
Trc_BCU_getNewStringTableNode_SharedTreeFull(sharedInternTable->sharedInvariantSRPHashtable->srpHashtableInternal->tableSize);
370
}
371
}
372
#endif
373
374
/* We did not insert it into the shared SRPHashTable, so add it to the local hash table. */
375
J9InternHashTableEntry nodeToAdd;
376
377
nodeToAdd.utf8 = utf8;
378
nodeToAdd.classLoader = classLoader;
379
nodeToAdd.internWeight = 0;
380
nodeToAdd.flags = (fromSharedROMClass ? STRINGINTERNTABLES_NODE_FLAG_UTF8_IS_SHARED : 0);
381
382
J9InternHashTableEntry *entry = insertLocalNode(&nodeToAdd, /* promoteIfExistingFound = */ true);
383
if (NULL != entry) {
384
if (_nodeCount == _maximumNodeCount) {
385
Trc_BCU_Assert_True(NULL != _tailNode);
386
deleteLocalNode(_tailNode);
387
} else {
388
_nodeCount++;
389
}
390
}
391
392
VERIFY_EXIT();
393
}
394
395
J9InternHashTableEntry *
396
StringInternTable::insertLocalNode(J9InternHashTableEntry *node, bool promoteIfExistingFound)
397
{
398
U_32 nodeCount = hashTableGetCount(_internHashTable);
399
400
node = (J9InternHashTableEntry*)hashTableAdd(_internHashTable, node);
401
402
if (NULL != node) {
403
/*
404
* If the hash table node count increased, that means that the returned node
405
* is the newly inserted node (and not an existing node with the same value).
406
*
407
* We cannot compare the returned node pointer with the node pointer passed to
408
* this function to check this as the pointer passed to this function points to
409
* a temporary node on the stack.
410
*/
411
bool wasInserted = (hashTableGetCount(_internHashTable) == nodeCount + 1);
412
413
if (wasInserted) {
414
node->prevNode = NULL;
415
node->nextNode = _headNode;
416
if (NULL == _tailNode) {
417
_tailNode = node;
418
} else {
419
_headNode->prevNode = node;
420
}
421
_headNode = node;
422
} else {
423
/* Found existing node with same value - do not return it. */
424
if (promoteIfExistingFound) {
425
promoteNodeToHead(node);
426
}
427
node = NULL;
428
}
429
}
430
431
return node;
432
}
433
434
void
435
StringInternTable::deleteLocalNode(J9InternHashTableEntry *node)
436
{
437
removeNodeFromList(node);
438
hashTableRemove(_internHashTable, node);
439
}
440
441
void
442
StringInternTable::removeNodeFromList(J9InternHashTableEntry *node)
443
{
444
Trc_BCU_Assert_True(NULL != node);
445
446
J9InternHashTableEntry *prevNode = node->prevNode;
447
J9InternHashTableEntry *nextNode = node->nextNode;
448
449
if (NULL != prevNode) {
450
prevNode->nextNode = nextNode;
451
}
452
if (NULL != nextNode) {
453
nextNode->prevNode = prevNode;
454
}
455
if (_tailNode == node) {
456
_tailNode = prevNode;
457
}
458
if (_headNode == node) {
459
_headNode = nextNode;
460
}
461
}
462
463
void
464
StringInternTable::promoteNodeToHead(J9InternHashTableEntry *node)
465
{
466
Trc_BCU_Assert_True(NULL != node);
467
468
/* Assumption: node must already be in the LRU list. */
469
470
if (_headNode != node) {
471
J9InternHashTableEntry *prevNode = node->prevNode;
472
J9InternHashTableEntry *nextNode = node->nextNode;
473
474
if (NULL != prevNode) {
475
prevNode->nextNode = nextNode;
476
}
477
if (NULL != nextNode) {
478
nextNode->prevNode = prevNode;
479
}
480
481
node->prevNode = NULL;
482
node->nextNode = _headNode;
483
484
_headNode->prevNode = node;
485
_headNode = node;
486
487
if (node == _tailNode) {
488
_tailNode = prevNode;
489
}
490
}
491
}
492
493
void StringInternTable::removeLocalNodesWithDeadClassLoaders()
494
{
495
VERIFY_ENTER();
496
497
J9InternHashTableEntry *node = _headNode;
498
while (NULL != node) {
499
J9InternHashTableEntry *nextNode = node->nextNode;
500
if (J9_ARE_ALL_BITS_SET(node->classLoader->gcFlags, J9_GC_CLASS_LOADER_DEAD)) {
501
deleteLocalNode(node);
502
_nodeCount--;
503
}
504
node = nextNode;
505
}
506
507
VERIFY_EXIT();
508
}
509
510
#define VERIFY_ASSERT(cond) do { \
511
if (!(cond)) { \
512
PORT_ACCESS_FROM_PORT(_portLibrary); \
513
j9tty_printf(PORTLIB, "StringInternTable verification condition ["#cond"] failed at %s:%d!\n", file, line); \
514
Trc_BCU_Assert_InternVerificationFailure(); \
515
return false; \
516
} \
517
} while (0)
518
519
bool StringInternTable::verify(const char *file, IDATA line) const
520
{
521
VERIFY_ASSERT(_nodeCount <= _maximumNodeCount);
522
VERIFY_ASSERT(hashTableGetCount(_internHashTable) == _nodeCount);
523
524
if ((NULL != _headNode) || (NULL != _tailNode)) {
525
verifyNode(_headNode, file, line);
526
verifyNode(_tailNode, file, line);
527
VERIFY_ASSERT(_nodeCount > 0);
528
} else {
529
VERIFY_ASSERT(NULL == _headNode);
530
VERIFY_ASSERT(NULL == _tailNode);
531
VERIFY_ASSERT(_nodeCount == 0);
532
}
533
534
UDATA count = 0;
535
J9InternHashTableEntry *node = _headNode;
536
while (NULL != node) {
537
verifyNode(node, file, line);
538
node = node->nextNode;
539
count++;
540
}
541
VERIFY_ASSERT(count == _nodeCount);
542
543
return true;
544
}
545
546
bool StringInternTable::verifyNode(J9InternHashTableEntry *node, const char *file, IDATA line) const
547
{
548
VERIFY_ASSERT(NULL != node);
549
if (node == _headNode) {
550
VERIFY_ASSERT(NULL == node->prevNode);
551
} else {
552
VERIFY_ASSERT(NULL != node->prevNode);
553
VERIFY_ASSERT(node == node->prevNode->nextNode);
554
}
555
if (node == _tailNode) {
556
VERIFY_ASSERT(NULL == node->nextNode);
557
} else {
558
VERIFY_ASSERT(NULL != node->nextNode);
559
VERIFY_ASSERT(node == node->nextNode->prevNode);
560
}
561
VERIFY_ASSERT(NULL != node->utf8);
562
VERIFY_ASSERT(hashTableFind(_internHashTable, node) == node);
563
return true;
564
}
565
566
#undef VERIFY_ASSERT
567
568
#if defined(J9VM_OPT_SHARED_CLASSES)
569
570
J9SharedInternSRPHashTableEntry *
571
StringInternTable::insertSharedNode(J9SharedInvariantInternTable *table, J9UTF8 * utf8, U_16 internWeight, U_16 flags, bool promoteIfExistingFound)
572
{
573
J9SharedInternHashTableQuery sharedQuery;
574
575
sharedQuery.length = J9UTF8_LENGTH(utf8);
576
sharedQuery.data = J9UTF8_DATA(utf8);
577
578
J9SharedInternSRPHashTableEntry *insertedNode = (J9SharedInternSRPHashTableEntry *)srpHashTableAdd(table->sharedInvariantSRPHashtable, &sharedQuery);
579
if (insertedNode) {
580
/* It is either found or inserted into srp hashtable */
581
if (IS_NEW_ELEMENT(insertedNode)) {
582
/* A new node is inserted */
583
UNMARK_NEW_ELEMENT(insertedNode, J9SharedInternSRPHashTableEntry *);
584
insertedNode->prevNode = 0;
585
/* Fix the pointers and fill out the inserted shared node */
586
J9INTERN_ENTRY_SET_NEXTNODE(insertedNode, table->headNode);
587
if (NULL == table->tailNode) {
588
table->tailNode = insertedNode;
589
} else {
590
J9INTERN_ENTRY_SET_PREVNODE(table->headNode, insertedNode);
591
}
592
table->headNode = insertedNode;
593
SRP_SET(insertedNode->utf8SRP, utf8);
594
insertedNode->internWeight = internWeight;
595
insertedNode->flags = flags;
596
/* update the table */
597
++*(table->totalSharedNodesPtr);
598
*(table->totalSharedWeightPtr) += internWeight;
599
} else {
600
/* Existing node is found */
601
if (promoteIfExistingFound) {
602
promoteSharedNodeToHead(table, insertedNode);
603
}
604
}
605
}
606
return insertedNode;
607
}
608
609
void
610
StringInternTable::deleteSharedNode(J9SharedInvariantInternTable *table, J9SharedInternSRPHashTableEntry *node)
611
{
612
removeSharedNodeFromList(table, node);
613
U_16 weight = node->internWeight;
614
J9UTF8 * utf8ToDelete = SRP_GET(node->utf8SRP, J9UTF8 *);
615
J9SharedInternHashTableQuery sharedQuery;
616
sharedQuery.length = J9UTF8_LENGTH(utf8ToDelete);
617
sharedQuery.data = J9UTF8_DATA(utf8ToDelete);
618
619
U_32 deleted = srpHashTableRemove(table->sharedInvariantSRPHashtable, &sharedQuery);
620
if (0 == deleted) {
621
/* shared node removed - update stats */
622
--*(table->totalSharedNodesPtr);
623
*(table->totalSharedWeightPtr) -= weight;
624
}
625
return;
626
}
627
628
void
629
StringInternTable::removeSharedNodeFromList(J9SharedInvariantInternTable *table, J9SharedInternSRPHashTableEntry *sharedNode)
630
{
631
Trc_BCU_Assert_True(NULL != sharedNode);
632
633
J9SharedInternSRPHashTableEntry *prevNode = J9INTERN_ENTRY_GET_PREVNODE(sharedNode);
634
J9SharedInternSRPHashTableEntry *nextNode = J9INTERN_ENTRY_GET_NEXTNODE(sharedNode);
635
636
if (NULL != prevNode) {
637
J9INTERN_ENTRY_SET_NEXTNODE(prevNode, nextNode);
638
}
639
if (NULL != nextNode) {
640
J9INTERN_ENTRY_SET_PREVNODE(nextNode, prevNode);
641
}
642
if (table->tailNode == sharedNode) {
643
table->tailNode = prevNode;
644
}
645
if (table->headNode == sharedNode) {
646
table->headNode = nextNode;
647
}
648
}
649
650
/**
651
* Calculate the bytes required to write a UTF8 with the specified length into memory.
652
* @param length Length of the UTF8
653
* @return U_16 space required in the memory to write the UTF8 with specified length
654
*/
655
UDATA
656
StringInternTable::getRequiredBytesForUTF8Length(U_16 length)
657
{
658
UDATA bytesRequiredByUTF8 = sizeof(U_16);
659
bytesRequiredByUTF8 += length;
660
if (0 != (length & 1)) {
661
bytesRequiredByUTF8 += 1;
662
}
663
return bytesRequiredByUTF8;
664
}
665
666
/**
667
* Increment a local node's weight when it's used.
668
* @param[in] node The local node.
669
*/
670
void
671
StringInternTable::updateLocalNodeWeight(J9InternHashTableEntry *node)
672
{
673
if (node->internWeight == MAX_INTERN_NODE_WEIGHT) {
674
return;
675
}
676
677
UDATA bytesRequiredByUTF8 = getRequiredBytesForUTF8Length(J9UTF8_LENGTH(node->utf8));
678
if ((node->internWeight + bytesRequiredByUTF8) >= MAX_INTERN_NODE_WEIGHT) {
679
node->internWeight = MAX_INTERN_NODE_WEIGHT;
680
} else {
681
node->internWeight += (U_16)bytesRequiredByUTF8;
682
}
683
}
684
685
/**
686
* Increment a shared node's weight when it's used.
687
* The shared table's weight is also incremented.
688
* @param[in] table The invariantInternTable.
689
* @param[in] sharedNode The shared node.
690
*/
691
void
692
StringInternTable::updateSharedNodeWeight(J9SharedInvariantInternTable *table, J9SharedInternSRPHashTableEntry *sharedNode)
693
{
694
J9UTF8 *utf8 = J9SHAREDINTERNSRPHASHTABLEENTRY_UTF8SRP(sharedNode);
695
UDATA bytesRequiredByUTF8 = getRequiredBytesForUTF8Length(J9UTF8_LENGTH(utf8));
696
if (sharedNode->internWeight != MAX_INTERN_NODE_WEIGHT) {
697
if ((sharedNode->internWeight + bytesRequiredByUTF8) >= MAX_INTERN_NODE_WEIGHT) {
698
sharedNode->internWeight = MAX_INTERN_NODE_WEIGHT;
699
} else {
700
sharedNode->internWeight += (U_16)bytesRequiredByUTF8;
701
}
702
}
703
704
*(table->totalSharedWeightPtr) += (U_32)bytesRequiredByUTF8;
705
}
706
707
/**
708
* Tests whether a local node should be promoted to the shared tree.
709
* @param[in] table The invariantInternTable.
710
* @param[in] node The local node to be tested.
711
* @param[in] sharedNodeToDisplace The shared node to displace, not null.
712
* @retval false The node should not be promoted into the shared table.
713
* @retval true The node should be promoted into the shared table.
714
*/
715
bool
716
StringInternTable::testNodePromotionWeight(J9SharedInvariantInternTable *table, J9InternHashTableEntry *node, J9SharedInternSRPHashTableEntry *sharedNodeToDisplace)
717
{
718
/* Only allow a node into the shared tree IF:
719
* - If J9AVLTREE_DISABLE_SHARED_TREE_UPDATES is not set
720
* - It is STRINGINTERNTABLENODE_FLAG_UTF8_IS_SHARED. This means that the string it points to is shared and has been successfully relocated.
721
* - If the intern weight of the node to be promoted is greater than the shared node being displaced,
722
* - or if the node has a greater intern weight of > 100,
723
*/
724
return (0 == (table->flags & J9AVLTREE_DISABLE_SHARED_TREE_UPDATES)) &&
725
(0 != (node->flags & STRINGINTERNTABLES_NODE_FLAG_UTF8_IS_SHARED)) &&
726
((node->internWeight > 100) || (node->internWeight > sharedNodeToDisplace->internWeight));
727
}
728
729
void
730
StringInternTable::swapLocalNodeWithTailSharedNode(J9InternHashTableEntry *node, J9SharedInvariantInternTable *table)
731
{
732
/* No need to check for J9AVLTREE_DISABLE_SHARED_TREE_UPDATES as this is tested in testNodePromotionWeight() */
733
J9InternHashTableEntry localNodeToInsert;
734
J9SharedInternSRPHashTableEntry *sharedTail = table->tailNode;
735
/* Copy shared tail node data*/
736
localNodeToInsert.utf8 = SRP_GET(sharedTail->utf8SRP, J9UTF8*);
737
localNodeToInsert.flags = (sharedTail->flags);
738
localNodeToInsert.internWeight = sharedTail->internWeight;
739
localNodeToInsert.classLoader = table->systemClassLoader;
740
741
deleteSharedNode(table, table->tailNode);
742
743
J9SharedInternSRPHashTableEntry * insertedSharedNode = insertSharedNode(table, node->utf8, node->internWeight, node->flags, FALSE);
744
deleteLocalNode(node);
745
746
/* Copy data from shared node to a local node and insert the local node into the hash table. */
747
J9InternHashTableEntry *entry = insertLocalNode(&localNodeToInsert, /* promoteIfExistingFound = */ false);
748
if (NULL == entry) {
749
/* Shared node matched an existing node in the local table and was not inserted. Decrement local count. */
750
_nodeCount--;
751
}
752
}
753
754
void
755
StringInternTable::promoteSharedNodeToHead(J9SharedInvariantInternTable *table, J9SharedInternSRPHashTableEntry *node)
756
{
757
J9SharedInternSRPHashTableEntry *headNode;
758
J9SharedInternSRPHashTableEntry **tableHeadNodePtr = &(table->headNode);
759
760
headNode = *tableHeadNodePtr;
761
762
if (headNode != node) {
763
J9SharedInternSRPHashTableEntry *prevNode;
764
J9SharedInternSRPHashTableEntry *nextNode;
765
766
prevNode = J9INTERN_ENTRY_GET_PREVNODE(node);
767
nextNode = J9INTERN_ENTRY_GET_NEXTNODE(node);
768
769
if (prevNode) {
770
/* unlink this node from the previous node */
771
J9INTERN_ENTRY_SET_NEXTNODE(prevNode, nextNode);
772
}
773
if (nextNode) {
774
/* unlink this node from the next node */
775
J9INTERN_ENTRY_SET_PREVNODE(nextNode, prevNode);
776
}
777
778
J9INTERN_ENTRY_SET_PREVNODE(node, 0);
779
J9INTERN_ENTRY_SET_NEXTNODE(node, headNode);
780
781
if (headNode) {
782
J9INTERN_ENTRY_SET_PREVNODE(headNode, node);
783
}
784
*tableHeadNodePtr = node;
785
786
if (NULL == table->tailNode) {
787
table->tailNode = node;
788
} else if (node == table->tailNode) {
789
if (prevNode != NULL) {
790
table->tailNode = prevNode;
791
}
792
}
793
}
794
795
return;
796
}
797
798
#endif /* J9VM_OPT_SHARED_CLASSES */
799
800