blob: 67cae9341157d27b3444dc19105316410e4f5976 [file] [log] [blame]
/*
* Copyright (c) 2009 Tomasz Grabiec
*
* This file is released under the GPL version 2 with the following
* clarification and special exception:
*
* Linking this library statically or dynamically with other modules is
* making a combined work based on this library. Thus, the terms and
* conditions of the GNU General Public License cover the whole
* combination.
*
* As a special exception, the copyright holders of this library give you
* permission to link this library with independent modules to produce an
* executable, regardless of the license terms of these independent
* modules, and to copy and distribute the resulting executable under terms
* of your choice, provided that you also meet, for each linked independent
* module, the terms and conditions of the license of that module. An
* independent module is a module which is not derived from or based on
* this library. If you modify this library, you may extend this exception
* to your version of the library, but you are not obligated to do so. If
* you do not wish to do so, delete this exception statement from your
* version.
*
* Please refer to the file LICENSE for details.
*/
#include "arch/memory.h"
#include "vm/call.h"
#include "vm/class.h"
#include "vm/die.h"
#include "vm/errors.h"
#include "vm/gc.h"
#include "vm/object.h"
#include "vm/preload.h"
#include "vm/reference.h"
#include "vm/signal.h"
#include "vm/stdlib.h"
#include "vm/thread.h"
#include "jit/exception.h"
#include <pthread.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
pthread_key_t current_exec_env_key;
__thread struct vm_exec_env *current_exec_env;
static struct vm_object *main_thread_group;
/* This mutex protects global operations on thread structures. */
pthread_mutex_t threads_mutex = PTHREAD_MUTEX_INITIALIZER;
/* Condition variable used for waiting on any thread's death. */
static pthread_cond_t thread_terminate_cond = PTHREAD_COND_INITIALIZER;
static unsigned int nr_threads;
static int nr_non_daemons;
struct list_head thread_list;
static bool thread_count_locked;
static pthread_cond_t thread_count_lock_cond = PTHREAD_COND_INITIALIZER;
static struct vm_thread *vm_thread_alloc(void)
{
struct vm_thread *thread = malloc(sizeof(struct vm_thread));
if (!thread)
return NULL;
if (pthread_mutex_init(&thread->mutex, NULL))
goto error_mutex;
atomic_set(&thread->state, VM_THREAD_STATE_NEW);
thread->vmthread = NULL;
thread->posix_id = -1;
thread->interrupted = false;
thread->waiting_mon = NULL;
thread->thread_state = VM_THREAD_STATE_CONSISTENT;
pthread_cond_init(&thread->park_cond, NULL);
pthread_mutex_init(&thread->park_mutex, NULL);
thread->unpark_called = false;
INIT_LIST_HEAD(&thread->list_node);
return thread;
error_mutex:
free(thread);
return NULL;
}
static void vm_thread_free(struct vm_thread *thread)
{
pthread_mutex_destroy(&thread->mutex);
pthread_mutex_destroy(&thread->park_mutex);
pthread_cond_destroy(&thread->park_cond);
free(thread);
}
/*
* Called when java.lang.VMThread is being collected.
*/
void vm_thread_collect_vmthread(struct vm_object *object)
{
struct vm_thread *thread = vm_thread_from_vmthread(object);
if (!thread)
return;
assert(thread->ee == NULL);
vm_thread_free(thread);
}
/**
* Returns instance of java.lang.Thread associated with given thread.
*/
struct vm_object *vm_thread_get_java_thread(struct vm_thread *thread)
{
struct vm_object *result;
pthread_mutex_lock(&thread->mutex);
result = field_get_object(thread->vmthread,
vm_java_lang_VMThread_thread);
pthread_mutex_unlock(&thread->mutex);
return result;
}
static bool vm_thread_is_daemon(struct vm_thread *thread)
{
struct vm_object *jthread;
jthread = vm_thread_get_java_thread(thread);
return field_get_int(jthread, vm_java_lang_Thread_daemon) != 0;
}
/* Must hold threads_mutex */
unsigned int vm_nr_threads(void)
{
return nr_threads;
}
void vm_thread_set_state(struct vm_thread *thread, enum vm_thread_state state)
{
atomic_set(&thread->state, state);
}
enum vm_thread_state vm_thread_get_state(struct vm_thread *thread)
{
return atomic_read(&thread->state);
}
static void vm_thread_attach_thread(struct vm_thread *thread)
{
list_add(&thread->list_node, &thread_list);
nr_threads++;
}
/* The caller must hold threads_mutex */
static void vm_thread_detach_thread(struct vm_thread *thread)
{
vm_thread_set_state(thread, VM_THREAD_STATE_TERMINATED);
list_del(&thread->list_node);
if (!vm_thread_is_daemon(thread))
nr_non_daemons--;
nr_threads--;
pthread_cond_broadcast(&thread_terminate_cond);
}
int init_threading(void)
{
INIT_LIST_HEAD(&thread_list);
nr_non_daemons = 0;
nr_threads = 0;
main_thread_group = vm_object_alloc(vm_java_lang_ThreadGroup);
if (!main_thread_group)
return -ENOMEM;
vm_call_method(vm_java_lang_ThreadGroup_init, main_thread_group);
if (exception_occurred())
return -1;
/*
* Initialize the main thread
*/
struct vm_object *thread = vm_object_alloc(vm_java_lang_Thread);
if (!thread)
return -ENOMEM;
struct vm_object *thread_name = vm_object_alloc_string_from_c("main");
if (!thread_name)
return -ENOMEM;
struct vm_object *vmthread = vm_object_alloc(vm_java_lang_VMThread);
if (!vmthread)
return -ENOMEM;
struct vm_thread *main_thread = vm_thread_alloc();
if (!main_thread)
return -ENOMEM;
atomic_set(&main_thread->state, VM_THREAD_STATE_RUNNABLE);
main_thread->vmthread = vmthread;
main_thread->posix_id = pthread_self();
main_thread->ee = vm_get_exec_env();
vm_get_exec_env()->thread = main_thread;
vm_call_method_object(vm_java_lang_Thread_init, thread,
vmthread, thread_name,
5 /* priority */,
0 /* daemon */);
if (exception_occurred())
return -1;
field_set_object(vmthread, vm_java_lang_VMThread_thread, thread);
field_set_object(vmthread, vm_java_lang_VMThread_vmdata,
(struct vm_object *) main_thread);
/* we must manually attach the main thread to the main ThreadGroup */
vm_call_method(vm_java_lang_ThreadGroup_addThread, main_thread_group, thread);
if (exception_occurred())
return -1;
field_set_object(thread, vm_java_lang_Thread_group, main_thread_group);
vm_thread_attach_thread(main_thread);
/* We must manually add main thread to InheritableThreadLocal. */
/* It must be called after all threading structures are set. */
vm_call_method(vm_java_lang_InheritableThreadLocal_newChildThread, thread);
if (exception_occurred())
return -1;
return 0;
}
static struct vm_exec_env *alloc_exec_env(void)
{
struct vm_exec_env *ee;
ee = vm_alloc(sizeof(struct vm_exec_env));
if (!ee)
return NULL;
ee->thread = NULL;
ee->exception = NULL;
ee->trace_classloader_level = 0;
INIT_LIST_HEAD(&ee->free_monitor_recs);
ee->in_safepoint = false;
ee->trace_buffer = NULL;
return ee;
}
static void free_exec_env(struct vm_exec_env *env)
{
struct vm_monitor_record *this, *next;
struct list_head *list = &env->free_monitor_recs;
list_for_each_entry_safe(this, next, list, ee_free_list_node) {
vm_monitor_record_free(this);
}
vm_free(env);
}
void init_exec_env(void)
{
if (pthread_key_create(&current_exec_env_key, NULL) != 0)
error("pthread_key_create");
struct vm_exec_env *vm_exec_env = alloc_exec_env();
if (!vm_exec_env)
error("out of memory");
pthread_setspecific(current_exec_env_key, vm_exec_env);
}
/**
* This is the entry point for all java threads.
*/
static void *vm_thread_entry(void *arg)
{
struct vm_exec_env *ee = arg;
struct vm_thread *thread = ee->thread;
pthread_setspecific(current_exec_env_key, ee);
setup_signal_handlers();
thread_init_exceptions();
/* XXX: Prevent collection of associated VMThread until
* this method returns. */
struct vm_reference *vmthread_ref
= vm_reference_alloc_strong(thread->vmthread);
if (!vmthread_ref)
return throw_oom_error();
vm_call_method(vm_java_lang_VMThread_run, thread->vmthread);
if (exception_occurred())
vm_print_exception(exception_occurred());
pthread_mutex_lock(&threads_mutex);
while (thread_count_locked)
pthread_cond_wait(&thread_count_lock_cond, &threads_mutex);
vm_thread_detach_thread(vm_thread_self());
pthread_mutex_unlock(&threads_mutex);
thread->ee = NULL;
vm_reference_free(vmthread_ref);
free_exec_env(ee);
return NULL;
}
/**
* Creates new native thread representing a java thread.
*/
int vm_thread_start(struct vm_object *vmthread)
{
/* Force object finalizer execution for vmthread */
if (gc_register_finalizer(vmthread, vm_object_finalizer)) {
throw_internal_error();
return -1;
}
struct vm_thread *thread = vm_thread_alloc();
if (!thread) {
throw_oom_error();
return -1;
}
struct vm_exec_env *ee = alloc_exec_env();
if (!ee) {
throw_oom_error();
goto out_free_thread;
}
pthread_attr_t attr;
if (pthread_attr_init(&attr)) {
throw_internal_error();
goto out_free_thread;
}
if (pthread_attr_setdetachstate(&attr, true)) {
throw_internal_error();
goto out_free_thread;
}
/* XXX: no need to lock because @thread is not yet visible to
* other threads. */
thread->vmthread = vmthread;
atomic_set(&thread->state, VM_THREAD_STATE_RUNNABLE);
field_set_object(vmthread, vm_java_lang_VMThread_vmdata,
(struct vm_object *) thread);
if (!vm_thread_is_daemon(thread)) {
pthread_mutex_lock(&threads_mutex);
nr_non_daemons++;
pthread_mutex_unlock(&threads_mutex);
}
pthread_mutex_lock(&threads_mutex);
while (thread_count_locked)
pthread_cond_wait(&thread_count_lock_cond, &threads_mutex);
vm_thread_attach_thread(thread);
thread->ee = ee;
thread->ee->thread = thread;
if (pthread_create(&thread->posix_id, &attr, &vm_thread_entry, thread->ee)) {
vm_thread_detach_thread(thread);
thread->ee = NULL;
free_exec_env(ee);
pthread_mutex_unlock(&threads_mutex);
signal_new_exception(vm_java_lang_Error, "Unable to create native thread");
return -1;
}
pthread_mutex_unlock(&threads_mutex);
pthread_attr_destroy(&attr);
return 0;
out_free_thread:
vm_thread_free(thread);
return -1;
}
void vm_thread_wait_for_non_daemons(void)
{
pthread_mutex_lock(&threads_mutex);
while (nr_non_daemons)
pthread_cond_wait(&thread_terminate_cond, &threads_mutex);
pthread_mutex_unlock(&threads_mutex);
}
char *vm_thread_get_name(struct vm_thread *thread)
{
struct vm_object *jthread;
struct vm_object *name;
jthread = vm_thread_get_java_thread(thread);
if (!jthread)
return NULL;
name = field_get_object(jthread, vm_java_lang_Thread_name);
if (!name)
return NULL;
return vm_string_to_cstr(name);
}
bool vm_thread_is_interrupted(struct vm_thread *thread)
{
bool status;
pthread_mutex_lock(&thread->mutex);
status = thread->interrupted;
pthread_mutex_unlock(&thread->mutex);
return status;
}
bool vm_thread_interrupted(struct vm_thread *thread)
{
bool status;
pthread_mutex_lock(&thread->mutex);
status = thread->interrupted;
thread->interrupted = false;
pthread_mutex_unlock(&thread->mutex);
return status;
}
void vm_thread_interrupt(struct vm_thread *thread)
{
struct vm_object *obj;
int err;
pthread_mutex_lock(&thread->mutex);
thread->interrupted = true;
obj = thread->waiting_mon;
pthread_mutex_unlock(&thread->mutex);
if (!obj)
return;
err = vm_object_lock(obj);
if (err)
return;
vm_object_notify_all(obj);
vm_object_unlock(obj);
}
void vm_lock_thread_count(void)
{
pthread_mutex_lock(&threads_mutex);
thread_count_locked = true;
pthread_mutex_unlock(&threads_mutex);
}
void vm_unlock_thread_count(void)
{
pthread_mutex_lock(&threads_mutex);
thread_count_locked = false;
pthread_cond_broadcast(&thread_count_lock_cond);
pthread_mutex_unlock(&threads_mutex);
}
void vm_thread_yield(void)
{
sched_yield();
}
struct vm_thread *vm_thread_from_vmthread(struct vm_object *vmthread)
{
return (struct vm_thread *)field_get_object(vmthread, vm_java_lang_VMThread_vmdata);
}
struct vm_thread *vm_thread_from_java_thread(struct vm_object *jthread)
{
struct vm_object *vmthread;
vmthread = field_get_object(jthread, vm_java_lang_Thread_vmThread);
return vm_thread_from_vmthread(vmthread);
}