Path: blob/master/runtime/compiler/control/JITServerCompilationThread.cpp
6000 views
/*******************************************************************************1* Copyright (c) 2018, 2022 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*******************************************************************************/2122#include "control/JITServerCompilationThread.hpp"2324#include "codegen/CodeGenerator.hpp"25#include "control/CompilationRuntime.hpp"26#include "control/MethodToBeCompiled.hpp"27#include "control/JITServerHelpers.hpp"28#include "env/ClassTableCriticalSection.hpp"29#include "env/VMAccessCriticalSection.hpp"30#include "env/JITServerAllocationRegion.hpp"31#include "env/JITServerPersistentCHTable.hpp"32#include "env/SystemSegmentProvider.hpp"33#include "env/VerboseLog.hpp"34#include "env/ut_j9jit.h"35#include "runtime/CodeCache.hpp"36#include "runtime/CodeCacheExceptions.hpp"37#include "runtime/J9VMAccess.hpp"38#include "runtime/RelocationTarget.hpp"39#include "net/ClientStream.hpp"40#include "net/ServerStream.hpp"41#include "jitprotos.h"42#include "vmaccess.h"4344/**45* @brief Helper method executed at the end of a compilation46* to determine whether the server is running out of memory.47*/48JITServer::ServerMemoryState49computeServerMemoryState(TR::CompilationInfo *compInfo)50{51// Compute LOW memory threshold relative to the number of clients but cap it at 1652// to avoid wasting memory in cases when clients disconnect without notifying the server53size_t numClients = compInfo->getClientSessionHT()->size();54numClients = numClients > 16 ? 16 : numClients;55uint64_t lowMemoryThreshold = TR::Options::getSafeReservePhysicalMemoryValue() + (numClients + 4) * TR::Options::getScratchSpaceLowerBound();56uint64_t veryLowMemoryThreshold = TR::Options::getSafeReservePhysicalMemoryValue() + 4 * TR::Options::getScratchSpaceLowerBound();5758uint64_t freePhysicalMemorySizeB = compInfo->getCachedFreePhysicalMemoryB();5960// If the last measurement was LOW or VERY_LOW, sample memory at a higher rate to61// get more up-to-date information62JITServer::ServerMemoryState memoryState = JITServer::ServerMemoryState::NORMAL;63int64_t updatePeriodMs = -1;64if (freePhysicalMemorySizeB != OMRPORT_MEMINFO_NOT_AVAILABLE)65{66if (freePhysicalMemorySizeB <= veryLowMemoryThreshold)67updatePeriodMs = 50;68else if (freePhysicalMemorySizeB <= lowMemoryThreshold)69updatePeriodMs = 250;70}71bool incompleteInfo;72freePhysicalMemorySizeB = compInfo->computeAndCacheFreePhysicalMemory(incompleteInfo, updatePeriodMs);7374if (freePhysicalMemorySizeB != OMRPORT_MEMINFO_NOT_AVAILABLE)75{76memoryState = freePhysicalMemorySizeB <= veryLowMemoryThreshold ?77JITServer::ServerMemoryState::VERY_LOW :78(freePhysicalMemorySizeB <= lowMemoryThreshold ?79JITServer::ServerMemoryState::LOW :80JITServer::ServerMemoryState::NORMAL);81return memoryState;82}83// memory info not available, return the default state84return JITServer::ServerMemoryState::NORMAL;85}8687/**88* @brief Helper method executed at the end of a compilation89* to determine whether the server is approaching the maximum number of active threads.90*/91JITServer::ServerActiveThreadsState92computeServerActiveThreadsState(TR::CompilationInfo *compInfo)93{94int32_t highActiveThreadThreshold = TR::Options::getHighActiveThreadThreshold();95int32_t veryHighActiveThreadThreshold = TR::Options::getVeryHighActiveThreadThreshold();9697// Typically getNumCompThreadsActive() needs to be protected by the compilationQueueMonitor,98// except here we allow some small imprecision because we implement a heuristic.99int32_t numOfActiveThreads = compInfo->getNumCompThreadsActive();100101JITServer::ServerActiveThreadsState activeThreadState = numOfActiveThreads > veryHighActiveThreadThreshold ?102JITServer::ServerActiveThreadsState::VERY_HIGH_THREAD :103(numOfActiveThreads > highActiveThreadThreshold ?104JITServer::ServerActiveThreadsState::HIGH_THREAD :105JITServer::ServerActiveThreadsState::NORMAL_THREAD);106return activeThreadState;107}108109/**110* @brief Method executed by JITServer to process the end of a compilation.111*/112void113outOfProcessCompilationEnd(TR_MethodToBeCompiled *entry, TR::Compilation *comp)114{115entry->_tryCompilingAgain = false; // TODO: Need to handle recompilations gracefully when relocation fails116auto compInfoPT = (TR::CompilationInfoPerThreadRemote *)entry->_compInfoPT;117118TR::CodeCache *codeCache = comp->cg()->getCodeCache();119120TR_ASSERT(comp->getAotMethodDataStart(), "The header must have been set");121TR_AOTMethodHeader *aotMethodHeaderEntry = comp->getAotMethodHeaderEntry();122123uint8_t *codeStart = (uint8_t *)aotMethodHeaderEntry->compileMethodCodeStartPC;124OMR::CodeCacheMethodHeader *codeCacheHeader = (OMR::CodeCacheMethodHeader *)codeStart;125126TR_DataCache *dataCache = (TR_DataCache *)comp->getReservedDataCache();127TR_ASSERT(dataCache, "A dataCache must be reserved for JITServer compilations");128J9JITDataCacheHeader *dataCacheHeader = (J9JITDataCacheHeader *)comp->getAotMethodDataStart();129130size_t codeSize = codeCache->getWarmCodeAlloc() - (uint8_t *)codeCacheHeader;131size_t dataSize = dataCache->getSegment()->heapAlloc - (uint8_t *)dataCacheHeader;132133std::string codeCacheStr((const char *)codeCacheHeader, codeSize);134std::string dataCacheStr((const char *)dataCacheHeader, dataSize);135136CHTableCommitData chTableData;137if (!comp->getOption(TR_DisableCHOpts) && !entry->_useAotCompilation)138{139TR_CHTable *chTable = comp->getCHTable();140chTableData = chTable->computeDataForCHTableCommit(comp);141}142143auto classesThatShouldNotBeNewlyExtended = compInfoPT->getClassesThatShouldNotBeNewlyExtended();144145// Pack log file to send to client146std::string logFileStr = TR::Options::packLogFile(comp->getOutFile());147148std::string svmValueToSymbolStr;149if (comp->getOption(TR_UseSymbolValidationManager))150{151svmValueToSymbolStr = comp->getSymbolValidationManager()->serializeValueToSymbolMap();152}153154// Send runtime assumptions created during compilation to the client155std::vector<SerializedRuntimeAssumption> serializedRuntimeAssumptions;156if (comp->getSerializedRuntimeAssumptions().size() > 0)157{158serializedRuntimeAssumptions.reserve(comp->getSerializedRuntimeAssumptions().size());159for (auto it : comp->getSerializedRuntimeAssumptions())160{161serializedRuntimeAssumptions.push_back(*it);162}163}164165auto resolvedMirrorMethodsPersistIPInfo = compInfoPT->getCachedResolvedMirrorMethodsPersistIPInfo();166167JITServer::ServerMemoryState memoryState = computeServerMemoryState(compInfoPT->getCompilationInfo());168JITServer::ServerActiveThreadsState activeThreadState = computeServerActiveThreadsState(compInfoPT->getCompilationInfo());169170// Send methods requring resolved trampolines in this compilation to the client171std::vector<TR_OpaqueMethodBlock *> methodsRequiringTrampolines;172if (comp->getMethodsRequiringTrampolines().size() > 0)173{174methodsRequiringTrampolines.reserve(comp->getMethodsRequiringTrampolines().size());175for (auto it : comp->getMethodsRequiringTrampolines())176{177methodsRequiringTrampolines.push_back(it);178}179}180181entry->_stream->finishCompilation(182codeCacheStr, dataCacheStr, chTableData,183std::vector<TR_OpaqueClassBlock*>(classesThatShouldNotBeNewlyExtended->begin(), classesThatShouldNotBeNewlyExtended->end()),184logFileStr, svmValueToSymbolStr,185resolvedMirrorMethodsPersistIPInfo186? std::vector<TR_ResolvedJ9Method*>(resolvedMirrorMethodsPersistIPInfo->begin(), resolvedMirrorMethodsPersistIPInfo->end())187: std::vector<TR_ResolvedJ9Method*>(),188*entry->_optimizationPlan, serializedRuntimeAssumptions, memoryState, activeThreadState, methodsRequiringTrampolines189);190compInfoPT->clearPerCompilationCaches();191192if (TR::Options::getVerboseOption(TR_VerboseJITServer))193{194TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer, "compThreadID=%d has successfully compiled %s memoryState=%d",195compInfoPT->getCompThreadId(), compInfoPT->getCompilation()->signature(), memoryState);196}197198Trc_JITServerCompileEnd(compInfoPT->getCompilationThread(), compInfoPT->getCompThreadId(),199compInfoPT->getCompilation()->signature(), compInfoPT->getCompilation()->getHotnessName());200201if (compInfoPT->isAOTCacheStore())202{203if (comp->isAOTCacheStore())204{205auto clientData = comp->getClientData();206auto cache = clientData->getAOTCache();207cache->storeMethod(compInfoPT->getDefiningClassChainRecord(), compInfoPT->getMethodIndex(),208entry->_optimizationPlan->getOptLevel(), clientData->getAOTHeaderRecord(),209comp->getSerializationRecords(), codeCacheHeader, codeSize,210dataCacheHeader, dataSize, comp->signature(), clientData->getClientUID());211}212else if (TR::Options::getVerboseOption(TR_VerboseJITServer))213{214TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer, "Failed to serialize AOT method %s", comp->signature());215}216}217}218219TR::CompilationInfoPerThreadRemote::CompilationInfoPerThreadRemote(TR::CompilationInfo &compInfo, J9JITConfig *jitConfig, int32_t id, bool isDiagnosticThread)220: CompilationInfoPerThread(compInfo, jitConfig, id, isDiagnosticThread),221_recompilationMethodInfo(NULL),222_seqNo(0),223_waitToBeNotified(false),224_clientOptions(NULL),225_clientOptionsSize(0),226_methodIPDataPerComp(NULL),227_resolvedMethodInfoMap(NULL),228_resolvedMirrorMethodsPersistIPInfo(NULL),229_classOfStaticMap(NULL),230_fieldAttributesCache(NULL),231_staticAttributesCache(NULL),232_isUnresolvedStrCache(NULL),233_classUnloadReadMutexDepth(0),234_aotCacheStore(false),235_methodIndex((uint32_t)-1),236_definingClassChainRecord(NULL)237{}238239/**240* @brief Method executed by JITServer to dequeue and notify all waiting threads241* that the condition they were waiting for has been fulfilled.242* Needs to be executed with clientSession->getSequencingMonitor() in hand.243*/244void245TR::CompilationInfoPerThreadRemote::notifyAndDetachWaitingRequests(ClientSessionData *clientSession)246{247TR_MethodToBeCompiled *nextEntry = clientSession->getOOSequenceEntryList();248// I need to keep notifying until the first request that is blocked249// because the request it depends on hasn't been processed yet.250while (nextEntry)251{252uint32_t nextWaitingSeqNo = ((CompilationInfoPerThreadRemote*)(nextEntry->_compInfoPT))->getSeqNo();253uint32_t expectedSeqNo = ((CompilationInfoPerThreadRemote*)(nextEntry->_compInfoPT))->getExpectedSeqNo();254if (expectedSeqNo <= clientSession->getLastProcessedCriticalSeqNo())255{256clientSession->notifyAndDetachFirstWaitingThread();257if (TR::Options::getVerboseOption(TR_VerboseJITServer))258TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer, "compThreadID=%d notifying out-of-sequence thread %d for clientUID=%llu seqNo=%u (entry=%p)",259getCompThreadId(), nextEntry->_compInfoPT->getCompThreadId(), (unsigned long long)clientSession->getClientUID(), nextWaitingSeqNo, nextEntry);260nextEntry = clientSession->getOOSequenceEntryList();261}262else // The request I depend on hasn't been processed yet, so stop here263{264break;265}266}267}268269int32_t TR::CompilationInfoPerThreadRemote::_numClearedCaches = 0;270271/**272* @brief Method executed by a compilation thread at JITServer to wait for all273* previous compilation requests it depends on to be processed.274* Needs to be executed with sequencingMonitor in hand.275*/276void277TR::CompilationInfoPerThreadRemote::waitForMyTurn(ClientSessionData *clientSession, TR_MethodToBeCompiled &entry)278{279TR_ASSERT(getMethodBeingCompiled() == &entry, "Must have stored the entry to be compiled into compInfoPT");280TR_ASSERT(entry._compInfoPT == this, "Must have stored compInfoPT into the entry to be compiled");281uint32_t seqNo = getSeqNo();282uint32_t criticalSeqNo = getExpectedSeqNo(); // This is the seqNo that I must wait for283284// Insert this thread into the list of out of sequence entries285JITServerHelpers::insertIntoOOSequenceEntryList(clientSession, &entry);286287do // Do a timed wait until the missing seqNo arrives288{289// Always reset _waitToBeNotified before waiting on the monitor290// If a notification does not arrive, this request will timeout and possibly clear the caches291setWaitToBeNotified(false);292293entry.getMonitor()->enter();294clientSession->getSequencingMonitor()->exit(); // release monitor before waiting295const int64_t waitTimeMillis = 1000; // TODO: create an option for this296if (TR::Options::getVerboseOption(TR_VerboseJITServer))297TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer, "compThreadID=%d (entry=%p) doing a timed wait for %d ms (waiting for seqNo=%u)",298getCompThreadId(), &entry, (int32_t)waitTimeMillis, criticalSeqNo);299300Trc_JITServerTimedWait(getCompilationThread(), getCompThreadId(), clientSession,301(unsigned long long)clientSession->getClientUID(), &entry,302seqNo, criticalSeqNo, clientSession->getNumActiveThreads(), (int32_t)waitTimeMillis);303304intptr_t monitorStatus = entry.getMonitor()->wait_timed(waitTimeMillis, 0); // 0 or J9THREAD_TIMED_OUT305if (monitorStatus == 0) // Thread was notified306{307entry.getMonitor()->exit();308clientSession->getSequencingMonitor()->enter();309310if (TR::Options::getVerboseOption(TR_VerboseJITServer))311TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer, "compThreadID=%d (entry=%p) is parked. seqNo=%u was notified",312getCompThreadId(), &entry, seqNo);313// Will verify condition again to see if expectedSeqNo has advanced enough314315Trc_JITServerParkThread(getCompilationThread(), getCompThreadId(), clientSession,316(unsigned long long)clientSession->getClientUID(), &entry,317seqNo, criticalSeqNo, clientSession->getNumActiveThreads(), seqNo);318}319else // Timeout320{321entry.getMonitor()->exit();322TR_ASSERT(monitorStatus == J9THREAD_TIMED_OUT, "Unexpected monitor state");323if (TR::Options::isAnyVerboseOptionSet(TR_VerboseCompFailure, TR_VerboseJITServer, TR_VerbosePerformance))324TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer, "compThreadID=%d (entry=%p) timed-out while waiting for seqNo=%u ",325getCompThreadId(), &entry, criticalSeqNo);326327Trc_JITServerTimedOut(getCompilationThread(), getCompThreadId(), clientSession,328(unsigned long long)clientSession->getClientUID(), &entry,329seqNo, criticalSeqNo, clientSession->getNumActiveThreads(), criticalSeqNo);330331// The simplest thing is to delete the cached data for the session and start fresh332// However, we must wait for any active threads to drain out333clientSession->getSequencingMonitor()->enter();334335// This thread could have been waiting, then timed-out and then waited336// to acquire the sequencing monitor. Check whether it can proceed.337if (criticalSeqNo <= clientSession->getLastProcessedCriticalSeqNo())338{339// The entry cannot be in the list340TR_MethodToBeCompiled *headEntry = clientSession->getOOSequenceEntryList();341if (headEntry)342{343uint32_t headSeqNo = ((TR::CompilationInfoPerThreadRemote*)(headEntry->_compInfoPT))->getSeqNo();344TR_ASSERT_FATAL(seqNo < headSeqNo, "Next in line method cannot be in the waiting list: seqNo=%u >= headSeqNo=%u entry=%p headEntry=%p",345seqNo, headSeqNo, &entry, headEntry);346}347break; // It's my turn, so proceed348}349350if (clientSession->getNumActiveThreads() <= 0 && // Wait for active threads to quiesce351&entry == clientSession->getOOSequenceEntryList() && // Allow only the smallest seqNo which is the head352!getWaitToBeNotified()) // Avoid a cohort of threads clearing the caches353{354clientSession->clearCaches();355incNumClearedCaches();356357if (TR::Options::getVerboseOption(TR_VerboseJITServer))358TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer,359"compThreadID=%d has cleared the session caches for clientUID=%llu criticalSeqNo=%u seqNo=%u firstEntry=%p",360getCompThreadId(), clientSession->getClientUID(), criticalSeqNo, seqNo, &entry);361362Trc_JITServerClearedSessionCaches(getCompilationThread(), getCompThreadId(), clientSession,363(unsigned long long)clientSession->getClientUID(),364seqNo, criticalSeqNo, clientSession->getNumActiveThreads(), &entry,365clientSession->getLastProcessedCriticalSeqNo(), seqNo);366367clientSession->setLastProcessedCriticalSeqNo(criticalSeqNo);// Allow myself to go through368notifyAndDetachWaitingRequests(clientSession);369// Mark the next request that it should not try to clear the caches,370// but rather to sleep again waiting for my notification.371// We only need to do this for the head entry in the waiting list372// due to the check `&entry == clientSession->getOOSequenceEntryList()` above373TR_MethodToBeCompiled *nextWaitingEntry = clientSession->getOOSequenceEntryList();374if (nextWaitingEntry)375{376((TR::CompilationInfoPerThreadRemote*)(nextWaitingEntry->_compInfoPT))->setWaitToBeNotified(true);377}378}379else380{381// Wait until all active threads have been drained382// and the head of the list has cleared the caches383if (TR::Options::getVerboseOption(TR_VerboseJITServer))384TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer,385"compThreadID=%d which previously timed-out will go to sleep again. Possible reasons numActiveThreads=%d waitToBeNotified=%d",386getCompThreadId(), clientSession->getNumActiveThreads(), getWaitToBeNotified());387388Trc_JITServerThreadGoSleep(getCompilationThread(), getCompThreadId(), clientSession,389(unsigned long long)clientSession->getClientUID(),390seqNo, criticalSeqNo, clientSession->getNumActiveThreads(), getWaitToBeNotified());391}392}393} while (criticalSeqNo > clientSession->getLastProcessedCriticalSeqNo());394}395396bool397TR::CompilationInfoPerThreadRemote::serveCachedAOTMethod(TR_MethodToBeCompiled &entry, J9Method *method, J9Class *definingClass,398TR_OptimizationPlan *optPlan, ClientSessionData *clientData,399J9::J9SegmentProvider &scratchSegmentProvider)400{401auto aotCache = clientData->getAOTCache();402auto serializedMethod = aotCache->findMethod(_definingClassChainRecord, _methodIndex,403optPlan->getOptLevel(), clientData->getAOTHeaderRecord());404if (!serializedMethod)405return false;406407size_t segmentSize = scratchSegmentProvider.getPreferredSegmentSize();408if (!segmentSize)409segmentSize = 1 << 24/*16 MB*/;410TR::RawAllocator rawAllocator(getCompilationThread()->javaVM);411J9::SystemSegmentProvider segmentProvider(1 << 16/*64 KB*/, segmentSize, TR::Options::getScratchSpaceLimit(),412scratchSegmentProvider, rawAllocator);413TR::Region region(segmentProvider, rawAllocator);414TR_Memory trMemory(*clientData->persistentMemory(), region);415416VectorAllocator<const AOTSerializationRecord *> recordsAllocator(trMemory.heapMemoryRegion());417Vector<const AOTSerializationRecord *> records(recordsAllocator);418{419OMR::CriticalSection cs(clientData->getAOTCacheKnownIdsMonitor());420records = aotCache->getSerializationRecords(serializedMethod, clientData->getAOTCacheKnownIds(), trMemory);421}422423std::vector<std::string> serializedRecords;424serializedRecords.reserve(records.size());425for (auto r : records)426serializedRecords.push_back(std::string((const char *)r, r->size()));427428//NOTE: Leaving optimization plan unchanged. This can be changed in the future.429entry._stream->write(JITServer::MessageType::AOTCache_serializedAOTMethod,430std::string((const char *)&serializedMethod->data(), serializedMethod->data().size()),431serializedRecords, *optPlan, computeServerMemoryState(getCompilationInfo()),432computeServerActiveThreadsState(getCompilationInfo()));433434return true;435}436437/**438* @brief Method executed by JITServer to process the compilation request.439*/440void441TR::CompilationInfoPerThreadRemote::processEntry(TR_MethodToBeCompiled &entry, J9::J9SegmentProvider &scratchSegmentProvider)442{443static bool enableJITServerPerCompConn = feGetEnv("TR_EnableJITServerPerCompConn") ? true : false;444445bool abortCompilation = false;446bool deleteStream = false;447uint64_t clientId = 0;448TR::CompilationInfo *compInfo = getCompilationInfo();449J9VMThread *compThread = getCompilationThread();450JITServer::ServerStream *stream = entry._stream;451setMethodBeingCompiled(&entry); // Must have compilation monitor452entry._compInfoPT = this; // Create the reverse link453// Update the last time the compilation thread had to do something.454compInfo->setLastReqStartTime(compInfo->getPersistentInfo()->getElapsedTime());455clearPerCompilationCaches();456457_recompilationMethodInfo = NULL;458// Release compMonitor before doing the blocking read459compInfo->releaseCompMonitor(compThread);460461char *clientOptions = NULL;462TR_OptimizationPlan *optPlan = NULL;463_vm = NULL;464bool useAotCompilation = false;465uint32_t seqNo = 0;466ClientSessionData *clientSession = NULL;467bool isCriticalRequest = false;468// numActiveThreads is incremented after it waits for its turn to execute469// and before the thread processes unloaded classes and CHTable init and update.470// A stream exception could be thrown at any time such as reading the compilation471// request (numActiveThreads hasn't been incremented), or an exeption could be472// thrown when a message such as MessageType::getUnloadedClassRangesAndCHTable473// is sent to the client (numActiveThreads has been incremented).474// hasIncNumActiveThreads is used to determine if decNumActiveThreads() should be475// called when an exception is thrown.476bool hasIncNumActiveThreads = false;477// Keep track of whether the lastProcessedCriticalSeqNo in the client session was updated478bool hasUpdatedSeqNo = false;479bool aotCacheHit = false;480481_aotCacheStore = false;482_methodIndex = (uint32_t)-1;483_definingClassChainRecord = NULL;484485try486{487auto req = stream->readCompileRequest<488uint64_t, uint32_t, uint32_t, J9Method *, J9Class *, TR_OptimizationPlan, std::string,489J9::IlGeneratorMethodDetailsType, std::vector<TR_OpaqueClassBlock *>, std::vector<TR_OpaqueClassBlock *>,490JITServerHelpers::ClassInfoTuple, std::string, std::string, std::string, std::string,491bool, bool, bool, uint32_t, uintptr_t *, std::vector<J9Class *>, std::vector<J9Class *>,492std::vector<JITServerHelpers::ClassInfoTuple>, std::vector<uintptr_t>493>();494495clientId = std::get<0>(req);496seqNo = std::get<1>(req); // Sequence number at the client497uint32_t criticalSeqNo = std::get<2>(req); // Sequence number of the request this request depends upon498J9Method *ramMethod = std::get<3>(req);499J9Class *clazz = std::get<4>(req);500auto &clientOptPlan = std::get<5>(req);501auto &detailsStr = std::get<6>(req);502auto detailsType = std::get<7>(req);503auto &unloadedClasses = std::get<8>(req);504auto &illegalModificationList = std::get<9>(req);505auto &classInfoTuple = std::get<10>(req);506auto &clientOptStr = std::get<11>(req);507auto &recompInfoStr = std::get<12>(req);508auto &chtableUnloads = std::get<13>(req);509auto &chtableMods = std::get<14>(req);510useAotCompilation = std::get<15>(req);511bool isInStartupPhase = std::get<16>(req);512bool aotCacheLoad = std::get<17>(req);513_methodIndex = std::get<18>(req);514uintptr_t *classChain = std::get<19>(req);515auto &ramClassChain = std::get<20>(req);516auto &uncachedRAMClasses = std::get<21>(req);517auto &uncachedClassInfos = std::get<22>(req);518auto &newKnownIds = std::get<23>(req);519520TR_ASSERT_FATAL(TR::Compiler->persistentMemory() == compInfo->persistentMemory(),521"per-client persistent memory must not be set at this point");522523TR::IlGeneratorMethodDetails *clientDetails = (TR::IlGeneratorMethodDetails *)detailsStr.data();524*(uintptr_t *)clientDetails = 0; // smash remote vtable pointer to catch bugs early525TR::IlGeneratorMethodDetails serverDetailsStorage;526TR::IlGeneratorMethodDetails *serverDetails = TR::IlGeneratorMethodDetails::clone(serverDetailsStorage, *clientDetails, detailsType);527528isCriticalRequest =529(!chtableMods.empty()530|| !chtableUnloads.empty()531|| !illegalModificationList.empty()532|| !unloadedClasses.empty())533&& !serverDetails->isJitDumpMethod(); // if this is a JitDump recompilation, ignore any critical updates534535if (useAotCompilation)536{537_vm = TR_J9VMBase::get(_jitConfig, compThread, TR_J9VMBase::J9_SHARED_CACHE_SERVER_VM);538}539else540{541_vm = TR_J9VMBase::get(_jitConfig, compThread, TR_J9VMBase::J9_SERVER_VM);542}543544if (_vm->sharedCache())545// Set/update stream pointer in shared cache.546// Note that if remote-AOT is enabled, even regular J9_SERVER_VM will have a shared cache547// This behaviour is consistent with non-JITServer548((TR_J9JITServerSharedCache *) _vm->sharedCache())->setStream(stream);549550//if (seqNo == 501)551// throw JITServer::StreamFailure(); // stress testing552553stream->setClientId(clientId);554setSeqNo(seqNo); // Memorize the sequence number of this request555setExpectedSeqNo(criticalSeqNo); // Memorize the message I have to wait for556557bool sessionDataWasEmpty = false;558{559// Get a pointer to this client's session data560// Obtain monitor RAII style because creating a new hastable entry may throw bad_alloc561OMR::CriticalSection compilationMonitorLock(compInfo->getCompilationMonitor());562compInfo->getClientSessionHT()->purgeOldDataIfNeeded(); // Try to purge old data563if (!(clientSession = compInfo->getClientSessionHT()->findOrCreateClientSession(clientId, criticalSeqNo, &sessionDataWasEmpty, _jitConfig)))564throw std::bad_alloc();565566setClientData(clientSession); // Cache the session data into CompilationInfoPerThreadRemote object567568// After this line, all persistent allocations are made per-client,569// until exitPerClientAllocationRegion is called570enterPerClientAllocationRegion();571TR_ASSERT(!clientSession->usesPerClientMemory() || (TR::Compiler->persistentMemory() != compInfo->persistentMemory()),572"per-client persistent memory must be set at this point");573574clientSession->setIsInStartupPhase(isInStartupPhase);575} // End critical section576577if (TR::Options::getVerboseOption(TR_VerboseJITServer))578TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer,579"compThreadID=%d %s clientSessionData=%p for clientUID=%llu seqNo=%u (isCritical=%d) (criticalSeqNo=%u lastProcessedCriticalReq=%u)",580getCompThreadId(), sessionDataWasEmpty ? "created" : "found", clientSession, (unsigned long long)clientId, seqNo,581isCriticalRequest, criticalSeqNo, clientSession->getLastProcessedCriticalSeqNo());582583Trc_JITServerClientSessionData1(compThread, getCompThreadId(), sessionDataWasEmpty ? "created" : "found",584clientSession, (unsigned long long)clientId, seqNo, isCriticalRequest, criticalSeqNo, clientSession->getLastProcessedCriticalSeqNo());585586if (!newKnownIds.empty())587{588OMR::CriticalSection cs(clientSession->getAOTCacheKnownIdsMonitor());589clientSession->getAOTCacheKnownIds().insert(newKnownIds.begin(), newKnownIds.end());590}591592// We must process unloaded classes lists in the same order they were generated at the client593// Use a sequencing scheme to re-order compilation requests594//595clientSession->getSequencingMonitor()->enter();596clientSession->updateMaxReceivedSeqNo(seqNo); // TODO: why do I need this?597598// This request can go through as long as criticalSeqNo has been processed599if (criticalSeqNo > clientSession->getLastProcessedCriticalSeqNo())600{601// Park this request until `criticalSeqNo` arrives and is processed602if (TR::Options::getVerboseOption(TR_VerboseJITServer))603TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer, "compThreadID=%d out-of-sequence msg=%u detected for clientUID=%llu criticalSeqNo=%u > lastCriticalSeqNo=%u. Parking this thread (entry=%p)",604getCompThreadId(), seqNo, (unsigned long long)clientId, criticalSeqNo, clientSession->getLastProcessedCriticalSeqNo(), &entry);605606Trc_JITServerOutOfSequenceMsg1(compThread, getCompThreadId(), clientSession, (unsigned long long)clientId, &entry, seqNo,607clientSession->getLastProcessedCriticalSeqNo(), clientSession->getNumActiveThreads(), clientSession->getLastProcessedCriticalSeqNo());608609waitForMyTurn(clientSession, entry);610}611612TR_ASSERT_FATAL(criticalSeqNo <= clientSession->getLastProcessedCriticalSeqNo(),613"Critical requests must be processed in order: compThreadID=%d seqNo=%u criticalSeqNo=%u > lastProcessedCriticalSeqNo=%u clientUID=%llu",614getCompThreadId(), seqNo, criticalSeqNo, clientSession->getLastProcessedCriticalSeqNo(), (unsigned long long)clientId);615616if (criticalSeqNo < clientSession->getLastProcessedCriticalSeqNo())617{618// It's possible that a critical request is delayed so much that we clear the caches, start over,619// and then the missing request arrives. At this point we should ignore it, so we throw StreamOOO.620// Another possibility is when request 1 arrives before request 0 and creates the session, setting621// lastProcessedCriticalSeqNo to 1. As request 0 is "critical" by definition, we throw StreamOOO.622// Request 0 will be aborted (retried at te client) while request 1 will ask about entire CHTable623// because CHTable is empty at the server624if (isCriticalRequest)625{626if (TR::Options::isAnyVerboseOptionSet(TR_VerboseCompFailure, TR_VerboseJITServer, TR_VerbosePerformance))627TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer,628"compThreadID=%d discarding older msg for clientUID=%llu seqNo=%u criticalSeqNo=%u < lastProcessedCriticalSeqNo=%u",629getCompThreadId(), (unsigned long long)clientId, seqNo, criticalSeqNo, clientSession->getLastProcessedCriticalSeqNo());630631Trc_JITServerDiscardMessage(compThread, getCompThreadId(), clientSession,632(unsigned long long)clientId, seqNo, criticalSeqNo, clientSession->getNumActiveThreads());633634clientSession->getSequencingMonitor()->exit();635throw JITServer::StreamOOO();636}637}638639// Increment the number of active threads before issuing the read for ramClass640clientSession->incNumActiveThreads();641hasIncNumActiveThreads = true;642643// If class redefinition using HCR extensions occurred, must clear all the caches644bool mustClearCaches = std::find(unloadedClasses.begin(), unloadedClasses.end(), ClientSessionData::mustClearCachesFlag) != unloadedClasses.end();645if (mustClearCaches)646{647if (TR::Options::isAnyVerboseOptionSet(TR_VerboseCompFailure, TR_VerboseJITServer, TR_VerbosePerformance))648TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer,649"compThreadID=%d clearing all caches for clientUID=%llu due to class redefinition using HCR extensions",650getCompThreadId(), (unsigned long long) clientId);651Trc_JITServerClearCaches(compThread, getCompThreadId(), clientSession, (unsigned long long) clientId);652653clientSession->clearCachesLocked(_vm);654}655656// We can release the sequencing monitor now because no critical request with a657// larger sequence number can pass until I increment lastProcessedCriticalSeqNo658clientSession->getSequencingMonitor()->exit();659660// At this point I know that all preceeding requests have been processed661// and only one thread with critical information can ever be present in this section662if (!clientSession->cachesAreCleared())663{664// Free data for all classes that were unloaded for this sequence number665// Redefined classes are marked as unloaded, since they need to be cleared666// from the ROM class cache.667if (!unloadedClasses.empty())668{669clientSession->processUnloadedClasses(unloadedClasses, true); // this locks getROMMapMonitor()670}671672if (!illegalModificationList.empty())673{674clientSession->processIllegalFinalFieldModificationList(illegalModificationList); // this locks getROMMapMonitor()675}676677// Process the CHTable updates in order678// Note that applying the updates will acquire the CHTable monitor and VMAccess679if ((!chtableUnloads.empty() || !chtableMods.empty())680&& !serverDetails->isJitDumpMethod())681{682auto chTable = (JITServerPersistentCHTable*)clientSession->getCHTable(); // Will create CHTable if it doesn't exist683TR_ASSERT_FATAL(chTable->isInitialized(), "CHTable must have been initialized for clientUID=%llu", (unsigned long long)clientId);684chTable->doUpdate(_vm, chtableUnloads, chtableMods);685}686}687else // Internal caches are empty688{689OMR::CriticalSection cs(clientSession->getCacheInitMonitor());690if (clientSession->cachesAreCleared())691{692if (TR::Options::getVerboseOption(TR_VerboseJITServer))693TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer, "compThreadID=%d will ask for address ranges of unloaded classes and CHTable for clientUID %llu",694getCompThreadId(), (unsigned long long)clientId);695696stream->write(JITServer::MessageType::getUnloadedClassRangesAndCHTable, compInfo->getPersistentInfo()->getServerUID());697auto response = stream->read<std::vector<TR_AddressRange>, int32_t, std::string>();698// TODO: we could send JVM info that is global and does not change together with CHTable699auto &unloadedClassRanges = std::get<0>(response);700auto maxRanges = std::get<1>(response);701std::string &serializedCHTable = std::get<2>(response);702703clientSession->initializeUnloadedClassAddrRanges(unloadedClassRanges, maxRanges);704if (!unloadedClasses.empty())705{706// This function updates multiple caches based on the newly unloaded classes list.707// Pass `false` here to indicate that we want the unloaded class ranges table cache excluded,708// since we just retrieved the entire table and it should therefore already be up to date.709clientSession->processUnloadedClasses(unloadedClasses, false);710}711auto chTable = static_cast<JITServerPersistentCHTable *>(clientSession->getCHTable());712// Need CHTable mutex713TR_ASSERT_FATAL(!chTable->isInitialized(), "CHTable must be empty for clientUID=%llu", (unsigned long long)clientId);714if (TR::Options::getVerboseOption(TR_VerboseJITServer))715TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer, "compThreadID=%d will initialize CHTable for clientUID %llu size=%zu",716getCompThreadId(), (unsigned long long)clientId, serializedCHTable.size());717chTable->initializeCHTable(_vm, serializedCHTable);718clientSession->setCachesAreCleared(false);719}720}721722// Critical requests must update lastProcessedCriticalSeqNo and notify any waiting threads.723// Dependent threads will pass through once we've released the sequencing monitor.724if (isCriticalRequest)725{726clientSession->getSequencingMonitor()->enter();727TR_ASSERT_FATAL(criticalSeqNo <= clientSession->getLastProcessedCriticalSeqNo(),728"compThreadID=%d clientSessionData=%p clientUID=%llu seqNo=%u, criticalSeqNo=%u != lastProcessedCriticalSeqNo=%u, numActiveThreads=%d",729getCompThreadId(), clientSession, (unsigned long long)clientId, seqNo, criticalSeqNo,730clientSession->getLastProcessedCriticalSeqNo(), clientSession->getNumActiveThreads());731732clientSession->setLastProcessedCriticalSeqNo(seqNo);733hasUpdatedSeqNo = true;734Trc_JITServerUpdateSeqNo(getCompilationThread(), getCompThreadId(), clientSession,735(unsigned long long)clientSession->getClientUID(),736getSeqNo(), criticalSeqNo, clientSession->getNumActiveThreads(),737clientSession->getLastProcessedCriticalSeqNo(), seqNo);738739// Notify waiting threads. I need to keep notifying until the first request that740// is blocked because the request it depends on hasn't been processed yet.741notifyAndDetachWaitingRequests(clientSession);742743// Finally, release the sequencing monitor so that other threads744// can process their lists of unloaded classes745clientSession->getSequencingMonitor()->exit();746}747748// Copy the option string749copyClientOptions(clientOptStr, clientSession->persistentMemory());750751// _recompilationMethodInfo will be passed to J9::Recompilation::_methodInfo,752// which will get freed in populateBodyInfo() when creating the753// method meta data during the compilation. Since _recompilationMethodInfo is already754// freed in populateBodyInfo(), no need to free it at the end of the compilation in this method.755if (recompInfoStr.size() > 0)756{757_recompilationMethodInfo = new (clientSession->persistentMemory()) TR_PersistentMethodInfo();758memcpy(_recompilationMethodInfo, recompInfoStr.data(), sizeof(TR_PersistentMethodInfo));759J9::Recompilation::resetPersistentProfileInfo(_recompilationMethodInfo);760}761// Get the ROMClass for the method to be compiled if it is already cached762// Or read it from the compilation request and cache it otherwise763J9ROMClass *romClass = NULL;764if (!(romClass = JITServerHelpers::getRemoteROMClassIfCached(clientSession, clazz)))765{766// Class for current request is not yet cached.767// If the client sent us the desired information in the compilation request, use that.768if(!(std::get<0>(classInfoTuple).empty()))769{770romClass = JITServerHelpers::romClassFromString(std::get<0>(classInfoTuple), clientSession->persistentMemory());771}772else773{774// The client did not embed info about desired class in the compilation request.775// This could happen if the client determined that it sent required information in776// a previous request, info which the server is expected to cache. However, this777// could be a new JITServer instance.778// Send a message to the client to retrieve desired info.779romClass = JITServerHelpers::getRemoteROMClass(clazz, stream, clientSession->persistentMemory(), classInfoTuple);780}781romClass = JITServerHelpers::cacheRemoteROMClassOrFreeIt(clientSession, clazz, romClass, classInfoTuple);782TR_ASSERT_FATAL(romClass, "ROM class of J9Class=%p must be cached at this point", clazz);783}784785// Optimization plan needs to use the global allocator,786// because there is a global pool of plans787{788JITServer::GlobalAllocationRegion globalRegion(this);789// Build my entry790if (!(optPlan = TR_OptimizationPlan::alloc(clientOptPlan.getOptLevel())))791throw std::bad_alloc();792optPlan->clone(&clientOptPlan);793if (optPlan->isLogCompilation())794optPlan->setLogCompilation(clientOptPlan.getLogCompilation());795}796797// All entries have the same priority for now. In the future we may want to give higher priority to sync requests798// Also, oldStartPC is always NULL for JITServer799entry._freeTag = ENTRY_IN_POOL_FREE; // Pretend we just got it from the pool because we need to initialize it again800entry.initialize(*serverDetails, NULL, CP_SYNC_NORMAL, optPlan);801entry._jitStateWhenQueued = compInfo->getPersistentInfo()->getJitState();802entry._stream = stream; // Add the stream to the entry803entry._entryTime = compInfo->getPersistentInfo()->getElapsedTime(); // Cheaper version804entry._methodIsInSharedCache = false; // No SCC for now in JITServer805entry._compInfoPT = this; // Need to know which comp thread is handling this request806entry._async = true; // All of requests at the server are async807// Weight is irrelevant for JITServer.808// If we want something then we need to increaseQueueWeightBy(weight) while holding compilation monitor809entry._weight = 0;810entry._useAotCompilation = useAotCompilation;811812auto aotCache = clientSession->getOrCreateAOTCache(stream);813_aotCacheStore = classChain && aotCache;814aotCacheLoad = aotCacheLoad && classChain && aotCache;815if (aotCache && !aotCacheLoad)816aotCache->incNumCacheBypasses();817818if (_aotCacheStore || aotCacheLoad)819{820// Get defining class chain record to use as a part of the key to lookup or store the method in AOT cache821JITServerHelpers::cacheRemoteROMClassBatch(clientSession, uncachedRAMClasses, uncachedClassInfos);822_definingClassChainRecord = clientSession->getClassChainRecord(clazz, classChain, ramClassChain, stream);823if (!_definingClassChainRecord)824{825if (TR::Options::getVerboseOption(TR_VerboseJITServer))826TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer,827"clientUID %llu failed to get defining class chain record for %p; "828"method %p won't be loaded from or stored in AOT cache",829(unsigned long long)clientId, clazz, ramMethod830);831if (aotCacheLoad)832aotCache->incNumCacheMisses();833_aotCacheStore = false;834aotCacheLoad = false;835}836}837838if (aotCacheLoad)839aotCacheHit = serveCachedAOTMethod(entry, ramMethod, clazz, &clientOptPlan, clientSession, scratchSegmentProvider);840}841catch (const JITServer::StreamFailure &e)842{843// This could happen because the client disconnected on purpose, in which case there is no harm done,844// or if there was a network problem. In the latter case, if the request was critical, the server845// will observe a missing seqNo and after a timeout, it will clear the caches and start over846if (TR::Options::getVerboseOption(TR_VerboseJITServer))847TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer, "compThreadID=%d stream failed while reading the compilation request: %s",848getCompThreadId(), e.what());849850Trc_JITServerStreamFailure(compThread, getCompThreadId(), __FUNCTION__, "", "", e.what());851852abortCompilation = true;853deleteStream = true;854}855catch (const JITServer::StreamVersionIncompatible &e)856{857if (TR::Options::getVerboseOption(TR_VerboseJITServer))858TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer, "compThreadID=%d stream version incompatible: %s", getCompThreadId(), e.what());859860Trc_JITServerStreamVersionIncompatible(compThread, getCompThreadId(), __FUNCTION__, "", "", e.what());861862stream->writeError(compilationStreamVersionIncompatible);863abortCompilation = true;864deleteStream = true;865}866catch (const JITServer::StreamMessageTypeMismatch &e)867{868if (TR::Options::getVerboseOption(TR_VerboseJITServer))869TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer, "compThreadID=%d stream message type mismatch: %s", getCompThreadId(), e.what());870871Trc_JITServerStreamMessageTypeMismatch(compThread, getCompThreadId(), __FUNCTION__, "", "", e.what());872873stream->writeError(compilationStreamMessageTypeMismatch);874abortCompilation = true;875deleteStream = true;876}877catch (const JITServer::StreamConnectionTerminate &e)878{879if (TR::Options::getVerboseOption(TR_VerboseJITServer))880TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer, "compThreadID=%d stream connection terminated by JITClient on stream %p while reading the compilation request: %s",881getCompThreadId(), stream, e.what());882883Trc_JITServerStreamConnectionTerminate(compThread, getCompThreadId(), __FUNCTION__, stream, e.what());884885abortCompilation = true;886deleteStream = true;887}888catch (const JITServer::StreamClientSessionTerminate &e)889{890if (TR::Options::getVerboseOption(TR_VerboseJITServer))891TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer, "compThreadID=%d stream client session terminated by JITClient: %s", getCompThreadId(), e.what());892893Trc_JITServerStreamClientSessionTerminate(compThread, getCompThreadId(), __FUNCTION__, e.what());894895abortCompilation = true;896deleteStream = true;897898deleteClientSessionData(e.getClientId(), compInfo, compThread);899}900catch (const JITServer::StreamInterrupted &e)901{902if (TR::Options::getVerboseOption(TR_VerboseJITServer))903TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer, "compThreadID=%d stream interrupted by JITClient while reading the compilation request: %s",904getCompThreadId(), e.what());905906Trc_JITServerStreamInterrupted(compThread, getCompThreadId(), __FUNCTION__, "", "", e.what());907908abortCompilation = true;909// If the client aborted this compilation, it could have happened while asking for entire910// CHTable and unloaded class address ranges and at that point the seqNo was not updated.911// In such case, we must update seqNo now to allow for blocking threads to pass through.912// Since the caches are already cleared there is no harm in discarding this message.913if (isCriticalRequest && !hasUpdatedSeqNo)914{915clientSession->getSequencingMonitor()->enter();916if (seqNo > clientSession->getLastProcessedCriticalSeqNo())917{918Trc_JITServerUpdateSeqNo(getCompilationThread(), getCompThreadId(), clientSession,919(unsigned long long)clientSession->getClientUID(),920seqNo, getExpectedSeqNo(), clientSession->getNumActiveThreads(),921clientSession->getLastProcessedCriticalSeqNo(), seqNo);922923clientSession->setLastProcessedCriticalSeqNo(seqNo);924notifyAndDetachWaitingRequests(clientSession);925}926clientSession->getSequencingMonitor()->exit();927}928}929catch (const JITServer::StreamOOO &e)930{931// Error message was printed when the exception was thrown932stream->writeError(compilationStreamLostMessage); // the client should recognize this code and retry933abortCompilation = true;934}935catch (const std::bad_alloc &e)936{937if (TR::Options::getVerboseOption(TR_VerboseJITServer))938TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer, "Server out of memory in processEntry: %s", e.what());939stream->writeError(compilationLowPhysicalMemory, (uint64_t) computeServerMemoryState(getCompilationInfo()));940abortCompilation = true;941}942943// Acquire VM access944//945acquireVMAccessNoSuspend(compThread);946947if (abortCompilation)948{949if (clientOptions)950{951deleteClientOptions(getClientData()->persistentMemory());952}953954if (_recompilationMethodInfo)955{956getClientData()->persistentMemory()->freePersistentMemory(_recompilationMethodInfo);957_recompilationMethodInfo = NULL;958}959960if (TR::Options::getVerboseOption(TR_VerboseJITServer))961{962if (getClientData())963{964TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer, "compThreadID=%d did an early abort for clientUID=%llu seqNo=%u",965getCompThreadId(), getClientData()->getClientUID(), getSeqNo());966Trc_JITServerEarlyAbortClientData(compThread, getCompThreadId(), (unsigned long long)getClientData()->getClientUID(), getSeqNo());967}968else969{970TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer, "compThreadID=%d did an early abort",971getCompThreadId());972Trc_JITServerEarlyAbort(compThread, getCompThreadId());973}974}975976compInfo->acquireCompMonitor(compThread);977releaseVMAccess(compThread);978compInfo->decreaseQueueWeightBy(entry._weight);979980// Put the request back into the pool981setMethodBeingCompiled(NULL); // Must have the compQmonitor982983exitPerClientAllocationRegion();984985if (optPlan)986{987TR_OptimizationPlan::freeOptimizationPlan(optPlan);988}989990if (!compInfo->getPersistentInfo()->getDisableFurtherCompilation()991&& !deleteStream992&& !enableJITServerPerCompConn)993{994compInfo->requeueOutOfProcessEntry(&entry);995}996else997{998// Delete server stream if per compilation connections are enabled999// or if the server disabled compilations due to a crash1000stream->~ServerStream();1001TR::Compiler->persistentGlobalAllocator().deallocate(stream);1002entry._stream = NULL;1003}10041005// Reset the pointer to the cached client session data1006if (getClientData())1007{1008if (hasIncNumActiveThreads)1009{1010getClientData()->getSequencingMonitor()->enter();1011getClientData()->decNumActiveThreads();1012getClientData()->getSequencingMonitor()->exit();1013}10141015getClientData()->decInUse(); // We have the compilation monitor so it's safe to access the inUse counter1016if (getClientData()->getInUse() == 0)1017{1018bool result = compInfo->getClientSessionHT()->deleteClientSession(clientId, false);1019if (result)1020{1021if (TR::Options::getVerboseOption(TR_VerboseJITServer))1022TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer,"client (%llu) deleted", (unsigned long long)clientId);1023}1024}1025setClientData(NULL);1026}1027return;1028}10291030// Do the hack for newInstance thunks1031// Also make the method appear as interpreted, otherwise we might want to access recompilation info1032// JITServer TODO: is this ever executed for JITServer?1033//if (entry.getMethodDetails().isNewInstanceThunk())1034// {1035// J9::NewInstanceThunkDetails &newInstanceDetails = static_cast<J9::NewInstanceThunkDetails &>(entry.getMethodDetails());1036// J9Class *classForNewInstance = newInstanceDetails.classNeedingThunk();1037// TR::CompilationInfo::setJ9MethodExtra(ramMethod, (uintptr_t)classForNewInstance | J9_STARTPC_NOT_TRANSLATED);1038// }10391040#ifdef STATS1041statQueueSize.update(compInfo->getMethodQueueSize());1042#endif1043void *startPC = NULL;1044// The following block of code will return with compilation monitor in hand1045if (aotCacheHit)1046{1047compInfo->acquireCompMonitor(compThread);1048stream->setClientData(NULL);1049}1050else1051{1052stream->setClientData(clientSession);1053getClientData()->readAcquireClassUnloadRWMutex(this);10541055startPC = compile(compThread, &entry, scratchSegmentProvider);10561057getClientData()->readReleaseClassUnloadRWMutex(this);1058stream->setClientData(NULL);1059}10601061deleteClientOptions(getClientData()->persistentMemory());10621063// Notify any threads waiting on this entry's monitor1064// The only thread that should be holding a monitor should be the JitDump thread1065entry.getMonitor()->notifyAll();1066// Release the queue slot monitor because we don't need it anymore1067// This will allow us to acquire the sequencing monitor later1068entry.releaseSlotMonitor(compThread);10691070entry._newStartPC = startPC;1071// Update statistics regarding the compilation status (including compilationOK)1072compInfo->updateCompilationErrorStats((TR_CompilationErrorCode)entry._compErrCode);10731074// Save the pointer to the plan before recycling the entry1075// Decrease the queue weight1076compInfo->decreaseQueueWeightBy(entry._weight);10771078exitPerClientAllocationRegion();10791080TR_OptimizationPlan::freeOptimizationPlan(optPlan); // we no longer need the optimization plan10811082// Put the request back into the pool1083setMethodBeingCompiled(NULL);10841085if (!compInfo->getPersistentInfo()->getDisableFurtherCompilation()1086&& !enableJITServerPerCompConn1087&& entry._compErrCode != compilationStreamFailure)1088{1089compInfo->requeueOutOfProcessEntry(&entry);1090}1091else1092{1093TR_ASSERT(entry._stream, "stream should still exist after compilation even if it encounters a streamFailure.");1094// Delete server stream if per compilation connections are enabled,1095// if the server disabled compilations due to a crash,1096// or if the stream failed1097stream->~ServerStream();1098TR::Compiler->persistentGlobalAllocator().deallocate(stream);1099entry._stream = NULL;1100}11011102compInfo->printQueue();11031104// Decrement number of active threads before _inUse, but we1105// need to acquire the sequencing monitor when accessing numActiveThreads1106getClientData()->getSequencingMonitor()->enter();1107getClientData()->decNumActiveThreads();1108getClientData()->getSequencingMonitor()->exit();1109getClientData()->decInUse(); // We have the compMonitor so it's safe to access the inUse counter1110if (getClientData()->getInUse() == 0)1111{1112bool deleted = compInfo->getClientSessionHT()->deleteClientSession(clientId, false);1113if (deleted && TR::Options::getVerboseOption(TR_VerboseJITServer))1114TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer,"client (%llu) deleted", (unsigned long long)clientId);1115}11161117setClientData(NULL); // Reset the pointer to the cached client session data11181119// At this point we should always have VMAccess1120// We should always have the compilation monitor1121// we should never have classUnloadMonitor11221123// Release VM access1124//1125compInfo->debugPrint(compThread, "\tcompilation thread releasing VM access\n");1126releaseVMAccess(compThread);1127compInfo->debugPrint(compThread, "-VMacc\n");11281129// We can suspend this thread if too many are active1130if (1131!isDiagnosticThread() // Must not be reserved for log1132&& compInfo->getNumCompThreadsActive() > 1 // we should have at least one active besides this one1133&& compilationThreadIsActive() // We haven't already been signaled to suspend or terminate1134&& (compInfo->getRampDownMCT() || compInfo->getSuspendThreadDueToLowPhysicalMemory())1135)1136{1137// Suspend this thread1138setCompilationThreadState(COMPTHREAD_SIGNAL_SUSPEND);1139compInfo->decNumCompThreadsActive();1140if (TR::Options::getCmdLineOptions()->getVerboseOption(TR_VerboseCompilationThreads))1141{1142TR_VerboseLog::writeLineLocked(TR_Vlog_INFO, "t=%6u Suspend compThread %d Qweight=%d active=%d %s %s",1143(uint32_t)compInfo->getPersistentInfo()->getElapsedTime(),1144getCompThreadId(),1145compInfo->getQueueWeight(),1146compInfo->getNumCompThreadsActive(),1147compInfo->getRampDownMCT() ? "RampDownMCT" : "",1148compInfo->getSuspendThreadDueToLowPhysicalMemory() ? "LowPhysicalMem" : "");1149}1150// If the other remaining active thread(s) are sleeping (maybe because1151// we wanted to avoid two concurrent hot requests) we need to wake them1152// now as a preventive measure. Worst case scenario they will go back to sleep1153if (compInfo->getNumCompThreadsJobless() > 0)1154{1155compInfo->getCompilationMonitor()->notifyAll();1156if (TR::Options::getCmdLineOptions()->getVerboseOption(TR_VerboseCompilationThreads))1157{1158TR_VerboseLog::writeLineLocked(TR_Vlog_INFO, "t=%6u compThread %d notifying other sleeping comp threads. Jobless=%d",1159(uint32_t)compInfo->getPersistentInfo()->getElapsedTime(),1160getCompThreadId(),1161compInfo->getNumCompThreadsJobless());1162}1163}1164}1165else // We will not suspend this thread1166{1167// If the low memory flag was set but there was no additional comp thread1168// to suspend, we must clear the flag now1169if (compInfo->getSuspendThreadDueToLowPhysicalMemory() &&1170compInfo->getNumCompThreadsActive() < 2)1171compInfo->setSuspendThreadDueToLowPhysicalMemory(false);1172}1173}11741175/**1176* @brief Method executed by JITServer to store bytecode iprofiler info to heap memory (instead of to persistent memory)1177*1178* @param method J9Method in question1179* @param byteCodeIndex bytecode in question1180* @param entry iprofile data to be stored1181* @return always return true at the moment1182*/1183bool1184TR::CompilationInfoPerThreadRemote::cacheIProfilerInfo(TR_OpaqueMethodBlock *method, uint32_t byteCodeIndex, TR_IPBytecodeHashTableEntry *entry)1185{1186IPTableHeapEntry *entryMap = NULL;1187if(!getCachedValueFromPerCompilationMap(_methodIPDataPerComp, (J9Method *) method, entryMap))1188{1189// Either first time cacheIProfilerInfo called during current compilation,1190// so _methodIPDataPerComp is NULL, or caching a new method, entryMap not found1191if (entry)1192{1193cacheToPerCompilationMap(entryMap, byteCodeIndex, entry);1194}1195else1196{1197// If entry is NULL, create entryMap, but do not cache anything1198initializePerCompilationCache(entryMap);1199}1200cacheToPerCompilationMap(_methodIPDataPerComp, (J9Method *) method, entryMap);1201}1202else if (entry)1203{1204// Adding an entry for already seen method (i.e. entryMap exists)1205cacheToPerCompilationMap(entryMap, byteCodeIndex, entry);1206}1207return true;1208}12091210/**1211* @brief Method executed by JITServer to retrieve bytecode iprofiler info from the heap memory1212*1213* @param method J9Method in question1214* @param byteCodeIndex bytecode in question1215* @param methodInfoPresent indicates to the caller whether the data is present1216* @return IPTableHeapEntry bytecode iprofile data1217*/1218TR_IPBytecodeHashTableEntry*1219TR::CompilationInfoPerThreadRemote::getCachedIProfilerInfo(TR_OpaqueMethodBlock *method, uint32_t byteCodeIndex, bool *methodInfoPresent)1220{1221*methodInfoPresent = false;12221223IPTableHeapEntry *entryMap = NULL;1224TR_IPBytecodeHashTableEntry *ipEntry = NULL;12251226getCachedValueFromPerCompilationMap(_methodIPDataPerComp, (J9Method *) method, entryMap);1227// methodInfoPresent=true means that we cached info for this method (i.e. entryMap is not NULL),1228// but entry for this bytecode might not exist, which means it's NULL1229if (entryMap)1230{1231*methodInfoPresent = true;1232getCachedValueFromPerCompilationMap(entryMap, byteCodeIndex, ipEntry);1233}1234return ipEntry;1235}12361237/**1238* @brief Method executed by JITServer to cache a resolved method to the resolved method cache1239*1240* @param key Identifier used to identify a resolved method in resolved methods cache1241* @param method The resolved method of interest1242* @param vTableSlot The vTableSlot for the resolved method of interest1243* @param methodInfo Additional method info about the resolved method of interest1244* @return returns void1245*/1246void1247TR::CompilationInfoPerThreadRemote::cacheResolvedMethod(TR_ResolvedMethodKey key, TR_OpaqueMethodBlock *method,1248uint32_t vTableSlot, const TR_ResolvedJ9JITServerMethodInfo &methodInfo, int32_t ttlForUnresolved)1249{1250static bool useCaching = !feGetEnv("TR_DisableResolvedMethodsCaching");1251if (!useCaching)1252return;1253// Create a new TR_ResolvedJ9JITServerMethodInfo using scratch memory12541255TR_ASSERT_FATAL(getCompilation(), "Must be in compilation when calling cacheResolvedMethod\n");1256TR_Memory *trMemory = getCompilation()->trMemory();12571258TR_PersistentJittedBodyInfo *bodyInfo = NULL;1259if (!std::get<1>(methodInfo).empty())1260{1261bodyInfo = (TR_PersistentJittedBodyInfo*) trMemory->allocateHeapMemory(sizeof(TR_PersistentJittedBodyInfo), TR_MemoryBase::Recompilation);1262memcpy(bodyInfo, std::get<1>(methodInfo).data(), sizeof(TR_PersistentJittedBodyInfo));1263}1264TR_PersistentMethodInfo *pMethodInfo = NULL;1265if (!std::get<2>(methodInfo).empty())1266{1267pMethodInfo = (TR_PersistentMethodInfo*) trMemory->allocateHeapMemory(sizeof(TR_PersistentMethodInfo), TR_MemoryBase::Recompilation);1268memcpy(pMethodInfo, std::get<2>(methodInfo).data(), sizeof(TR_PersistentMethodInfo));1269}1270TR_ContiguousIPMethodHashTableEntry *entry = NULL;1271if (!std::get<3>(methodInfo).empty())1272{1273entry = (TR_ContiguousIPMethodHashTableEntry*) trMemory->allocateHeapMemory(sizeof(TR_ContiguousIPMethodHashTableEntry), TR_MemoryBase::Recompilation);1274memcpy(entry, std::get<3>(methodInfo).data(), sizeof(TR_ContiguousIPMethodHashTableEntry));1275}12761277TR_ResolvedMethodCacheEntry cacheEntry;1278cacheEntry.method = method;1279cacheEntry.vTableSlot = vTableSlot;1280cacheEntry.methodInfoStruct = std::get<0>(methodInfo);1281cacheEntry.persistentBodyInfo = bodyInfo;1282cacheEntry.persistentMethodInfo = pMethodInfo;1283cacheEntry.IPMethodInfo = entry;12841285// time-to-live for cached unresolved methods.1286// Irrelevant for resolved methods.1287cacheEntry.ttlForUnresolved = ttlForUnresolved;12881289cacheToPerCompilationMap(_resolvedMethodInfoMap, key, cacheEntry);1290}12911292/**1293* @brief Method executed by JITServer to retrieve a resolved method from the resolved method cache1294*1295* @param key Identifier used to identify a resolved method in resolved methods cache1296* @param owningMethod Owning method of the resolved method of interest1297* @param resolvedMethod The resolved method of interest, set by this API1298* @param unresolvedInCP The unresolvedInCP boolean value of interest, set by this API1299* @return returns true if method is cached, sets resolvedMethod and unresolvedInCP to cached values, false otherwise.1300*/1301bool1302TR::CompilationInfoPerThreadRemote::getCachedResolvedMethod(TR_ResolvedMethodKey key, TR_ResolvedJ9JITServerMethod *owningMethod,1303TR_ResolvedMethod **resolvedMethod, bool *unresolvedInCP)1304{1305TR_ResolvedMethodCacheEntry methodCacheEntry = {0};13061307*resolvedMethod = NULL;13081309if (unresolvedInCP)1310*unresolvedInCP = true;13111312if (getCachedValueFromPerCompilationMap(_resolvedMethodInfoMap, key, methodCacheEntry))1313{1314auto comp = getCompilation();1315TR_OpaqueMethodBlock *method = methodCacheEntry.method;13161317// if remoteMirror == NULL means we have cached an unresolved method;1318// purge the cached entry if time-to-live has expired.1319if (!methodCacheEntry.methodInfoStruct.remoteMirror)1320{1321// decrement time-to-live of this unresolved entry1322methodCacheEntry.ttlForUnresolved--;13231324if (methodCacheEntry.ttlForUnresolved <= 0)1325_resolvedMethodInfoMap->erase(key);1326return true;1327}13281329uint32_t vTableSlot = methodCacheEntry.vTableSlot;1330auto methodInfoStruct = methodCacheEntry.methodInfoStruct;1331TR_ResolvedJ9JITServerMethodInfo methodInfo = make_tuple(methodInfoStruct,1332methodCacheEntry.persistentBodyInfo ? std::string((const char*)methodCacheEntry.persistentBodyInfo, sizeof(TR_PersistentJittedBodyInfo)) : std::string(),1333methodCacheEntry.persistentMethodInfo ? std::string((const char*)methodCacheEntry.persistentMethodInfo, sizeof(TR_PersistentMethodInfo)) : std::string(),1334methodCacheEntry.IPMethodInfo ? std::string((const char*)methodCacheEntry.IPMethodInfo, sizeof(TR_ContiguousIPMethodHashTableEntry)) : std::string());1335// Re-add validation record1336if (comp->compileRelocatableCode() && comp->getOption(TR_UseSymbolValidationManager) && !comp->getSymbolValidationManager()->inHeuristicRegion())1337{1338if(!owningMethod->addValidationRecordForCachedResolvedMethod(key, method))1339return true; // Could not add a validation record1340}13411342// Create resolved method from cached method info1343if (key.type != TR_ResolvedMethodType::VirtualFromOffset)1344{1345*resolvedMethod = owningMethod->createResolvedMethodFromJ9Method(1346comp,1347key.cpIndex,1348vTableSlot,1349(J9Method *) method,1350unresolvedInCP,1351NULL,1352methodInfo);1353}1354else1355{1356if (_vm->isAOT_DEPRECATED_DO_NOT_USE())1357*resolvedMethod = method ? new (comp->trHeapMemory()) TR_ResolvedRelocatableJ9JITServerMethod(method, _vm, comp->trMemory(), methodInfo, owningMethod) : 0;1358else1359*resolvedMethod = method ? new (comp->trHeapMemory()) TR_ResolvedJ9JITServerMethod(method, _vm, comp->trMemory(), methodInfo, owningMethod) : 0;1360}13611362if (*resolvedMethod)1363{1364if (unresolvedInCP)1365*unresolvedInCP = false;1366return true;1367}1368else1369{1370TR_ASSERT(false, "Should not have cached unresolved method globally");1371}1372}1373return false;1374}13751376/**1377* @brief Method executed by JITServer to compose a TR_ResolvedMethodKey used for the resolved method cache1378*1379* @param type Resolved method type: VirtualFromCP, VirtualFromOffset, Interface, Static, Special, ImproperInterface, NoType1380* @param ramClass ramClass of resolved method of interest1381* @param cpIndex constant pool index1382* @param classObject default to NULL, only set for resolved interface method1383* @return key used to identify a resolved method in the resolved method cache1384*/1385TR_ResolvedMethodKey1386TR::CompilationInfoPerThreadRemote::getResolvedMethodKey(TR_ResolvedMethodType type, TR_OpaqueClassBlock *ramClass, int32_t cpIndex, TR_OpaqueClassBlock *classObject)1387{1388TR_ResolvedMethodKey key = {type, ramClass, cpIndex, classObject};1389return key;1390}13911392/**1393* @brief Method executed by JITServer to save the mirrors of resolved method of interest to a list1394*1395* @param resolvedMethod The mirror of the resolved method of interest (existing at JITClient)1396* @return void1397*/1398void1399TR::CompilationInfoPerThreadRemote::cacheResolvedMirrorMethodsPersistIPInfo(TR_ResolvedJ9Method *resolvedMethod)1400{1401if (!_resolvedMirrorMethodsPersistIPInfo)1402{1403initializePerCompilationCache(_resolvedMirrorMethodsPersistIPInfo);1404if (!_resolvedMirrorMethodsPersistIPInfo)1405return;1406}14071408_resolvedMirrorMethodsPersistIPInfo->push_back(resolvedMethod);1409}14101411/**1412* @brief Method executed by JITServer to remember NULL answers for classOfStatic() queries1413*1414* @param ramClass The static class of interest as part of the key1415* @param cpIndex The constant pool index of interest as part of the key1416* @return void1417*/1418void1419TR::CompilationInfoPerThreadRemote::cacheNullClassOfStatic(TR_OpaqueClassBlock *ramClass, int32_t cpIndex)1420{1421TR_OpaqueClassBlock *nullClazz = NULL;1422cacheToPerCompilationMap(_classOfStaticMap, std::make_pair(ramClass, cpIndex), nullClazz);1423}14241425/**1426* @brief Method executed by JITServer to determine if a previous classOfStatic() query returned NULL1427*1428* @param ramClass The static class of interest1429* @param cpIndex The constant pool index of interest1430* @return returns true if the previous classOfStatic() query returned NULL and false otherwise1431*/1432bool1433TR::CompilationInfoPerThreadRemote::getCachedNullClassOfStatic(TR_OpaqueClassBlock *ramClass, int32_t cpIndex)1434{1435TR_OpaqueClassBlock *nullClazz;1436return getCachedValueFromPerCompilationMap(_classOfStaticMap, std::make_pair(ramClass, cpIndex), nullClazz);1437}14381439/**1440* @brief Method executed by JITServer to cache field or static attributes1441*1442* @param ramClass The ramClass of interest as part of the key1443* @param cpIndex The cpIndex of interest as part of the key1444* @param attrs The value we are going to cache1445* @param isStatic Whether the field is static1446* @return void1447*/1448void1449TR::CompilationInfoPerThreadRemote::cacheFieldOrStaticAttributes(TR_OpaqueClassBlock *ramClass, int32_t cpIndex, const TR_J9MethodFieldAttributes &attrs, bool isStatic)1450{1451if (isStatic)1452cacheToPerCompilationMap(_staticAttributesCache, std::make_pair(ramClass, cpIndex), attrs);1453else1454cacheToPerCompilationMap(_fieldAttributesCache, std::make_pair(ramClass, cpIndex), attrs);1455}14561457/**1458* @brief Method executed by JITServer to retrieve field or static attributes from the cache1459*1460* @param ramClass The ramClass of interest as part of the key1461* @param cpIndex The cpIndex of interest as part of the value1462* @param attrs The value to be set by the API1463* @param isStatic Whether the field is static1464* @return returns true if found in cache else false1465*/1466bool1467TR::CompilationInfoPerThreadRemote::getCachedFieldOrStaticAttributes(TR_OpaqueClassBlock *ramClass, int32_t cpIndex, TR_J9MethodFieldAttributes &attrs, bool isStatic)1468{1469if (isStatic)1470return getCachedValueFromPerCompilationMap(_staticAttributesCache, std::make_pair(ramClass, cpIndex), attrs);1471else1472return getCachedValueFromPerCompilationMap(_fieldAttributesCache, std::make_pair(ramClass, cpIndex), attrs);1473}14741475/**1476* @brief Method executed by JITServer to cache unresolved string1477*1478* @param ramClass The ramClass of interest as part of the key1479* @param cpIndex The cpIndex of interest as part of the key1480* @param stringAttrs The value we are going to cache1481* @return void1482*/1483void1484TR::CompilationInfoPerThreadRemote::cacheIsUnresolvedStr(TR_OpaqueClassBlock *ramClass, int32_t cpIndex, const TR_IsUnresolvedString &stringAttrs)1485{1486cacheToPerCompilationMap(_isUnresolvedStrCache, std::make_pair(ramClass, cpIndex), stringAttrs);1487}14881489/**1490* @brief Method executed by JITServer to retrieve unresolved string1491*1492* @param ramClass The ramClass of interest as part of the key1493* @param cpIndex The cpIndex of interest as part of the key1494* @param attrs The value to be set by the API1495* @return returns true if found in cache else false1496*/1497bool1498TR::CompilationInfoPerThreadRemote::getCachedIsUnresolvedStr(TR_OpaqueClassBlock *ramClass, int32_t cpIndex, TR_IsUnresolvedString &stringAttrs)1499{1500return getCachedValueFromPerCompilationMap(_isUnresolvedStrCache, std::make_pair(ramClass, cpIndex), stringAttrs);1501}15021503/**1504* @brief Method executed by JITServer to clear cache for per compilation1505*/1506void1507TR::CompilationInfoPerThreadRemote::clearPerCompilationCaches()1508{1509clearPerCompilationCache(_methodIPDataPerComp);1510clearPerCompilationCache(_resolvedMethodInfoMap);1511clearPerCompilationCache(_resolvedMirrorMethodsPersistIPInfo);1512clearPerCompilationCache(_classOfStaticMap);1513clearPerCompilationCache(_fieldAttributesCache);1514clearPerCompilationCache(_staticAttributesCache);1515clearPerCompilationCache(_isUnresolvedStrCache);1516}15171518/**1519* @brief Method executed by JITServer to delete client session data when client stream is terminated1520*/1521void1522TR::CompilationInfoPerThreadRemote::deleteClientSessionData(uint64_t clientId, TR::CompilationInfo* compInfo, J9VMThread* compThread)1523{1524// Need to acquire compilation monitor for both deleting the client data and the setting the thread state to COMPTHREAD_SIGNAL_SUSPEND.1525compInfo->acquireCompMonitor(compThread);1526bool result = compInfo->getClientSessionHT()->deleteClientSession(clientId, true);1527if (TR::Options::getVerboseOption(TR_VerboseJITServer) ||1528TR::Options::getVerboseOption(TR_VerboseJITServerConns))1529{1530uint32_t timestamp = (uint32_t) compInfo->getPersistentInfo()->getElapsedTime();1531if (!result)1532{1533TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer,"t=%6u Client (clientUID=%llu) disconnected. Client session not deleted", timestamp, (unsigned long long)clientId);1534}1535else1536{1537TR_VerboseLog::writeLineLocked(TR_Vlog_JITServer,"t=%6u Client (clientUID=%llu) disconnected. Client session deleted", timestamp, (unsigned long long)clientId);1538}1539}15401541compInfo->releaseCompMonitor(compThread);1542}15431544void1545TR::CompilationInfoPerThreadRemote::freeAllResources()1546{1547if (_recompilationMethodInfo)1548{1549TR_Memory::jitPersistentFree(_recompilationMethodInfo);1550_recompilationMethodInfo = NULL;1551}15521553TR::CompilationInfoPerThread::freeAllResources();1554}155515561557