blob: 1dcced13c39db3ecc7ecf8ea418eddd051d2f516 [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 Intel, Pavel Afremov
*/
#define LOG_DOMAIN "vm.object_queue"
#include "cxxlog.h"
#include "open/types.h"
#include "lock_manager.h"
#include "object_layout.h"
#include "jthread.h"
#include "vm_process.h"
#include "Class.h"
#include "environment.h"
#include "ini.h"
#include "m2n.h"
#include "exceptions.h"
#include "compile.h"
#include "nogc.h"
#include "jit_runtime_support.h"
#include "finalize.h"
#include "vtable.h"
#include "jit_import_rt.h"
#include "finalizer_thread.h" /* added for NATIVE FINALIZER THREAD */
#include "ref_enqueue_thread.h" /* added for NATIVE REFERENCE ENQUEUE THREAD */
#include "classloader.h"
#include "thread_generic.h"
#ifndef USE_GC_STATIC
__declspec(dllexport)
#endif
// FINALIZER_THREAD means that thread is finalizer thread
#define FINALIZER_THREAD 0x1
// FINALIZER_STARTER means that thread is running inside run_finalizers function
#define FINALIZER_STARTER 0x2
//
// This code holds the logic that deals with finalization as well as weak/soft/phantom
// references. Finalization runs arbitrary code that can include synchronization logic.
//
// There are two different times that finalizers and enqueues can be run.
//
// The first is to have a separate thread that receives the objects that need to be
// finalized and executes them sometime after the GC has completed. This is probable
// what was intended by the designers of the finalization and weak/soft/phantom
// reference features of the language.
//
// The second approach that can be taken is to run the finalization or enqueues code
// immediately after the gc is complete prior to resuming the thread that caused the gc
// to happen.
//
// The first scheme was implemented for finalization and second is used for
// weak/soft/phantom references.
//
hylatch_t begin_run_finalizer;
class Object_Queue
{
ManagedObject **objects;
unsigned capacity;
unsigned num_objects;
Lock_Manager objects_lock;
const char* log_domain;
void reallocate(unsigned new_capacity);
public:
Object_Queue();
Object_Queue(const char* log_domain);
~Object_Queue(){
STD_FREE(objects);
}
void add_object(ManagedObject *p_obj);
ManagedObject* remove_object();
int getLength();
void enumerate_for_gc();
}; //Object_Queue
class Objects_To_Finalize: public Object_Queue
{
// workaround part to ignore classes java.nio.charset.CharsetEncoder
// java.io.FileDescriptor & java.io.FileOutputStream during finalization on exit
bool is_class_ignored(Class* test);
bool classes_cached;
Lock_Manager ignore_lock;
Class* FileDescriptor;
Class* FileOutputStream;
//jobject get_work_lock();
//jboolean* get_work_lock();
//jboolean* get_work_lock();
bool fields_obtained;
void check_fields_obtained();
Lock_Manager obtain_fields_lock;
jobject work_lock;
jboolean* shutdown;
jboolean* on_exit;
public:
Objects_To_Finalize() : Object_Queue("finalize") {
classes_cached = false;
fields_obtained = false;
work_lock = NULL;
on_exit = NULL;
};
// redefine of add method
void add_object(ManagedObject *p_obj);
void run_finalizers();
int do_finalization(int quantity);
void obtain_fields();
}; //Objects_To_Finalize
class References_To_Enqueue: public Object_Queue
{
public:
References_To_Enqueue() : Object_Queue("ref") {};
void enqueue_references();
}; //References_To_Enqueue
Object_Queue::Object_Queue() {
Object_Queue::Object_Queue("unknown");
}
Object_Queue::Object_Queue(const char* log_domain)
{
objects = 0;
capacity = 0;
num_objects = 0;
this-> log_domain = log_domain;
reallocate(128);
} //Object_Queue::Object_Queue
/**
* Allocates array to save objects. Should be called from synchronized block.
* Now it's called from add_object only.
*/
void Object_Queue::reallocate(unsigned new_capacity)
{
ManagedObject **new_table =
(ManagedObject **)STD_MALLOC(new_capacity * sizeof(ManagedObject *));
// print trace info about queue enlarge
TRACE2( log_domain, "The " << log_domain << " queue capacity is enlarged to "
<< new_capacity << " units.");
// asserts that queue information is correct
assert(new_table);
assert(num_objects <= capacity);
// if queue already contains objects, copies it to new array and frees memory for previous one.
if(objects) {
memcpy(new_table, objects, num_objects * sizeof(ManagedObject *));
STD_FREE(objects);
}
// saves new queue information
objects = new_table;
capacity = new_capacity;
} //Object_Queue::reallocate
/**
* Adds object to queue
*/
void Object_Queue::add_object(ManagedObject *p_obj)
{
objects_lock._lock();
// reallocates if there is not enough place to save new one
if(num_objects >= capacity) {
reallocate(capacity * 2);
}
// asserts that queue information is correct
assert(num_objects < capacity);
// adds object to queue
objects[num_objects++] = p_obj;
objects_lock._unlock();
} //Object_Queue::add_object
/**
* Removes latest object from queue and returns it as result.
*/
ManagedObject*
Object_Queue::remove_object() {
ManagedObject* removed_object;
objects_lock._lock();
// if there is any objects in queue, remove the latest from there to return as result
if (0 < num_objects) {
removed_object = this->objects[--num_objects];
} else {
removed_object = NULL;
}
objects_lock._unlock();
return removed_object;
} //Object_Queue::remove_object
/**
* Returns length of queue
*/
int Object_Queue::getLength() {
unsigned result;
// synchronization used there to avoid of return very old value
objects_lock._lock();
result = this->num_objects;
objects_lock._unlock();
// there unfresh value can be returned, but this value was correct after function start
return result;
}
/**
* Enumerates queue for GC
*/
void Object_Queue::enumerate_for_gc()
{
// locks here because some native code can work during gc until tmn_suspend_disable() call
objects_lock._lock();
// print trace info about queue enlarge
TRACE2( log_domain, "The " << log_domain << " queue length is " << num_objects << " units");
// enumerate elements in the queue
for(unsigned i = 0; i < num_objects; i++) {
vm_enumerate_root_reference((void **)&(objects[i]), FALSE);
}
// unlock
objects_lock._unlock();
} //Object_Queue::enumerate_for_gc
void Objects_To_Finalize::add_object(ManagedObject *p_obj)
{
Class* finalizer_thread =
VM_Global_State::loader_env->java_lang_FinalizerThread_Class;
if (!finalizer_thread) {
return;
} else {
Object_Queue::add_object(p_obj);
}
} //Objects_To_Finalize::add_object
// workaround method to ignore classes
// java.io.FileDescriptor & java.io.FileOutputStream during finalization on exit
bool Objects_To_Finalize::is_class_ignored(Class* test) {
assert(!hythread_is_suspend_enabled());
if (!classes_cached) {
tmn_suspend_enable();
String* FileDescriptorName =
VM_Global_State::loader_env->string_pool.lookup("java/io/FileDescriptor");
String* FileOutputStreamName =
VM_Global_State::loader_env->string_pool.lookup("java/io/FileOutputStream");
Class* FileDescriptor =
VM_Global_State::loader_env->bootstrap_class_loader->LoadVerifyAndPrepareClass(
VM_Global_State::loader_env, FileDescriptorName);
Class* FileOutputStream =
VM_Global_State::loader_env->bootstrap_class_loader->LoadVerifyAndPrepareClass(
VM_Global_State::loader_env, FileOutputStreamName);
tmn_suspend_disable();
ignore_lock._lock();
this->FileDescriptor = FileDescriptor;
this->FileOutputStream = FileOutputStream;
classes_cached = true;
ignore_lock._unlock();
}
return ((test==FileDescriptor) || (test==FileOutputStream));
}
void Objects_To_Finalize::check_fields_obtained() {
if (!fields_obtained) {
obtain_fields();
}
}
void Objects_To_Finalize::obtain_fields() {
obtain_fields_lock._lock();
if (!fields_obtained) {
Class* finalizer_thread =
VM_Global_State::loader_env->java_lang_FinalizerThread_Class;
if (!finalizer_thread) {
obtain_fields_lock._unlock();
return;
}
Field* work_lock_field = class_lookup_field_recursive(
finalizer_thread,
"workLock", "Ljava/lang/Object;");
Field* shutdown_field = class_lookup_field_recursive(
finalizer_thread,
"shutdown", "Z");
Field* on_exit_field = class_lookup_field_recursive(
finalizer_thread,
"onExit", "Z");
assert(work_lock_field);
assert(shutdown_field);
assert(on_exit_field);
tmn_suspend_disable();
ManagedObject* work_lock_addr = get_raw_reference_pointer(
(ManagedObject **)work_lock_field->get_address());
assert(work_lock_addr);
work_lock = oh_allocate_global_handle();
work_lock->object = work_lock_addr;
assert(work_lock);
tmn_suspend_enable();
shutdown = (jboolean*) shutdown_field->get_address();
on_exit = (jboolean*) on_exit_field->get_address();
assert(shutdown);
assert(on_exit);
fields_obtained = true;
}
obtain_fields_lock._unlock();
}
void Objects_To_Finalize::run_finalizers()
{
assert(hythread_is_suspend_enabled());
Class* finalizer_thread =
VM_Global_State::loader_env->java_lang_FinalizerThread_Class;
if (!finalizer_thread) {
return;
}
check_fields_obtained();
int num_objects = getLength() + vm_get_references_quantity();
if (num_objects == 0) {
return;
}
if ((p_TLS_vmthread->finalize_thread_flags & (FINALIZER_STARTER | FINALIZER_THREAD)) != 0) {
TRACE2("finalize", "recursive finalization prevented");
return;
}
p_TLS_vmthread->finalize_thread_flags |= FINALIZER_STARTER;
TRACE2("finalize", "run_finalizers() started");
// saves curent thread exception risen before running finalizers
// if any and clears it to allow java to work
jthrowable cte = exn_get();
exn_clear();
if (FRAME_COMPILATION ==
(FRAME_COMPILATION & m2n_get_frame_type(m2n_get_last_frame()))) {
assert(work_lock);
IDATA r = jthread_monitor_try_enter(work_lock);
if (r == TM_ERROR_NONE) {
jthread_monitor_notify_all(work_lock);
jthread_monitor_exit(work_lock);
}
} else {
Method* finalize_meth = class_lookup_method_recursive(finalizer_thread,
"startFinalization", "(Z)V");
assert(finalize_meth);
jvalue args[1];
args[0].z = false;
tmn_suspend_disable();
vm_execute_java_method_array((jmethodID) finalize_meth, 0, args);
tmn_suspend_enable();
}
#ifndef NDEBUG
if (exn_raised()) {
INFO2("finalize", "Uncaught exception "
<< exn_get_name()
<< " while running a wakeFinalization in FinalizerThread");
}
#endif
exn_clear();
// restores curent thread exception risen before if any
if (NULL != cte) {
exn_raise_object(cte);
}
p_TLS_vmthread->finalize_thread_flags &= ~FINALIZER_STARTER;
} //Objects_To_Finalize::run_finalizers
int Objects_To_Finalize::do_finalization(int quantity) {
/* BEGIN: added for NATIVE FINALIZER THREAD */
Boolean native_finalizer_thread_flag = get_native_finalizer_thread_flag();
Boolean native_finalizer_shutdown, native_finalizer_on_exit;
/* END: added for NATIVE FINALIZER THREAD */
//SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_HIGHEST);
if(!native_finalizer_thread_flag) // added for NATIVE FINALIZER THREAD
p_TLS_vmthread->finalize_thread_flags = FINALIZER_THREAD;
int i;
tmn_suspend_disable();
ObjectHandle handle = oh_allocate_local_handle();
tmn_suspend_enable();
jvalue args[1];
args[0].l = (jobject) handle;
/* BEGIN: modified for NATIVE FINALIZER THREAD */
if(!native_finalizer_thread_flag){
assert(VM_Global_State::loader_env->java_lang_FinalizerThread_Class);
check_fields_obtained();
assert(shutdown);
assert(on_exit);
native_finalizer_shutdown = (Boolean)*shutdown;
native_finalizer_on_exit = (Boolean)*on_exit;
}
/* END: modified for NATIVE FINALIZER THREAD */
for (i=0; ((i<quantity)||(0==quantity)); i++) {
// shutdown flag in FinalizerThread set after finalization on exit is completed
/* BEGIN: modified for NATIVE FINALIZER THREAD */
if(native_finalizer_thread_flag)
native_finalizer_shutdown = get_finalizer_shutdown_flag();
if(native_finalizer_shutdown)
return i;
/* END: modified for NATIVE FINALIZER THREAD */
tmn_suspend_disable();
ManagedObject* object = remove_object();
handle->object = object;
tmn_suspend_enable();
if (object == NULL) {
return i;
}
if(get_native_finalizer_thread_flag()){
int finalizable_obj_num = getLength();
sched_heavy_finalizer_in_finalization(finalizable_obj_num, i);
}
tmn_suspend_disable();
assert(handle->object->vt()->clss);
Class *clss = handle->object->vt()->clss;
assert(clss);
/* BEGIN: modified for NATIVE FINALIZER THREAD */
if(native_finalizer_thread_flag) {
native_finalizer_on_exit = get_finalizer_on_exit_flag();
if(native_finalizer_on_exit && is_class_ignored(clss)) {
tmn_suspend_enable();
continue;
}
}
/* END: modified for NATIVE FINALIZER THREAD */
if ((*on_exit) && is_class_ignored(clss)) {
tmn_suspend_enable();
continue;
}
Method *finalize = class_lookup_method_recursive(clss,
VM_Global_State::loader_env->FinalizeName_String,
VM_Global_State::loader_env->VoidVoidDescriptor_String);
assert(finalize);
TRACE2("finalize", "finalize object " << handle->object->vt()->clss);
vm_execute_java_method_array( (jmethodID) finalize, 0, args);
tmn_suspend_enable();
#ifndef NDEBUG
if (exn_raised()) {
tmn_suspend_disable();
assert(handle->object->vt()->clss);
INFO2("finalize", "Uncaught exception "
<< exn_get_name()
<< " while running a finalize of the object"
<< object->vt()->clss << ".");
tmn_suspend_enable();
}
#endif
exn_clear();
}
return i;
} //Objects_To_Finalize::do_finalization
void References_To_Enqueue::enqueue_references()
{
TRACE2("ref", "enqueue_references() started");
jvalue args[1], r;
tmn_suspend_disable();
ObjectHandle handle = oh_allocate_local_handle();
tmn_suspend_enable();
args[0].l = (jobject) handle;
// saves curent thread exception risen before enqueuing references
// if any and clears it to allow java to work
jthrowable cte = exn_get();
exn_clear();
while(true) {
tmn_suspend_disable();
ManagedObject* object = remove_object();
handle->object = object;
tmn_suspend_enable();
if (object == NULL) {
// restores curent thread exception risen before if any
if (NULL != cte) {
exn_raise_object(cte);
}
return;
}
tmn_suspend_disable();
assert(handle->object->vt()->clss);
Class *clss = handle->object->vt()->clss;
TRACE2("ref","Enqueueing reference " << (handle->object));
Method *enqueue = class_lookup_method_recursive(clss,
VM_Global_State::loader_env->EnqueueName_String,
VM_Global_State::loader_env->VoidBooleanDescriptor_String);
assert(enqueue);
vm_execute_java_method_array( (jmethodID) enqueue, &r, args);
tmn_suspend_enable();
#ifndef NDEBUG
if (exn_raised()) {
tmn_suspend_disable();
assert(object->vt()->clss);
INFO2("ref", "Uncaught exception "
<< exn_get_name()
<< " while running a enqueue method of the object"
<< object->vt()->clss << ".");
tmn_suspend_enable();
}
#endif
exn_clear();
}
// restores curent thread exception risen before if any
if (NULL != cte) {
exn_raise_object(cte);
}
TRACE2("ref", "enqueue_references() completed");
} //Objects_To_Finalize::notify_reference_queues
static Objects_To_Finalize objects_to_finalize;
void vm_finalize_object(Managed_Object_Handle p_obj)
{
objects_to_finalize.add_object((ManagedObject *)p_obj);
if(get_native_finalizer_thread_flag()){
int finalizable_obj_num = objects_to_finalize.getLength();
sched_heavy_finalizer(finalizable_obj_num);
}
} //vm_finalize_object
void vm_run_pending_finalizers()
{
NativeObjectHandles nhs;
assert(hythread_is_suspend_enabled());
/* BEGIN: modified for NATIVE FINALIZER THREAD */
if(get_native_finalizer_thread_flag()) {
activate_finalizer_threads(FALSE);
}
else {
objects_to_finalize.run_finalizers();
}
/* END: modified for NATIVE FINALIZER THREAD */
} //vm_run_pending_finalizers
int vm_do_finalization(int quantity)
{
assert(hythread_is_suspend_enabled());
return objects_to_finalize.do_finalization(quantity);
} //vm_do_finalization
bool is_it_finalize_thread() {
return ((p_TLS_vmthread->finalize_thread_flags & FINALIZER_THREAD) != 0);
}
void vm_enumerate_objects_to_be_finalized()
{
TRACE2("enumeration", "enumeration objects to be finalized");
INFO2("stats.finalize", "Enumerating finalize queue");
objects_to_finalize.enumerate_for_gc();
} //vm_enumerate_objects_to_be_finalized
int vm_get_finalizable_objects_quantity()
{
return objects_to_finalize.getLength();
}
/* returns true if finalization system is turned on, and false otherwise */
bool vm_finalization_is_enabled()
{
return VM_Global_State::loader_env->java_lang_FinalizerThread_Class != NULL;
}
void vm_obtain_finalizer_fields() {
objects_to_finalize.obtain_fields();
}
// -- Code to deal with Reference Queues that need to be notified.
static References_To_Enqueue references_to_enqueue;
void vm_enumerate_references_to_enqueue()
{
TRACE2("enumeration", "enumeration pending references to be enqueued");
INFO2("stats.finalize", "Enumerating reference queue");
references_to_enqueue.enumerate_for_gc();
} //vm_enumerate_references_to_enqueue
void vm_enqueue_reference(Managed_Object_Handle obj)
{
TRACE2("ref", obj << " is being added to enqueue list");
references_to_enqueue.add_object((ManagedObject *)obj);
} // vm_enqueue_reference
void vm_activate_ref_enqueue_thread()
{
if(get_native_ref_enqueue_thread_flag())
activate_ref_enqueue_thread(FALSE);
}
void vm_enqueue_references()
{
/* BEGIN: modified for NATIVE REFERENCE ENQUEUE THREAD */
if(get_native_ref_enqueue_thread_flag())
activate_ref_enqueue_thread(FALSE);
else
references_to_enqueue.enqueue_references();
/* END: modified for NATIVE REFERENCE ENQUEUE THREAD */
} //vm_enqueue_references
int vm_get_references_quantity()
{
return references_to_enqueue.getLength();
}
/* added for NATIVE REFERENCE ENQUEUE THREAD */
void vm_ref_enqueue_func(void)
{
references_to_enqueue.enqueue_references();
}