blob: ae8f91843d92a8ad32f180cb95d947fc3a61a71e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* @author Salikh Zakirov, Pavel Afremov
*/
package java.lang;
/**
* Dedicated finalizer thread.
*/
class FinalizerThread extends Thread {
/**
* Wake up permanent finalizer threads or create additional
* temporary threads, and wait until they start.
* @param wait - says to wait untill all finalizer threads complite work.
*/
public static void startFinalization(boolean wait) {
if (!enabled) {
return;
}
//wakeup finalazer threads
wakeupFinalization();
// work balance system creates balancing threads
spawnBalanceThreads();
// if flag is raised up waits finalization complete
if (wait) {
waitFinalizationEnd();
}
}
/**
* VM calls this thread from Runtime.runFinalization().
*/
public static void runFinalization() {
/* BEGIN: added for NATIVE FINALIZER THREAD */
if(NATIVE_FINALIZER_THREAD)
runFinalizationInNativeFinalizerThreads();
else
/* END: added for NATIVE FINALIZER THREAD */
startFinalization(true);
}
/*
* Staic package private part
*/
/**
* Initializes finalization system. Starts permanent thread.
*/
static void initialize() {
/* BEGIN: added for NATIVE FINALIZER THREAD */
if (NATIVE_FINALIZER_THREAD)
return;
/* END: added for NATIVE FINALIZER THREAD */
if (TRACE) {
trace("FinalizerThread: static initialization started");
}
processorsQuantity = getProcessorsQuantity();
// -XDvm.finalize=0 disables the finalizer thread
if (! isNativePartEnabled()) {
warn("finalizer thread have not been created");
} else {
(new FinalizerThread(true)).start();
enabled = true;
}
if (TRACE) {
trace("FinalizerThread: static initialization complete");
}
}
/**
* VM calls this method to request finalizer thread shutdown.
*/
static void shutdown(boolean startFinalizationOnExit) {
/* BEGIN: added for NATIVE FINALIZER THREAD */
if(NATIVE_FINALIZER_THREAD) {
finalizerShutDown(startFinalizationOnExit);
return;
}
/* END: added for NATIVE FINALIZER THREAD */
if (TRACE) {
trace("shutting down finalizer thread");
}
if (startFinalizationOnExit) {
doFinalizationOnExit();
}
synchronized (workLock) {
shutdown = true;
workLock.notifyAll();
}
}
/* added for NATIVE FINALIZER THREAD
* A flag to indicate whether the finalizer threads are native threads or Java threads.
*/
private static final boolean NATIVE_FINALIZER_THREAD = getNativeFinalizerThreadFlagFromVM();
/* BEGIN: These three methods are added for NATIVE FINALIZER THREAD */
/**
* This method gets the flag that indicates
* whether VM uses native finalizer threads or Java finalizer threads.
*/
private static native boolean getNativeFinalizerThreadFlagFromVM();
/**
* This method implements runFinalization() method in native finalizer threads.
*/
private static native void runFinalizationInNativeFinalizerThreads();
/**
* This method does finalization work related to VM shutdown in native finalizer threads.
*/
private static native void finalizerShutDown(boolean finalizeOnExit);
/* END: These three methods are added for NATIVE FINALIZER THREAD */
/*
* Staic private part
*/
// Maximum quantity of finalizers threads
private static final int MAX_THREADS = 256;
// create separate class for finalizer workLock to easier debugging
private static class FinalizerWorkLock {};
// Lock used to wake up permanent finalizer threads and synchronize change of state of work
private static Object workLock = new FinalizerWorkLock();
// Shows that finalizers works in state on exit
// Used by VM. It should be package private to eliminate compiler warning.
static boolean onExit = false;
// create separate class for finalizer waitFinishLock to easier debugging
private static class FinalizerWaitFinishLock {};
/*
* Lock used to to synchronize quantity of active threads and to wake up finalizer starter thread
* when new finalization tasks aren't available and finalizer threads finish work.
*/
private static Object waitFinishLock = new FinalizerWaitFinishLock();
/*
* The Quantity of active finalizer threads which shoud be stopped to wake up finalizer starter
* thread. When is thread is started this counter is incremented when thread is stoppig it's decremeted.
* Zero means finalization tasks aren't available and finalizer threads aren't working.
*/
private static int waitFinishCounter = 0;
// Indicates processors quantity in the system
private static int processorsQuantity;
// true means finalizer threads is enabled
private static boolean enabled = false;
// true means finalizer threads need to shut down
private static boolean shutdown = false;
/**
* Gets quantity of processors in the System
*/
private static native int getFinalizersQuantity();
/**
* Gets quantity of processors in the System
*/
private static native int getProcessorsQuantity();
/**
* Do finalizations in native mode for specified quantity of finalizable object
*/
private static native int doFinalization(int quantity);
private static native void fillFinalizationQueueOnExit();
/**
* Returns true if native part of finalization system is
* turned on, and false otherwise.
*/
private static native boolean isNativePartEnabled();
/**
* Returns true if current thread is finalizer thread
*/
private static boolean isFinalizerThread() {
return ((Thread.currentThread()) instanceof FinalizerThread);
}
/**
* Wakes up permanent finalizer threads
*/
private static void wakeupFinalization() {
synchronized (workLock) {
workLock.notifyAll();
}
}
/**
* Waits when finalization work is completed and then returns.
*/
private static void waitFinalizationEnd() {
if (isFinalizerThread()) {
return;
}
synchronized (waitFinishLock) {
if (waitFinishCounter > 0) {
try {
waitFinishLock.wait();
} catch (InterruptedException e) {}
}
}
}
/**
* Starts additional temporary threads to make sure
* finalization system keeps number of unfinalized objects
* at acceptable level.
* Waits until created thraed starts and then return.
* It is called from startFinalization() only.
*/
private static void spawnBalanceThreads() {
/* finalizer threads shouldn't be spawn by finalizer thread,
* in this case balancing can't work
*/
if (isFinalizerThread()) {
return;
}
FinalizerThread newThread = null;
if (waitFinishCounter >= MAX_THREADS) {
Thread.yield();
} else {
try {
for (int i = 0; i < processorsQuantity; i++) {
newThread = new FinalizerThread(false);
synchronized (newThread.startLock){
newThread.start();
// waiting when new thread will be started
try {
newThread.startLock.wait();
} catch (InterruptedException e) {}
}
}
} catch (OutOfMemoryError e) {}
}
}
private static void doFinalizationOnExit() {
System.gc();
startFinalization(true);
fillFinalizationQueueOnExit();
synchronized (workLock) {
onExit = true;
}
startFinalization(true);
}
/**
* Waits when new finalization task are available and finalizer threads can continue work
*/
private static void waitNewTask() {
if (getFinalizersQuantity() != 0) {
return;
}
synchronized (workLock) {
synchronized (waitFinishLock) {
waitFinishCounter --;
if (waitFinishCounter == 0) {
waitFinishLock.notifyAll();
}
}
while ((getFinalizersQuantity() == 0) && (!shutdown)) {
try {
workLock.wait();
} catch (InterruptedException e) {}
}
synchronized (waitFinishLock) {
waitFinishCounter ++;
}
}
}
private static final boolean TRACE = false;
/**
* debug output.
*/
private static void trace (Object o) {
/*
System.err.println(o);
System.err.flush();
*/
}
/**
* Prints warning.
*/
private static void warn (Object o) {
System.err.println("FinalizerThread: " + o);
System.err.flush();
}
public FinalizerThread () {
this (true);
}
// create separate class for finalizer startLock to easier debugging
private class FinalizerStartLock {};
private FinalizerStartLock startLock = new FinalizerStartLock();
protected FinalizerThread (boolean permanent) {
super(Thread.systemThreadGroup, "FinalizerThread");
this.permanent = permanent;
this.setDaemon(true);
}
public void run() {
// don't put any code here, before try block
try {
synchronized (waitFinishLock) {
waitFinishCounter ++;
}
/* notify that finalizer thread has started.
* Don't put any code whith any memory allocation before here!
* It should be granted that notify is called in any case!
*/
synchronized (startLock) {
startLock.notify();
}
if (TRACE) {
if (permanent) {
trace("permanent finalization thread started");
} else {
trace("temporary finalization thread started");
}
}
while (true) {
int n = doFinalization(128);
synchronized (workLock) {
if (shutdown) {
if (TRACE) {
trace("terminated by shutdown request");
}
break;
}
}
if (0 == n) {
if (permanent) {
waitNewTask();
} else {
break;
}
}
}
} catch (Throwable th) {
warn("FinalizerThread terminated by " + th);
throw new RuntimeException("FinalizerThread interrupted", th);
} finally {
synchronized (waitFinishLock) {
waitFinishCounter --;
if (waitFinishCounter == 0) {
waitFinishLock.notifyAll();
}
}
if (TRACE) {
trace("FinalizerThread completed");
}
}
}
/**
* Indicates that thread shouldn't be destroyed when finalization is complete
*/
private boolean permanent;
}