Path: blob/master/runtime/bcutil/StringInternTable.cpp
5985 views
/*******************************************************************************1* Copyright (c) 2001, 2019 IBM Corp. and others2*3* This program and the accompanying materials are made available under4* the terms of the Eclipse Public License 2.0 which accompanies this5* distribution and is available at https://www.eclipse.org/legal/epl-2.0/6* or the Apache License, Version 2.0 which accompanies this distribution and7* is available at https://www.apache.org/licenses/LICENSE-2.0.8*9* This Source Code may also be made available under the following10* Secondary Licenses when the conditions for such availability set11* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU12* General Public License, version 2 with the GNU Classpath13* Exception [1] and GNU General Public License, version 2 with the14* OpenJDK Assembly Exception [2].15*16* [1] https://www.gnu.org/software/classpath/license.html17* [2] http://openjdk.java.net/legal/assembly-exception.html18*19* 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-exception20*******************************************************************************/21/*22* StringInternTable.cpp23*/2425#include "StringInternTable.hpp"2627#include "j9.h"28#include "j9port.h"29#include "j9protos.h"30#include "j9consts.h"31#include "j9modron.h"32#include "ut_j9bcu.h"33#include "bcutil_internal.h"34#include "SCStringInternHelpers.h"3536/*37* If you set VERIFY_ON_EVERY_OPERATION to 1, then the intern table38* will be verified before and after every API operation.39*/40#define VERIFY_ON_EVERY_OPERATION 04142#define J9INTERN_ENTRY_SET_PREVNODE(base, value) SRP_SET((base)->prevNode, value)43#define J9INTERN_ENTRY_GET_PREVNODE(base) J9SHAREDINTERNSRPHASHTABLEENTRY_PREVNODE(base)44#define J9INTERN_ENTRY_SET_NEXTNODE(base, value) SRP_SET((base)->nextNode, value)45#define J9INTERN_ENTRY_GET_NEXTNODE(base) J9SHAREDINTERNSRPHASHTABLEENTRY_NEXTNODE(base)4647#define MAX_INTERN_NODE_WEIGHT 0xFFFF4849#if VERIFY_ON_EVERY_OPERATION50#define VERIFY_ENTER() verify(__FILE__, __LINE__)51#define VERIFY_EXIT() verify(__FILE__, __LINE__)52#else53#define VERIFY_ENTER()54#define VERIFY_EXIT()55#endif565758/* Query struct for searching the string intern hash table. */59typedef struct J9InternHashTableQuery {60J9UTF8 *utf8; /* Must be parallel to J9InternHashTableEntry.utf8 - always NULL */61J9ClassLoader *classLoader; /* Must be parallel to J9InternHashTableEntry.classLoader */62UDATA length;63U_8 *data;64} J9InternHashTableQuery;656667extern "C" {6869static UDATA70internHashEqualFn(void *leftKey, void *rightKey, void *userData)71{72UDATA leftUtf8Length, rightUtf8Length;73U_8 *leftUtf8Data, *rightUtf8Data;7475J9InternHashTableEntry *left = (J9InternHashTableEntry*)leftKey;76J9InternHashTableEntry *right = (J9InternHashTableEntry*)rightKey;7778if (left->classLoader != right->classLoader) {79return 0;80}8182/* NOTE: Making this a function to get the fields kills performance - don't do it! */83if (NULL != left->utf8) {84leftUtf8Length = UDATA(J9UTF8_LENGTH(left->utf8));85leftUtf8Data = J9UTF8_DATA(left->utf8);86} else {87leftUtf8Length = ((J9InternHashTableQuery*)left)->length;88leftUtf8Data = ((J9InternHashTableQuery*)left)->data;89}9091if (NULL != right->utf8) {92rightUtf8Length = UDATA(J9UTF8_LENGTH(right->utf8));93rightUtf8Data = J9UTF8_DATA(right->utf8);94} else {95rightUtf8Length = ((J9InternHashTableQuery*)right)->length;96rightUtf8Data = ((J9InternHashTableQuery*)right)->data;97}9899return J9UTF8_DATA_EQUALS(leftUtf8Data, leftUtf8Length, rightUtf8Data, rightUtf8Length);100}101102static UDATA103internHashFn(void *key, void *userData)104{105UDATA length;106U_8 *data;107108J9InternHashTableEntry *node = (J9InternHashTableEntry*)key;109110if (NULL != node->utf8) {111length = UDATA(J9UTF8_LENGTH(node->utf8));112data = J9UTF8_DATA(node->utf8);113} else {114length = ((J9InternHashTableQuery*)node)->length;115data = ((J9InternHashTableQuery*)node)->data;116}117118UDATA hash = UDATA(node->classLoader);119for (UDATA i = 0; i < length; i++) {120hash = (hash << 5) - hash + data[i];121}122123return hash;124}125126#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)127static void128internHashClassLoadersUnloadHook(J9HookInterface **vmHooks, UDATA eventNum, void *eventData, void *userData)129{130J9VMClassLoadersUnloadEvent *event = (J9VMClassLoadersUnloadEvent*)eventData;131StringInternTable *stringInternTable = (StringInternTable*)userData;132Trc_Assert_BCU_mustHaveExclusiveVMAccess(event->currentThread);133stringInternTable->removeLocalNodesWithDeadClassLoaders();134}135#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */136137} /* extern "C" */138139StringInternTable::StringInternTable(J9JavaVM *vm, J9PortLibrary *portLibrary, UDATA maximumNodeCount) :140_vm(vm),141_portLibrary(portLibrary),142_internHashTable(NULL),143_headNode(NULL),144_tailNode(NULL),145_nodeCount(0),146_maximumNodeCount(maximumNodeCount)147{148if (0 != maximumNodeCount) {149_internHashTable = hashTableNew(OMRPORT_FROM_J9PORT(_portLibrary), J9_GET_CALLSITE(),150U_32(maximumNodeCount + 1), sizeof(J9InternHashTableEntry), sizeof(char *), 0,151J9MEM_CATEGORY_CLASSES, internHashFn, internHashEqualFn, NULL, vm);152153#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)154if ((NULL != _vm) && (NULL != _internHashTable)) {155J9HookInterface **vmHooks = _vm->internalVMFunctions->getVMHookInterface(vm);156if (0 != (*vmHooks)->J9HookRegisterWithCallSite(vmHooks, J9HOOK_VM_CLASS_LOADERS_UNLOAD, internHashClassLoadersUnloadHook, OMR_GET_CALLSITE(), this)) {157/* Failed to register the hook. Kill the hash table so that isOK() returns false. */158hashTableFree(_internHashTable);159_internHashTable = NULL;160}161}162#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */163}164165if (isOK()) {166if (maximumNodeCount == 0) {167Trc_BCU_stringInternTableNotCreated();168} else {169Trc_BCU_stringInternTableCreated(maximumNodeCount);170}171} else {172Trc_BCU_stringInternTableCreationFailed(maximumNodeCount);173}174}175176StringInternTable::~StringInternTable()177{178if (NULL != _internHashTable) {179hashTableFree(_internHashTable);180181#if defined(J9VM_GC_DYNAMIC_CLASS_UNLOADING)182if (NULL != _vm) {183J9HookInterface **vmHooks = _vm->internalVMFunctions->getVMHookInterface(_vm);184(*vmHooks)->J9HookUnregister(vmHooks, J9HOOK_VM_CLASS_LOADERS_UNLOAD, internHashClassLoadersUnloadHook, this);185}186#endif /* J9VM_GC_DYNAMIC_CLASS_UNLOADING */187}188}189190bool191StringInternTable::findUtf8(J9InternSearchInfo *searchInfo, J9SharedInvariantInternTable *sharedInternTable, bool isSharedROMClass, J9InternSearchResult *result)192{193194if (NULL == _internHashTable) {195return false;196}197198VERIFY_ENTER();199200#if defined(J9VM_OPT_SHARED_CLASSES)201/**202* Every UTF8s in shared string intern table is shared UTF8 in the shared cache.203* If ROM class is shared, then UTF8 can be used without SRP range check. (isSharedCacheInRange = SC_COMPLETELY_IN_THE_SRP_RANGE)204*205* If ROM class is local, then UTF8 can be used only if it is in the SRP range.206* In this case, SRP range check is required depending on the value of isSharedCacheInRange.207* 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.208* 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.209* 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.210*211*/212if ((SC_COMPLETELY_IN_THE_SRP_RANGE == searchInfo->sharedCacheSRPRangeInfo) || (SC_PARTIALLY_IN_THE_SRP_RANGE == searchInfo->sharedCacheSRPRangeInfo)) {213if (NULL != sharedInternTable) {214J9SharedInternHashTableQuery sharedQuery;215216sharedQuery.length = searchInfo->stringLength;217sharedQuery.data = searchInfo->stringData;218219J9SharedInternSRPHashTableEntry *sharedNode = (J9SharedInternSRPHashTableEntry*)srpHashTableFind(sharedInternTable->sharedInvariantSRPHashtable, &sharedQuery);220if (NULL != sharedNode) {221222J9UTF8 *utf8 = J9SHAREDINTERNSRPHASHTABLEENTRY_UTF8SRP(sharedNode);223bool isUTF8InSRPRange = true;224#if defined(J9VM_ENV_DATA64)225if (SC_PARTIALLY_IN_THE_SRP_RANGE == searchInfo->sharedCacheSRPRangeInfo) {226if (!areAddressesInSRPRange(utf8, searchInfo->romClassBaseAddr) ||227!areAddressesInSRPRange(utf8, searchInfo->romClassEndAddr)) {228isUTF8InSRPRange = false;229}230}231#endif232if (isUTF8InSRPRange) {233Trc_BCU_Assert_True(NULL != utf8);234result->utf8 = utf8;235result->node = sharedNode;236result->isSharedNode = true;237VERIFY_EXIT();238return true;239}240}241}242}243#endif244245/* Otherwise, search the local hash table. */246J9InternHashTableQuery query;247248query.utf8 = NULL;249query.classLoader = searchInfo->classloader;250query.length = searchInfo->stringLength;251query.data = searchInfo->stringData;252253J9InternHashTableEntry *node = (J9InternHashTableEntry*)hashTableFind(_internHashTable, &query);254255/* If the node was not matched, try searching for the utf8 with the systemClassLoader. */256if ((NULL == node) && (NULL != _vm) && (query.classLoader != _vm->systemClassLoader)) {257query.classLoader = _vm->systemClassLoader;258node = (J9InternHashTableEntry*)hashTableFind(_internHashTable, &query);259}260261if (NULL != node) {262bool sharedUtf8 = (STRINGINTERNTABLES_NODE_FLAG_UTF8_IS_SHARED == (node->flags & STRINGINTERNTABLES_NODE_FLAG_UTF8_IS_SHARED));263/**264* node->utf8 can be used in 3 conditions out of 4.265* 1. sharedUTF8 && isSharedROMClass - node->utf8 can be used and no SRP range check is required for 64 bit environment.266* (isSharedCacheInRange = SC_COMPLETELY_IN_THE_SRP_RANGE)267* 2. sharedUTF8 && !isSharedROMClass - node->utf8 maybe used if it is in SRP range.268* (isSharedCacheInRange = SC_COMPLETELY_OUT_OF_THE_SRP_RANGE, SC_COMPLETELY_IN_THE_SRP_RANGE or SC_PARTIALLY_IN_THE_SRP_RANGE)269* 3. !sharedUTF8 && isSharedROMClass - node->utf8 can not be used.270* ROM classes in shared cache can only use sharedUTF8s in the shared cache.271* 4. !sharedUTF8 && !isSharedROMClass - node->utf8 maybe used if it if in SRP range.272*273*274*/275if (sharedUtf8 || !isSharedROMClass) {276/* Condition 1, 2 or 4 */277bool isUTF8InSRPRange = true;278#if defined(J9VM_ENV_DATA64)279if (!isSharedROMClass) {280/* Condition 2 or 4 */281if ((sharedUtf8 && (SC_PARTIALLY_IN_THE_SRP_RANGE == searchInfo->sharedCacheSRPRangeInfo)) || !sharedUtf8) {282if (!areAddressesInSRPRange(node->utf8, searchInfo->romClassBaseAddr) ||283!areAddressesInSRPRange(node->utf8, searchInfo->romClassEndAddr)) {284isUTF8InSRPRange = false;285}286} else if ((sharedUtf8 && (searchInfo->sharedCacheSRPRangeInfo == SC_COMPLETELY_OUT_OF_THE_SRP_RANGE))) {287isUTF8InSRPRange = false;288}289}290#endif291if (isUTF8InSRPRange) {292result->utf8 = node->utf8;293result->node = node;294result->isSharedNode = false;295VERIFY_EXIT();296return true;297}298}299}300301VERIFY_EXIT();302return false;303}304void305StringInternTable::markNodeAsUsed(J9InternSearchResult *result, J9SharedInvariantInternTable *sharedTable)306{307VERIFY_ENTER();308309#if defined(J9VM_OPT_SHARED_CLASSES)310if (NULL != sharedTable) {311312if (result->isSharedNode) {313if (0 == (sharedTable->flags & J9AVLTREE_DISABLE_SHARED_TREE_UPDATES)) {314J9SharedInternSRPHashTableEntry *sharedNode = (J9SharedInternSRPHashTableEntry *)result->node;315updateSharedNodeWeight(sharedTable, sharedNode);316promoteSharedNodeToHead(sharedTable, sharedNode);317}318} else { /* local node */319J9InternHashTableEntry *node = (J9InternHashTableEntry*)result->node;320321bool promoteToShared = false;322323updateLocalNodeWeight(node);324325/* we don't allocate new shared nodes, so there must be an existing shared node in order to promote */326if (NULL != sharedTable->tailNode) {327J9SharedInternSRPHashTableEntry *sharedTail = sharedTable->tailNode;328promoteToShared = testNodePromotionWeight(sharedTable, node, sharedTail);329}330331if (promoteToShared) {332swapLocalNodeWithTailSharedNode(node, sharedTable);333} else {334promoteNodeToHead(node);335}336}337VERIFY_EXIT();338return;339}340#endif341342Trc_BCU_Assert_False(result->isSharedNode);343344J9InternHashTableEntry *node = (J9InternHashTableEntry*)result->node;345promoteNodeToHead(node);346347VERIFY_EXIT();348}349void350StringInternTable::internUtf8(J9UTF8 *utf8, J9ClassLoader *classLoader, bool fromSharedROMClass, J9SharedInvariantInternTable *sharedInternTable)351{352Trc_BCU_Assert_True(NULL != utf8);353354if (NULL == _internHashTable) {355return;356}357358VERIFY_ENTER();359360#if defined(J9VM_OPT_SHARED_CLASSES)361if ((NULL != sharedInternTable) && (0 == (sharedInternTable->flags & J9AVLTREE_DISABLE_SHARED_TREE_UPDATES)) && fromSharedROMClass) {362J9SharedInternSRPHashTableEntry *insertedEntry = insertSharedNode(sharedInternTable, utf8, 0, STRINGINTERNTABLES_NODE_FLAG_UTF8_IS_SHARED, /* promoteIfExistingFound = */ true);363364if (NULL != insertedEntry) {365VERIFY_EXIT();366return;367} else {368Trc_BCU_getNewStringTableNode_SharedTreeFull(sharedInternTable->sharedInvariantSRPHashtable->srpHashtableInternal->tableSize);369}370}371#endif372373/* We did not insert it into the shared SRPHashTable, so add it to the local hash table. */374J9InternHashTableEntry nodeToAdd;375376nodeToAdd.utf8 = utf8;377nodeToAdd.classLoader = classLoader;378nodeToAdd.internWeight = 0;379nodeToAdd.flags = (fromSharedROMClass ? STRINGINTERNTABLES_NODE_FLAG_UTF8_IS_SHARED : 0);380381J9InternHashTableEntry *entry = insertLocalNode(&nodeToAdd, /* promoteIfExistingFound = */ true);382if (NULL != entry) {383if (_nodeCount == _maximumNodeCount) {384Trc_BCU_Assert_True(NULL != _tailNode);385deleteLocalNode(_tailNode);386} else {387_nodeCount++;388}389}390391VERIFY_EXIT();392}393394J9InternHashTableEntry *395StringInternTable::insertLocalNode(J9InternHashTableEntry *node, bool promoteIfExistingFound)396{397U_32 nodeCount = hashTableGetCount(_internHashTable);398399node = (J9InternHashTableEntry*)hashTableAdd(_internHashTable, node);400401if (NULL != node) {402/*403* If the hash table node count increased, that means that the returned node404* is the newly inserted node (and not an existing node with the same value).405*406* We cannot compare the returned node pointer with the node pointer passed to407* this function to check this as the pointer passed to this function points to408* a temporary node on the stack.409*/410bool wasInserted = (hashTableGetCount(_internHashTable) == nodeCount + 1);411412if (wasInserted) {413node->prevNode = NULL;414node->nextNode = _headNode;415if (NULL == _tailNode) {416_tailNode = node;417} else {418_headNode->prevNode = node;419}420_headNode = node;421} else {422/* Found existing node with same value - do not return it. */423if (promoteIfExistingFound) {424promoteNodeToHead(node);425}426node = NULL;427}428}429430return node;431}432433void434StringInternTable::deleteLocalNode(J9InternHashTableEntry *node)435{436removeNodeFromList(node);437hashTableRemove(_internHashTable, node);438}439440void441StringInternTable::removeNodeFromList(J9InternHashTableEntry *node)442{443Trc_BCU_Assert_True(NULL != node);444445J9InternHashTableEntry *prevNode = node->prevNode;446J9InternHashTableEntry *nextNode = node->nextNode;447448if (NULL != prevNode) {449prevNode->nextNode = nextNode;450}451if (NULL != nextNode) {452nextNode->prevNode = prevNode;453}454if (_tailNode == node) {455_tailNode = prevNode;456}457if (_headNode == node) {458_headNode = nextNode;459}460}461462void463StringInternTable::promoteNodeToHead(J9InternHashTableEntry *node)464{465Trc_BCU_Assert_True(NULL != node);466467/* Assumption: node must already be in the LRU list. */468469if (_headNode != node) {470J9InternHashTableEntry *prevNode = node->prevNode;471J9InternHashTableEntry *nextNode = node->nextNode;472473if (NULL != prevNode) {474prevNode->nextNode = nextNode;475}476if (NULL != nextNode) {477nextNode->prevNode = prevNode;478}479480node->prevNode = NULL;481node->nextNode = _headNode;482483_headNode->prevNode = node;484_headNode = node;485486if (node == _tailNode) {487_tailNode = prevNode;488}489}490}491492void StringInternTable::removeLocalNodesWithDeadClassLoaders()493{494VERIFY_ENTER();495496J9InternHashTableEntry *node = _headNode;497while (NULL != node) {498J9InternHashTableEntry *nextNode = node->nextNode;499if (J9_ARE_ALL_BITS_SET(node->classLoader->gcFlags, J9_GC_CLASS_LOADER_DEAD)) {500deleteLocalNode(node);501_nodeCount--;502}503node = nextNode;504}505506VERIFY_EXIT();507}508509#define VERIFY_ASSERT(cond) do { \510if (!(cond)) { \511PORT_ACCESS_FROM_PORT(_portLibrary); \512j9tty_printf(PORTLIB, "StringInternTable verification condition ["#cond"] failed at %s:%d!\n", file, line); \513Trc_BCU_Assert_InternVerificationFailure(); \514return false; \515} \516} while (0)517518bool StringInternTable::verify(const char *file, IDATA line) const519{520VERIFY_ASSERT(_nodeCount <= _maximumNodeCount);521VERIFY_ASSERT(hashTableGetCount(_internHashTable) == _nodeCount);522523if ((NULL != _headNode) || (NULL != _tailNode)) {524verifyNode(_headNode, file, line);525verifyNode(_tailNode, file, line);526VERIFY_ASSERT(_nodeCount > 0);527} else {528VERIFY_ASSERT(NULL == _headNode);529VERIFY_ASSERT(NULL == _tailNode);530VERIFY_ASSERT(_nodeCount == 0);531}532533UDATA count = 0;534J9InternHashTableEntry *node = _headNode;535while (NULL != node) {536verifyNode(node, file, line);537node = node->nextNode;538count++;539}540VERIFY_ASSERT(count == _nodeCount);541542return true;543}544545bool StringInternTable::verifyNode(J9InternHashTableEntry *node, const char *file, IDATA line) const546{547VERIFY_ASSERT(NULL != node);548if (node == _headNode) {549VERIFY_ASSERT(NULL == node->prevNode);550} else {551VERIFY_ASSERT(NULL != node->prevNode);552VERIFY_ASSERT(node == node->prevNode->nextNode);553}554if (node == _tailNode) {555VERIFY_ASSERT(NULL == node->nextNode);556} else {557VERIFY_ASSERT(NULL != node->nextNode);558VERIFY_ASSERT(node == node->nextNode->prevNode);559}560VERIFY_ASSERT(NULL != node->utf8);561VERIFY_ASSERT(hashTableFind(_internHashTable, node) == node);562return true;563}564565#undef VERIFY_ASSERT566567#if defined(J9VM_OPT_SHARED_CLASSES)568569J9SharedInternSRPHashTableEntry *570StringInternTable::insertSharedNode(J9SharedInvariantInternTable *table, J9UTF8 * utf8, U_16 internWeight, U_16 flags, bool promoteIfExistingFound)571{572J9SharedInternHashTableQuery sharedQuery;573574sharedQuery.length = J9UTF8_LENGTH(utf8);575sharedQuery.data = J9UTF8_DATA(utf8);576577J9SharedInternSRPHashTableEntry *insertedNode = (J9SharedInternSRPHashTableEntry *)srpHashTableAdd(table->sharedInvariantSRPHashtable, &sharedQuery);578if (insertedNode) {579/* It is either found or inserted into srp hashtable */580if (IS_NEW_ELEMENT(insertedNode)) {581/* A new node is inserted */582UNMARK_NEW_ELEMENT(insertedNode, J9SharedInternSRPHashTableEntry *);583insertedNode->prevNode = 0;584/* Fix the pointers and fill out the inserted shared node */585J9INTERN_ENTRY_SET_NEXTNODE(insertedNode, table->headNode);586if (NULL == table->tailNode) {587table->tailNode = insertedNode;588} else {589J9INTERN_ENTRY_SET_PREVNODE(table->headNode, insertedNode);590}591table->headNode = insertedNode;592SRP_SET(insertedNode->utf8SRP, utf8);593insertedNode->internWeight = internWeight;594insertedNode->flags = flags;595/* update the table */596++*(table->totalSharedNodesPtr);597*(table->totalSharedWeightPtr) += internWeight;598} else {599/* Existing node is found */600if (promoteIfExistingFound) {601promoteSharedNodeToHead(table, insertedNode);602}603}604}605return insertedNode;606}607608void609StringInternTable::deleteSharedNode(J9SharedInvariantInternTable *table, J9SharedInternSRPHashTableEntry *node)610{611removeSharedNodeFromList(table, node);612U_16 weight = node->internWeight;613J9UTF8 * utf8ToDelete = SRP_GET(node->utf8SRP, J9UTF8 *);614J9SharedInternHashTableQuery sharedQuery;615sharedQuery.length = J9UTF8_LENGTH(utf8ToDelete);616sharedQuery.data = J9UTF8_DATA(utf8ToDelete);617618U_32 deleted = srpHashTableRemove(table->sharedInvariantSRPHashtable, &sharedQuery);619if (0 == deleted) {620/* shared node removed - update stats */621--*(table->totalSharedNodesPtr);622*(table->totalSharedWeightPtr) -= weight;623}624return;625}626627void628StringInternTable::removeSharedNodeFromList(J9SharedInvariantInternTable *table, J9SharedInternSRPHashTableEntry *sharedNode)629{630Trc_BCU_Assert_True(NULL != sharedNode);631632J9SharedInternSRPHashTableEntry *prevNode = J9INTERN_ENTRY_GET_PREVNODE(sharedNode);633J9SharedInternSRPHashTableEntry *nextNode = J9INTERN_ENTRY_GET_NEXTNODE(sharedNode);634635if (NULL != prevNode) {636J9INTERN_ENTRY_SET_NEXTNODE(prevNode, nextNode);637}638if (NULL != nextNode) {639J9INTERN_ENTRY_SET_PREVNODE(nextNode, prevNode);640}641if (table->tailNode == sharedNode) {642table->tailNode = prevNode;643}644if (table->headNode == sharedNode) {645table->headNode = nextNode;646}647}648649/**650* Calculate the bytes required to write a UTF8 with the specified length into memory.651* @param length Length of the UTF8652* @return U_16 space required in the memory to write the UTF8 with specified length653*/654UDATA655StringInternTable::getRequiredBytesForUTF8Length(U_16 length)656{657UDATA bytesRequiredByUTF8 = sizeof(U_16);658bytesRequiredByUTF8 += length;659if (0 != (length & 1)) {660bytesRequiredByUTF8 += 1;661}662return bytesRequiredByUTF8;663}664665/**666* Increment a local node's weight when it's used.667* @param[in] node The local node.668*/669void670StringInternTable::updateLocalNodeWeight(J9InternHashTableEntry *node)671{672if (node->internWeight == MAX_INTERN_NODE_WEIGHT) {673return;674}675676UDATA bytesRequiredByUTF8 = getRequiredBytesForUTF8Length(J9UTF8_LENGTH(node->utf8));677if ((node->internWeight + bytesRequiredByUTF8) >= MAX_INTERN_NODE_WEIGHT) {678node->internWeight = MAX_INTERN_NODE_WEIGHT;679} else {680node->internWeight += (U_16)bytesRequiredByUTF8;681}682}683684/**685* Increment a shared node's weight when it's used.686* The shared table's weight is also incremented.687* @param[in] table The invariantInternTable.688* @param[in] sharedNode The shared node.689*/690void691StringInternTable::updateSharedNodeWeight(J9SharedInvariantInternTable *table, J9SharedInternSRPHashTableEntry *sharedNode)692{693J9UTF8 *utf8 = J9SHAREDINTERNSRPHASHTABLEENTRY_UTF8SRP(sharedNode);694UDATA bytesRequiredByUTF8 = getRequiredBytesForUTF8Length(J9UTF8_LENGTH(utf8));695if (sharedNode->internWeight != MAX_INTERN_NODE_WEIGHT) {696if ((sharedNode->internWeight + bytesRequiredByUTF8) >= MAX_INTERN_NODE_WEIGHT) {697sharedNode->internWeight = MAX_INTERN_NODE_WEIGHT;698} else {699sharedNode->internWeight += (U_16)bytesRequiredByUTF8;700}701}702703*(table->totalSharedWeightPtr) += (U_32)bytesRequiredByUTF8;704}705706/**707* Tests whether a local node should be promoted to the shared tree.708* @param[in] table The invariantInternTable.709* @param[in] node The local node to be tested.710* @param[in] sharedNodeToDisplace The shared node to displace, not null.711* @retval false The node should not be promoted into the shared table.712* @retval true The node should be promoted into the shared table.713*/714bool715StringInternTable::testNodePromotionWeight(J9SharedInvariantInternTable *table, J9InternHashTableEntry *node, J9SharedInternSRPHashTableEntry *sharedNodeToDisplace)716{717/* Only allow a node into the shared tree IF:718* - If J9AVLTREE_DISABLE_SHARED_TREE_UPDATES is not set719* - It is STRINGINTERNTABLENODE_FLAG_UTF8_IS_SHARED. This means that the string it points to is shared and has been successfully relocated.720* - If the intern weight of the node to be promoted is greater than the shared node being displaced,721* - or if the node has a greater intern weight of > 100,722*/723return (0 == (table->flags & J9AVLTREE_DISABLE_SHARED_TREE_UPDATES)) &&724(0 != (node->flags & STRINGINTERNTABLES_NODE_FLAG_UTF8_IS_SHARED)) &&725((node->internWeight > 100) || (node->internWeight > sharedNodeToDisplace->internWeight));726}727728void729StringInternTable::swapLocalNodeWithTailSharedNode(J9InternHashTableEntry *node, J9SharedInvariantInternTable *table)730{731/* No need to check for J9AVLTREE_DISABLE_SHARED_TREE_UPDATES as this is tested in testNodePromotionWeight() */732J9InternHashTableEntry localNodeToInsert;733J9SharedInternSRPHashTableEntry *sharedTail = table->tailNode;734/* Copy shared tail node data*/735localNodeToInsert.utf8 = SRP_GET(sharedTail->utf8SRP, J9UTF8*);736localNodeToInsert.flags = (sharedTail->flags);737localNodeToInsert.internWeight = sharedTail->internWeight;738localNodeToInsert.classLoader = table->systemClassLoader;739740deleteSharedNode(table, table->tailNode);741742J9SharedInternSRPHashTableEntry * insertedSharedNode = insertSharedNode(table, node->utf8, node->internWeight, node->flags, FALSE);743deleteLocalNode(node);744745/* Copy data from shared node to a local node and insert the local node into the hash table. */746J9InternHashTableEntry *entry = insertLocalNode(&localNodeToInsert, /* promoteIfExistingFound = */ false);747if (NULL == entry) {748/* Shared node matched an existing node in the local table and was not inserted. Decrement local count. */749_nodeCount--;750}751}752753void754StringInternTable::promoteSharedNodeToHead(J9SharedInvariantInternTable *table, J9SharedInternSRPHashTableEntry *node)755{756J9SharedInternSRPHashTableEntry *headNode;757J9SharedInternSRPHashTableEntry **tableHeadNodePtr = &(table->headNode);758759headNode = *tableHeadNodePtr;760761if (headNode != node) {762J9SharedInternSRPHashTableEntry *prevNode;763J9SharedInternSRPHashTableEntry *nextNode;764765prevNode = J9INTERN_ENTRY_GET_PREVNODE(node);766nextNode = J9INTERN_ENTRY_GET_NEXTNODE(node);767768if (prevNode) {769/* unlink this node from the previous node */770J9INTERN_ENTRY_SET_NEXTNODE(prevNode, nextNode);771}772if (nextNode) {773/* unlink this node from the next node */774J9INTERN_ENTRY_SET_PREVNODE(nextNode, prevNode);775}776777J9INTERN_ENTRY_SET_PREVNODE(node, 0);778J9INTERN_ENTRY_SET_NEXTNODE(node, headNode);779780if (headNode) {781J9INTERN_ENTRY_SET_PREVNODE(headNode, node);782}783*tableHeadNodePtr = node;784785if (NULL == table->tailNode) {786table->tailNode = node;787} else if (node == table->tailNode) {788if (prevNode != NULL) {789table->tailNode = prevNode;790}791}792}793794return;795}796797#endif /* J9VM_OPT_SHARED_CLASSES */798799800