Path: blob/master/runtime/gc_realtime/Scheduler.cpp
5985 views
/*******************************************************************************1* Copyright (c) 1991, 2020 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 "omr.h"23#include "omrcfg.h"24#include "ModronAssertions.h"2526#include <string.h>2728#include "AtomicOperations.hpp"29#include "EnvironmentRealtime.hpp"30#include "GCCode.hpp"31#include "GCExtensionsBase.hpp"32#include "Heap.hpp"33#include "IncrementalParallelTask.hpp"34#include "MemoryPoolSegregated.hpp"35#include "MemorySubSpaceMetronome.hpp"36#include "Metronome.hpp"37#include "MetronomeAlarmThread.hpp"38#include "MetronomeDelegate.hpp"39#include "RealtimeGC.hpp"40#include "OSInterface.hpp"41#include "ParallelDispatcher.hpp"42#include "Scheduler.hpp"43#include "Timer.hpp"44#include "UtilizationTracker.hpp"4546/**47* Initialization.48* @todo Provide method documentation49* @ingroup GC_Metronome methodGroup50*/51MM_Scheduler*52MM_Scheduler::newInstance(MM_EnvironmentBase *env, omrsig_handler_fn handler, void* handler_arg, uintptr_t defaultOSStackSize)53{54MM_Scheduler *scheduler = (MM_Scheduler *)env->getForge()->allocate(sizeof(MM_Scheduler), MM_AllocationCategory::FIXED, OMR_GET_CALLSITE());55if (scheduler) {56new(scheduler) MM_Scheduler(env, handler, handler_arg, defaultOSStackSize);57if (!scheduler->initialize(env)) {58scheduler->kill(env);59scheduler = NULL;60}61}62return scheduler;63}6465/**66* Initialization.67* @todo Provide method documentation68* @ingroup GC_Metronome methodGroup69*/70void71MM_Scheduler::kill(MM_EnvironmentBase *env)72{73tearDown(env);74}7576/**77* Teardown78* @todo Provide method documentation79* @ingroup GC_Metronome methodGroup80*/81void82MM_Scheduler::tearDown(MM_EnvironmentBase *env)83{84if (_mainThreadMonitor) {85omrthread_monitor_destroy(_mainThreadMonitor);86}87if (NULL != _threadResumedTable) {88env->getForge()->free(_threadResumedTable);89_threadResumedTable = NULL;90}91if (NULL != _utilTracker) {92_utilTracker->kill(env);93}94MM_ParallelDispatcher::kill(env);95}9697uintptr_t98MM_Scheduler::getParameter(uintptr_t which, char *keyBuffer, I_32 keyBufferSize, char *valueBuffer, I_32 valueBufferSize)99{100OMRPORT_ACCESS_FROM_OMRVM(_vm);101switch (which) {102case 0: omrstr_printf(keyBuffer, keyBufferSize, "Verbose Level");103omrstr_printf(valueBuffer, valueBufferSize, "%d", verbose());104return 1;105case 1:106{107omrstr_printf(keyBuffer, keyBufferSize, "Scheduling Method");108I_32 len = (I_32)omrstr_printf(valueBuffer, valueBufferSize, "TIME_BASED with ");109while (_alarmThread == NULL || _alarmThread->_alarm == NULL) {110/* Wait for GC to finish initializing */111omrthread_sleep(100);112}113_alarmThread->_alarm->describe(OMRPORTLIB, &valueBuffer[len], valueBufferSize - len);114return 1;115}116case 2:117omrstr_printf(keyBuffer, keyBufferSize, "Time Window");118omrstr_printf(valueBuffer, valueBufferSize, "%6.2f ms", window * 1.0e3);119return 1;120case 3:121omrstr_printf(keyBuffer, keyBufferSize, "Target Utilization");122omrstr_printf(valueBuffer, valueBufferSize, "%4.1f%%", _utilTracker->getTargetUtilization() * 1.0e2);123return 1;124case 4:125omrstr_printf(keyBuffer, keyBufferSize, "Beat Size");126omrstr_printf(valueBuffer, valueBufferSize, "%4.2f ms", beat * 1.0e3);127return 1;128case 5:129omrstr_printf(keyBuffer, keyBufferSize, "Heap Size");130omrstr_printf(valueBuffer, valueBufferSize, "%6.2f MB", ((double)(_extensions->memoryMax)) / (1 << 20));131return 1;132case 6:133omrstr_printf(keyBuffer, keyBufferSize, "GC Trigger");134omrstr_printf(valueBuffer, valueBufferSize, "%6.2f MB", _extensions->gcTrigger / (double) (1<<20));135return 1;136case 7:137omrstr_printf(keyBuffer, keyBufferSize, "Headroom");138omrstr_printf(valueBuffer, valueBufferSize, "%5.2f MB", _extensions->headRoom / (double) (1<<20));139return 1;140case 8:141omrstr_printf(keyBuffer, keyBufferSize, "Number of GC Threads");142omrstr_printf(valueBuffer, valueBufferSize, "%d", _extensions->gcThreadCount);143return 1;144case 9:145omrstr_printf(keyBuffer, keyBufferSize, "Regionsize");146omrstr_printf(valueBuffer, valueBufferSize, "%d", _extensions->regionSize);147return 1;148}149return 0;150}151152void153MM_Scheduler::showParameters(MM_EnvironmentBase *env)154{155OMRPORT_ACCESS_FROM_ENVIRONMENT(env);156omrtty_printf("****************************************************************************\n");157for (uintptr_t which=0; ; which++) {158char keyBuffer[256], valBuffer[256];159uintptr_t rc = getParameter(which, keyBuffer, sizeof(keyBuffer), valBuffer, sizeof(valBuffer));160if (rc == 0) { break; }161if (rc == 1) { omrtty_printf("%s: %s\n", keyBuffer, valBuffer); }162}163omrtty_printf("****************************************************************************\n");164}165166void167MM_Scheduler::initializeForVirtualSTW(MM_GCExtensionsBase *ext)168{169ext->gcInitialTrigger = (uintptr_t) - 1;170ext->gcTrigger = ext->gcInitialTrigger;171ext->targetUtilizationPercentage = 0;172}173174/**175* Initialization.176* @todo Provide method documentation177* @ingroup GC_Metronome methodGroup178*/179bool180MM_Scheduler::initialize(MM_EnvironmentBase *env)181{182if (!MM_ParallelDispatcher::initialize(env)) {183return false;184}185186/* Show GC parameters here before we enter real execution */187window = _extensions->timeWindowMicro / 1e6;188beat = _extensions->beatMicro / 1e6;189beatNanos = (U_64) (_extensions->beatMicro * 1e3);190_staticTargetUtilization = _extensions->targetUtilizationPercentage / 1e2;191_utilTracker = MM_UtilizationTracker::newInstance(env, window, beatNanos, _staticTargetUtilization);192if (NULL == _utilTracker) {193goto error_no_memory;194}195196197/* Set up the table used for keeping track of which threads were resumed from suspended */198_threadResumedTable = (bool*)env->getForge()->allocate(_threadCountMaximum * sizeof(bool), MM_AllocationCategory::FIXED, OMR_GET_CALLSITE());199if (NULL == _threadResumedTable) {200goto error_no_memory;201}202memset(_threadResumedTable, false, _threadCountMaximum * sizeof(bool));203204if (omrthread_monitor_init_with_name(&_mainThreadMonitor, 0, "MainThread")) {205return false;206}207208return true;209210error_no_memory:211return false;212}213214void215MM_Scheduler::collectorInitialized(MM_RealtimeGC *gc) {216_gc = gc;217_osInterface = _gc->_osInterface;218}219220void221MM_Scheduler::checkStartGC(MM_EnvironmentRealtime *env)222{223uintptr_t bytesInUse = _gc->_memoryPool->getBytesInUse();224if (isInitialized() && !isGCOn() && (bytesInUse > _extensions->gcTrigger)) {225startGC(env);226}227}228229/* Races with other startGC's are ok230*231*/232void233MM_Scheduler::startGC(MM_EnvironmentBase *env)234{235OMRPORT_ACCESS_FROM_ENVIRONMENT(env);236if (verbose() >= 3) {237omrtty_printf("GC request: %d Mb in use\n", _gc->_memoryPool->getBytesInUse() >> 20);238}239240if (METRONOME_GC_OFF == MM_AtomicOperations::lockCompareExchangeU32(&_gcOn, METRONOME_GC_OFF, METRONOME_GC_ON)) {241if (_gc->isPreviousCycleBelowTrigger()) {242_gc->setPreviousCycleBelowTrigger(false);243TRIGGER_J9HOOK_MM_PRIVATE_METRONOME_TRIGGER_START(_extensions->privateHookInterface,244env->getOmrVMThread(), omrtime_hires_clock(),245J9HOOK_MM_PRIVATE_METRONOME_TRIGGER_START246);247}248}249}250251/* External synchronization to make sure this does not race with startGC252*/253void254MM_Scheduler::stopGC(MM_EnvironmentBase *env)255{256_gcOn = METRONOME_GC_OFF;257}258259bool260MM_Scheduler::isGCOn()261{262return (METRONOME_GC_ON == _gcOn);263}264265bool266MM_Scheduler::continueGC(MM_EnvironmentRealtime *env, GCReason reason, uintptr_t resonParameter, OMR_VMThread *thr, bool doRequestExclusiveVMAccess)267{268uintptr_t gcPriority = 0;269bool didGC = true;270271assert1(isInitialized());272if (!isGCOn()) {273return false;274}275276if (_extensions->trackMutatorThreadCategory) {277/* This thread is doing GC work, account for the time spent into the GC bucket */278omrthread_set_category(omrthread_self(), J9THREAD_CATEGORY_SYSTEM_GC_THREAD, J9THREAD_TYPE_SET_GC);279}280281_gc->getRealtimeDelegate()->preRequestExclusiveVMAccess(thr);282283/* Wake up only the main thread -- it is responsible for284* waking up any workers.285* Make sure _completeCurrentGCSynchronously and _mode are atomically changed.286*/287omrthread_monitor_enter(_mainThreadMonitor);288switch(reason) {289case OUT_OF_MEMORY_TRIGGER:290/* For now we assume that OUT_OF_MEMORY trigger means perform291* a synchronous GC, but maybe we want a mode where we try one292* more time slice before degrading to synchronous.293*/294if(!_extensions->synchronousGCOnOOM) {295break;296}297/* fall through */298case SYSTEM_GC_TRIGGER:299/* System garbage collects, if not disabled through the usual command lines,300* force a synchronous GC301*/302_completeCurrentGCSynchronously = true;303_completeCurrentGCSynchronouslyReason = reason;304_completeCurrentGCSynchronouslyReasonParameter = resonParameter;305306break;307default: /* WORK_TRIGGER or TIME_TRIGGER */ {308if(_threadWaitingOnMainThreadMonitor != NULL) {309/* Check your timer again incase another thread beat you to checking for shouldMutatorDoubleBeat */310if (env->getTimer()->hasTimeElapsed(getStartTimeOfCurrentMutatorSlice(), beatNanos)) {311if (shouldMutatorDoubleBeat(_threadWaitingOnMainThreadMonitor, env->getTimer())) {312/*313* Since the mutator should double beat signal the mutator threads to update their314* timer with the current time.315*/316setStartTimeOfCurrentMutatorSlice(env->getTimer()->getTimeInNanos());317didGC = false;318goto exit;319}320} else {321didGC = false;322goto exit;323}324}325break;326}327}328if(_threadWaitingOnMainThreadMonitor == NULL) {329/*330* The gc thread(s) are already awake and collecting (otherwise, the main331* gc thread would be waiting on the monitor).332* This also means that the application threads are already sleeping.333* So there is no need to put the application threads to sleep or to334* awaken the gc thread(s). However we return true to indicate that335* garbage collection is indeed taking place as requested.336*/337goto exit;338}339340/* At this point main thread is blocked and cannot change _gcOn flag anymore.341* Check the flag again, since there is (a small) chance it may have changed since the last check342* (main thread, driven by mutators' could have finished the GC cycle)343*/344if (!isGCOn()) {345didGC = false;346goto exit;347}348349_exclusiveVMAccessRequired = doRequestExclusiveVMAccess;350351_mode = WAKING_GC;352353if (_exclusiveVMAccessRequired) {354/* initiate the request for exclusive VM access; this function does not wait for exclusive access to occur,355* that will be done by the main gc thread when it resumes activity after the mainThreadMonitor is notified356* We do not block. It's best effort. If the request is success full TRUE is returned via requested flag.357*/358if (FALSE == _gc->getRealtimeDelegate()->requestExclusiveVMAccess(_threadWaitingOnMainThreadMonitor, FALSE /* do not block */, &gcPriority)) {359didGC = false;360goto exit;361}362_gc->setGCThreadPriority(env->getOmrVMThread(), gcPriority);363}364365omrthread_monitor_notify(_mainThreadMonitor);366/* set the waiting thread to NULL while we are in the _mainThreadMonitor so that nobody else will notify the waiting thread */367_threadWaitingOnMainThreadMonitor = NULL;368369exit:370if (_extensions->trackMutatorThreadCategory) {371/* Done doing GC, reset the category back to the old one */372omrthread_set_category(omrthread_self(), 0, J9THREAD_TYPE_SET_GC);373}374375omrthread_monitor_exit(_mainThreadMonitor);376_gc->getRealtimeDelegate()->postRequestExclusiveVMAccess(thr);377378return didGC;379}380381uintptr_t382MM_Scheduler::getTaskThreadCount(MM_EnvironmentBase *env)383{384if (env->_currentTask == NULL) {385return 1;386}387return env->_currentTask->getThreadCount();388}389390void391MM_Scheduler::waitForMutatorsToStop(MM_EnvironmentRealtime *env)392{393/* assumption: only main enters this */394OMRPORT_ACCESS_FROM_ENVIRONMENT(env);395396/* we need to record how long it took to wait for the mutators to stop */397U_64 exclusiveAccessTime = omrtime_hires_clock();398399/* The time before acquiring exclusive VM access is charged to the mutator but the time400* during the acquisition is conservatively charged entirely to the GC. */401_utilTracker->addTimeSlice(env, env->getTimer(), true);402omrthread_monitor_enter(_mainThreadMonitor);403/* If main GC thread gets here without anybody requesting exclusive access for us404* (possible in a shutdown scenario after we kill alarm thread), the thread will request405* exclusive access for itself.406* requestExclusiveVMAccess is invoked atomically with _mode being set to WAKING_GC407* under mainThreadMonitor (see continueGC). Therefore, we check here if mode is not408* WAKING_GC, and only then we request exclusive assess for ourselves.409* TODO: This approach is just to fix some timing holes in shutdown. Consider removing this410* "if" statement and fix alarm thread not to die before requesting exclusive access for us.411*/412if (_mainThreadMustShutDown && _mode != WAKING_GC) {413uintptr_t gcPriority = 0;414_gc->getRealtimeDelegate()->requestExclusiveVMAccess(env, TRUE /* block */, &gcPriority);415_gc->setGCThreadPriority(env->getOmrVMThread(), gcPriority);416}417/* Avoid another attempt to start up GC increment */418_mode = STOP_MUTATOR;419omrthread_monitor_exit(_mainThreadMonitor);420421_gc->getRealtimeDelegate()->waitForExclusiveVMAccess(env, _exclusiveVMAccessRequired);422423_mode = RUNNING_GC;424425_extensions->globalGCStats.metronomeStats._microsToStopMutators = omrtime_hires_delta(exclusiveAccessTime, omrtime_hires_clock(), OMRPORT_TIME_DELTA_IN_MICROSECONDS);426}427428void429MM_Scheduler::startMutators(MM_EnvironmentRealtime *env) {430_mode = WAKING_MUTATOR;431_gc->getRealtimeDelegate()->releaseExclusiveVMAccess(env, _exclusiveVMAccessRequired);432}433434void435MM_Scheduler::startGCTime(MM_EnvironmentRealtime *env, bool isDoubleBeat)436{437if (env->isMainThread()) {438setStartTimeOfCurrentGCSlice(_utilTracker->addTimeSlice(env, env->getTimer(), false));439}440}441442void443MM_Scheduler::stopGCTime(MM_EnvironmentRealtime *env)444{445if (env->isMainThread()) {446setStartTimeOfCurrentMutatorSlice(_utilTracker->addTimeSlice(env, env->getTimer(), false));447}448}449450bool451MM_Scheduler::shouldGCDoubleBeat(MM_EnvironmentRealtime *env)452{453double targetUtilization = _utilTracker->getTargetUtilization();454if (targetUtilization <= 0.0) {455return true;456}457I_32 maximumAllowedConsecutiveBeats = (I_32) (1.0 / targetUtilization);458if (_currentConsecutiveBeats >= maximumAllowedConsecutiveBeats) {459return false;460}461/* Note that shouldGCDoubleBeat is only called by the main thread, this means we462* can call addTimeSlice without checking for isMainThread() */463_utilTracker->addTimeSlice(env, env->getTimer(), false);464double excessTime = (_utilTracker->getCurrentUtil() - targetUtilization) * window;465double excessBeats = excessTime / beat;466return (excessBeats >= 2.0);467}468469bool470MM_Scheduler::shouldMutatorDoubleBeat(MM_EnvironmentRealtime *env, MM_Timer *timer)471{472_utilTracker->addTimeSlice(env, timer, true);473474/* The call to currentUtil will modify the timeSlice array, so calls to shouldMutatorDoubleBeat475* must be protected by a mutex (which is indeed currently the case) */476double curUtil = _utilTracker->getCurrentUtil();477double excessTime = (curUtil - _utilTracker->getTargetUtilization()) * window;478double excessBeats = excessTime / beat;479return (excessBeats <= 1.0);480}481482void483MM_Scheduler::reportStartGCIncrement(MM_EnvironmentRealtime *env)484{485OMRPORT_ACCESS_FROM_ENVIRONMENT(env);486487if(_completeCurrentGCSynchronously) {488_completeCurrentGCSynchronouslyMainThreadCopy = true;489U_64 exclusiveAccessTimeMicros = 0;490U_64 meanExclusiveAccessIdleTimeMicros = 0;491492Trc_MM_SystemGCStart(env->getLanguageVMThread(),493_extensions->heap->getApproximateActiveFreeMemorySize(MEMORY_TYPE_NEW),494_extensions->heap->getActiveMemorySize(MEMORY_TYPE_NEW),495_extensions->heap->getApproximateActiveFreeMemorySize(MEMORY_TYPE_OLD),496_extensions->heap->getActiveMemorySize(MEMORY_TYPE_OLD),497(_extensions-> largeObjectArea ? _extensions->heap->getApproximateActiveFreeLOAMemorySize(MEMORY_TYPE_OLD) : 0 ),498(_extensions-> largeObjectArea ? _extensions->heap->getActiveLOAMemorySize(MEMORY_TYPE_OLD) : 0 )499);500501exclusiveAccessTimeMicros = omrtime_hires_delta(0, env->getExclusiveAccessTime(), OMRPORT_TIME_DELTA_IN_MICROSECONDS);502meanExclusiveAccessIdleTimeMicros = omrtime_hires_delta(0, env->getMeanExclusiveAccessIdleTime(), OMRPORT_TIME_DELTA_IN_MICROSECONDS);503Trc_MM_ExclusiveAccess(env->getLanguageVMThread(),504(U_32)(exclusiveAccessTimeMicros / 1000),505(U_32)(exclusiveAccessTimeMicros % 1000),506(U_32)(meanExclusiveAccessIdleTimeMicros / 1000),507(U_32)(meanExclusiveAccessIdleTimeMicros % 1000),508env->getExclusiveAccessHaltedThreads(),509env->getLastExclusiveAccessResponder(),510env->exclusiveAccessBeatenByOtherThread());511512_gc->reportSyncGCStart(env, _completeCurrentGCSynchronouslyReason, _completeCurrentGCSynchronouslyReasonParameter);513}514515/* GC start/end are reported at each GC increment,516* not at the beginning/end of a GC cycle,517* since no Java code is supposed to run between those two events */518_extensions->globalGCStats.metronomeStats.clearStart();519_gc->reportGCStart(env);520TRIGGER_J9HOOK_MM_PRIVATE_METRONOME_INCREMENT_START(_extensions->privateHookInterface, env->getOmrVMThread(), omrtime_hires_clock(), J9HOOK_MM_PRIVATE_METRONOME_INCREMENT_START, _extensions->globalGCStats.metronomeStats._microsToStopMutators);521522_currentConsecutiveBeats = 1;523startGCTime(env, false);524525_gc->flushCachesForGC(env);526}527528void529MM_Scheduler::reportStopGCIncrement(MM_EnvironmentRealtime *env, bool isCycleEnd)530{531/* assumption: only main enters this */532533stopGCTime(env);534535/* This can not be combined with the reportGCCycleEnd below as it has to happen before536* the incrementEnd event is triggered.537*/538if (isCycleEnd) {539if (_completeCurrentGCSynchronously) {540/* The requests for Sync GC made at the very end of541* GC cycle might not had a chance to make the local copy542*/543if (_completeCurrentGCSynchronouslyMainThreadCopy) {544Trc_MM_SystemGCEnd(env->getLanguageVMThread(),545_extensions->heap->getApproximateActiveFreeMemorySize(MEMORY_TYPE_NEW),546_extensions->heap->getActiveMemorySize(MEMORY_TYPE_NEW),547_extensions->heap->getApproximateActiveFreeMemorySize(MEMORY_TYPE_OLD),548_extensions->heap->getActiveMemorySize(MEMORY_TYPE_OLD),549(_extensions->largeObjectArea ? _extensions->heap->getApproximateActiveFreeLOAMemorySize(MEMORY_TYPE_OLD) : 0 ),550(_extensions->largeObjectArea ? _extensions->heap->getActiveLOAMemorySize(MEMORY_TYPE_OLD) : 0 )551);552_gc->reportSyncGCEnd(env);553_completeCurrentGCSynchronouslyMainThreadCopy = false;554}555_completeCurrentGCSynchronously = false;556_completeCurrentGCSynchronouslyReason = UNKOWN_REASON;557}558}559560OMRPORT_ACCESS_FROM_ENVIRONMENT(env);561TRIGGER_J9HOOK_MM_PRIVATE_METRONOME_INCREMENT_END(_extensions->privateHookInterface, env->getOmrVMThread(), omrtime_hires_clock(), J9HOOK_MM_PRIVATE_METRONOME_INCREMENT_END,5620, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0563);564565/* GC start/end are reported at each GC increment,566* not at the beginning/end of a GC cycle,567* since no Java code is supposed to run between those two events */568_gc->reportGCEnd(env);569_extensions->globalGCStats.metronomeStats.clearEnd();570}571572void573MM_Scheduler::restartMutatorsAndWait(MM_EnvironmentRealtime *env)574{575startMutators(env);576577omrthread_monitor_enter(_mainThreadMonitor);578/* Atomically change mode to MUTATOR and set threadWaitingOnMainThreadMonitor579* (only after the main is fully stoped, we switch from WAKING_MUTATOR to MUTATOR) */580_mode = MUTATOR;581_threadWaitingOnMainThreadMonitor = env;582583/* If we're shutting down, we don't want to wait. Note that this is safe584* since on shutdown, the only mutator thread left is the thread that is585* doing the shutdown.586*/587if (!_mainThreadMustShutDown) {588omrthread_monitor_wait(_mainThreadMonitor);589/* Main is awoken to either do another increment of GC or590* to shutdown (but never both)591*/592Assert_MM_true((isGCOn() && !_mainThreadMustShutDown) || (!_gcOn &&_mainThreadMustShutDown));593}594omrthread_monitor_exit(_mainThreadMonitor);595}596597bool598MM_Scheduler::shouldGCYield(MM_EnvironmentRealtime *env, U_64 timeSlack)599{600return internalShouldGCYield(env, timeSlack);601}602603/**604* Test whether it's time for the GC to yield, and whether yielding is currently enabled.605* To enhance the generality of methods that may call this method, the call may occur on606* a non-GC thread, in which case this method does nothing.607* @param timeSlack a slack factor to apply to time-based scheduling608* @param location the phase of the GC during which this call is occurring (for tracing: in609* some cases may be approximate).610* @return true if the GC thread should yield, false otherwise611*/612MMINLINE bool613MM_Scheduler::internalShouldGCYield(MM_EnvironmentRealtime *env, U_64 timeSlack)614{615if (_completeCurrentGCSynchronouslyMainThreadCopy) {616/* If we have degraded to a synchronous GC, don't yield until finished */617return false;618}619/* Be harmless when called indirectly on mutator thread */620if (env->getThreadType() == MUTATOR_THREAD) {621return false;622}623/* The GC does not have to yield when ConcurrentTracing or ConcurrentSweeping is624* enabled since the GC is not holding exclusive access.625*/626if (_gc->isCollectorConcurrentTracing() || _gc->isCollectorConcurrentSweeping()) {627return false;628}629630/* If at least one thread thinks we should yield, than all should yield.631* Discrepancy may happen due different timeSlack that GC threads may have */632if (_shouldGCYield) {633return true;634}635636if (env->hasDistanceToYieldTimeCheck()) {637return false;638}639640I_64 nanosLeft = _utilTracker->getNanosLeft(env, getStartTimeOfCurrentGCSlice());641if (nanosLeft > 0) {642if ((U_64)nanosLeft > timeSlack) {643return false;644}645}646_shouldGCYield = true;647return true;648}649650bool651MM_Scheduler::condYieldFromGCWrapper(MM_EnvironmentBase *env, U_64 timeSlack)652{653return condYieldFromGC(env, timeSlack);654}655656/**657* Test whether it's time for the GC to yield, and whether yielding is currently enabled, and658* if appropriate actually do the yielding. To enhance the generality of methods that may659* call this method, the call may occur on a non-GC thread, in which case this method does660* nothing.661* @param location the phase of the GC during which this call is occurring (for tracing: in662* some cases may be approximate).663* @param timeSlack a slack factor to apply to time-based scheduling664* @return true if yielding actually occurred, false otherwise665*/666bool667MM_Scheduler::condYieldFromGC(MM_EnvironmentBase *envBase, U_64 timeSlack)668{669MM_EnvironmentRealtime *env = MM_EnvironmentRealtime::getEnvironment(envBase);670671if (env->getYieldDisableDepth() > 0) {672return false;673}674if (!internalShouldGCYield(env, timeSlack)) {675return false;676}677678yieldFromGC(env, true);679680env->resetCurrentDistanceToYieldTimeCheck();681682return true;683}684685void MM_Scheduler::yieldFromGC(MM_EnvironmentRealtime *env, bool distanceChecked)686{687assert(!_gc->isCollectorConcurrentTracing());688assert(!_gc->isCollectorConcurrentSweeping());689if (env->isMainThread()) {690if (_yieldCollaborator) {691/* wait for workers to yield/sync */692_yieldCollaborator->yield(env);693}694695_sharedBarrierState = shouldGCDoubleBeat(env);696697if (_sharedBarrierState) {698_currentConsecutiveBeats += 1;699startGCTime(env, true);700} else {701reportStopGCIncrement(env);702env->reportScanningSuspended();703Assert_MM_true(isGCOn());704restartMutatorsAndWait(env);705waitForMutatorsToStop(env);706env->reportScanningResumed();707reportStartGCIncrement(env);708_shouldGCYield = false;709}710711if (_yieldCollaborator) {712_yieldCollaborator->resumeWorkersFromYield(env);713}714715} else {716/* Worker only running here. _yieldCollaborator instance exists for sure */717env->reportScanningSuspended();718_yieldCollaborator->yield(env);719env->reportScanningResumed();720}721}722723void724MM_Scheduler::prepareThreadsForTask(MM_EnvironmentBase *env, MM_Task *task, uintptr_t threadCount)725{726omrthread_monitor_enter(_workerThreadMutex);727_workerThreadsReservedForGC = true;728729task->setSynchronizeMutex(_synchronizeMutex);730731for (uintptr_t index=0; index < threadCount; index++) {732_statusTable[index] = worker_status_reserved;733_taskTable[index] = task;734}735736wakeUpThreads(threadCount);737omrthread_monitor_exit(_workerThreadMutex);738739pushYieldCollaborator(((MM_IncrementalParallelTask *)task)->getYieldCollaborator());740}741742void743MM_Scheduler::completeTask(MM_EnvironmentBase *env)744{745if (env->isMainThread()) {746popYieldCollaborator();747}748MM_ParallelDispatcher::completeTask(env);749}750751bool752MM_Scheduler::startUpThreads()753{754OMRPORT_ACCESS_FROM_OMRVM(_vm);755MM_EnvironmentRealtime env(_vm);756757if (_extensions->gcThreadCount > _osInterface->getNumbersOfProcessors()) {758omrtty_printf("Please specify fewer GC threads than the number of physical processors.\n");759return false;760}761762/* Start up the GC threads */763if (!MM_ParallelDispatcher::startUpThreads()) {764return false;765}766767/* At this point, all GC threads have signalled that they are ready.768* However, because Metronome uses omrthread_suspend/omrthread_resume to stop and769* start threads, there is a race: the thread may have been preempted after770* signalling but before suspending itself. An alternative may be to use771* omrthread_park/unpark.772*/773_isInitialized = true;774775/* Now that the GC threads are started, it is safe to start the alarm thread */776_alarmThread = MM_MetronomeAlarmThread::newInstance(&env);777if (_alarmThread == NULL) {778omrtty_printf("Unable to initialize alarm thread for time-based GC scheduling\n");779omrtty_printf("Most likely cause is non-supported version of OS\n");780return false;781}782783if (verbose() >= 1) {784showParameters(&env);785}786787return true;788}789790/**791* @copydoc MM_ParallelDispatcher::recomputeActiveThreadCount()792* This function is called at the start of a complete GC cycle to calculate the number of793* GC threads to use for the cycle.794*/795void796MM_Scheduler::recomputeActiveThreadCount(MM_EnvironmentBase *env)797{798_activeThreadCount = _threadCount;799}800801/**802* @copydoc MM_ParallelDispatcher::getThreadPriority()803*/804uintptr_t805MM_Scheduler::getThreadPriority()806{807/* this is the priority that the threads are started with */808return J9THREAD_PRIORITY_USER_MAX + 1;809}810811/**812* @copydoc MM_MetronomeDispatcher::workerEntryPoint()813*/814void815MM_Scheduler::workerEntryPoint(MM_EnvironmentBase *envModron)816{817MM_EnvironmentRealtime *env = MM_EnvironmentRealtime::getEnvironment(envModron);818819uintptr_t workerID = env->getWorkerID();820821setThreadInitializationComplete(env);822823omrthread_monitor_enter(_workerThreadMutex);824825while(worker_status_dying != _statusTable[workerID]) {826/* Wait for a task to be dispatched to the worker thread */827while(worker_status_waiting == _statusTable[workerID]) {828omrthread_monitor_wait(_workerThreadMutex);829}830831if(worker_status_reserved == _statusTable[workerID]) {832/* Found a task to dispatch to - do prep work for dispatch */833acceptTask(env);834omrthread_monitor_exit(_workerThreadMutex);835836env->_currentTask->run(env);837838omrthread_monitor_enter(_workerThreadMutex);839/* Returned from task - do clean up work from dispatch */840completeTask(env);841}842}843omrthread_monitor_exit(_workerThreadMutex);844}845846/**847* @copydoc MM_ParallelDispatcher::mainEntryPoint()848*/849void850MM_Scheduler::mainEntryPoint(MM_EnvironmentBase *envModron)851{852MM_EnvironmentRealtime *env = MM_EnvironmentRealtime::getEnvironment(envModron);853854setThreadInitializationComplete(env);855856omrthread_monitor_enter(_mainThreadMonitor);857_threadWaitingOnMainThreadMonitor = env;858omrthread_monitor_wait(_mainThreadMonitor);859omrthread_monitor_exit(_mainThreadMonitor);860861/* We want to execute the body of the do-while (run a gc) if a shutdown has862* been requested at the same time as the first gc. In other words, we want863* a gc to complete before shutting down.864*865* We however do not want to execute a gc if it hasn't been requested. The866* outer while loop guarantees this. It is a while loop (as opposed to an867* if) to cover the case of simultaneous gc/shutdown while waiting in868* stopGCIntervalAndWait. Again, we want to complete the gc in that case.869*/870while (isGCOn()) {871do {872/* Before starting a new GC, recompute the number of threads to use */873recomputeActiveThreadCount(env);874waitForMutatorsToStop(env);875/* note that the cycle and increment start events will be posted from MM_RealtimeGC::internalPreCollect */876_gc->_memorySubSpace->collect(env, _gcCode);877restartMutatorsAndWait(env);878879/* We must also check for the _mainThreadMustShutDown flag since if we880* try to shutdown while we're in a stopGCIntervalAndWait, the GC will881* continue potentially changing the status of the main thread882*/883} while ((worker_status_dying != _statusTable[env->getWorkerID()] && !_mainThreadMustShutDown));884}885/* TODO: tear down the thread before exiting */886}887888/**889* If there is an ongoing GC cycle complete it890*/891void892MM_Scheduler::completeCurrentGCSynchronously(MM_EnvironmentRealtime *env)893{894omrthread_monitor_enter(_vm->_gcCycleOnMonitor);895if (_vm->_gcCycleOn || isGCOn()) {896_completeCurrentGCSynchronously = true;897_completeCurrentGCSynchronouslyReason = VM_SHUTDOWN;898899/* wait till get notified by main that the cycle is finished */900omrthread_monitor_wait(_vm->_gcCycleOnMonitor);901}902omrthread_monitor_exit(_vm->_gcCycleOnMonitor);903}904905/**906* @copydoc MM_ParallelDispatcher::wakeUpThreads()907*/908void909MM_Scheduler::wakeUpThreads(uintptr_t count)910{911assert1(count > 0);912913/* Resume the main thread */914omrthread_monitor_enter(_mainThreadMonitor);915omrthread_monitor_notify(_mainThreadMonitor);916omrthread_monitor_exit(_mainThreadMonitor);917918if (count > 1) {919wakeUpWorkerThreads(count - 1);920}921}922923/**924* Wakes up `count` worker threads. This function will actually busy wait until925* `count` number of workers have been resumed from the suspended state.926*927* @param count Number of worker threads to wake up928*/929void930MM_Scheduler::wakeUpWorkerThreads(uintptr_t count)931{932omrthread_monitor_notify_all(_workerThreadMutex);933}934935/**936* @copydoc MM_ParallelDispatcher::shutDownThreads()937*/938void939MM_Scheduler::shutDownThreads()940{941/* This will stop threads from requesting another GC cycle to start*/942_isInitialized = false;943944/* If the GC is currently in a Cycle complete it before we shutdown */945completeCurrentGCSynchronously();946947/* Don't kill the main thread before the alarm thread since the alarm thread948* may still refer to the main thread if a continueGC happens to occur during949* shutdown.950*/951shutDownWorkerThreads();952953/* Don't kill the alarm thread until after the GC worker threads, since it may954* be needed to drive a final synchronous GC */955if (_alarmThread) {956MM_EnvironmentBase env(_vm);957_alarmThread->kill(&env);958_alarmThread = NULL;959}960961/* Now that the alarm and trace threads are killed, we can shutdown the main thread */962shutDownMainThread();963}964965/**966* Signals the workers to shutdown, will block until they are all shutdown.967*968* @note Assumes all threads are live before the function is called (ie: this969* must be called before shutDownMainThread)970*/971void972MM_Scheduler::shutDownWorkerThreads()973{974/* If _threadShutdownCount is 1, only the main must shutdown, if 0,975* no shutdown required (happens when args passed to java are invalid976* so the vm doesn't actually start up, ex: -Xgc:threads=5 on a 4-way977* box)978*/979if (_threadShutdownCount <= 1) {980return;981}982983omrthread_monitor_enter(_workerThreadMutex);984985for (uintptr_t threadIndex = 1; threadIndex < _threadCountMaximum; threadIndex++) {986_statusTable[threadIndex] = worker_status_dying;987}988989_threadCount = 1;990991wakeUpWorkerThreads(_threadShutdownCount - 1);992993omrthread_monitor_exit(_workerThreadMutex);994995/* -1 because the thread shutdown count includes the main thread */996omrthread_monitor_enter(_dispatcherMonitor);997998while (1 != _threadShutdownCount) {999omrthread_monitor_wait(_dispatcherMonitor);1000}10011002omrthread_monitor_exit(_dispatcherMonitor);1003}10041005/**1006* Signals the main to shutdown, will block until the thread is shutdown.1007*1008* @note Assumes the main thread is the last gc thread left (ie: this must be1009* called after shutDownWorkerThreads)1010*/1011void1012MM_Scheduler::shutDownMainThread()1013{1014omrthread_monitor_enter(_workerThreadMutex);1015_statusTable[0] = worker_status_dying;1016omrthread_monitor_exit(_workerThreadMutex);10171018/* Note: Calling wakeUpThreads at this point would be unsafe since there is1019* more than 1 location where the main thread could be waiting and the one1020* in stopGcIntervalAndWait [which ultimately gets invoked by the1021* condYieldFromGC] assumes that a request for exclusive VM access on behalf1022* of the main has been made. Blindly notifying the monitor [as1023* wakeUpThreads does] would cause the main thread to wait for exclusive1024* access without requesting for it first, causing a hang.1025*/1026omrthread_monitor_enter(_mainThreadMonitor);1027_mainThreadMustShutDown = true;1028omrthread_monitor_notify(_mainThreadMonitor);1029omrthread_monitor_exit(_mainThreadMonitor);10301031omrthread_monitor_enter(_dispatcherMonitor);1032while (0 != _threadShutdownCount) {1033omrthread_monitor_wait(_dispatcherMonitor);1034}1035omrthread_monitor_exit(_dispatcherMonitor);1036}10371038/**1039* Check to see if it is time to do the next GC increment. If beatNanos time1040* has elapsed since the end of the last GC increment then start the next1041* increment now.1042*/1043void1044MM_Scheduler::startGCIfTimeExpired(MM_EnvironmentBase *envModron)1045{1046MM_EnvironmentRealtime *env = MM_EnvironmentRealtime::getEnvironment(envModron);1047if (isInitialized() && isGCOn() && env->getTimer()->hasTimeElapsed(getStartTimeOfCurrentMutatorSlice(), beatNanos)) {1048continueGC(env, TIME_TRIGGER, 0, env->getOmrVMThread(), true);1049}1050}10511052uintptr_t1053MM_Scheduler::incrementMutatorCount()1054{1055return MM_AtomicOperations::add(&_mutatorCount, 1);1056}10571058extern "C" {10591060void1061j9gc_startGCIfTimeExpired(OMR_VMThread* vmThread)1062{1063MM_EnvironmentRealtime *env = MM_EnvironmentRealtime::getEnvironment(vmThread);1064MM_Scheduler *scheduler = (MM_Scheduler *)env->getExtensions()->dispatcher;1065scheduler->startGCIfTimeExpired(env);1066}10671068}106910701071