* Assertion support.
* Linenumber table code rewritten.
* Exception table code rewritten.
+ * Replaced --with-classpath-includedir with --with-jni_h and
+ --with-jni_md_h to better support OpenJDK/IcedTea build.s
New in release 0.98 (June 6, 2007)
log_println("Java - header-generator started");
}
- /* initialize the utf8 hashtable stuff: lock, often used utf8 strings
- (must be done _after_ threads_preinit) */
-
- if (!utf8_init())
- vm_abort("utf8_init failed\n");
+ utf8_init();
/* initialize the classcache hashtable stuff: lock, hashtable
(must be done _after_ threads_preinit) */
/* src/cacaoh/dummy.c - dummy functions for cacaoh
- Copyright (C) 2007 R. Grafl, A. Krall, C. Kruegel,
- C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring,
- E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich,
- J. Wenninger, Institut f. Computersprachen - TU Wien
+ Copyright (C) 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
This file is part of CACAO.
#include "config.h"
#include <assert.h>
+#include <errno.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include "vm/vm.h"
#include "vmcore/class.h"
-#include "vmcore/method.h"
-#include "vmcore/utf8.h"
#include "vmcore/classcache.h"
#include "vmcore/loader.h"
+#include "vmcore/method.h"
+#include "vmcore/utf8.h"
+#include "vmcore/system.h"
/* global variables ***********************************************************/
{
va_list ap;
- /* print the log message */
-
va_start(ap, text);
vfprintf(stderr, text, ap);
va_end(ap);
- /* now abort the VM */
+ system_abort();
+}
- abort();
+void vm_abort_errno(const char *text, ...)
+{
+ va_list ap;
+
+ va_start(ap, text);
+ vm_abort_errnum(errno, text, ap);
+ va_end(ap);
+}
+
+void vm_abort_errnum(int errnum, const char *text, ...)
+{
+ va_list ap;
+
+ log_start();
+
+ va_start(ap, text);
+ log_vprint(text, ap);
+ va_end(ap);
+
+ log_print(": %s", system_strerror(errnum));
+ log_finish();
+
+ system_abort();
}
java_handle_t *vm_call_method(methodinfo *m, java_handle_t *o, ...)
jint _Jv_JNI_DestroyJavaVM(JavaVM *vm)
{
- int32_t status;
+ int status;
TRACEJNICALLS(("_Jv_JNI_DestroyJavaVM(vm=%p)", vm));
+ if (vm_created == false)
+ return JNI_ERR;
+
status = vm_destroy(vm);
return status;
jint _Jv_JNI_AttachCurrentThread(JavaVM *vm, void **p_env, void *thr_args)
{
- STATISTICS(jniinvokation());
+ int result;
- return jni_attach_current_thread(p_env, thr_args, false);
+ TRACEJNICALLS(("_Jv_JNI_AttachCurrentThread(vm=%p, p_env=%p, thr_args=%p)", vm, p_env, thr_args));
+
+ if (vm_created == false)
+ return JNI_ERR;
+
+ result = jni_attach_current_thread(p_env, thr_args, false);
+
+ return result;
}
{
TRACEJNICALLS(("_Jv_JNI_GetEnv(vm=%p, env=%p, %d=version)", vm, env, version));
+ if (vm_created == false) {
+ *env = NULL;
+ return JNI_EDETACHED;
+ }
+
#if defined(ENABLE_THREADS)
if (threads_get_current_threadobject() == NULL) {
*env = NULL;
jint _Jv_JNI_AttachCurrentThreadAsDaemon(JavaVM *vm, void **penv, void *args)
{
- STATISTICS(jniinvokation());
+ int result;
+
+ TRACEJNICALLS(("_Jv_JNI_AttachCurrentThreadAsDaemon(vm=%p, penv=%p, args=%p)", vm, penv, args));
- return jni_attach_current_thread(penv, args, true);
+ if (vm_created == false)
+ return JNI_ERR;
+
+ result = jni_attach_current_thread(penv, args, true);
+
+ return result;
}
{ "compareAndSwapInt", "(Ljava/lang/Object;JII)Z", (void *) (intptr_t) &Java_sun_misc_Unsafe_compareAndSwapInt },
{ "compareAndSwapLong", "(Ljava/lang/Object;JJJ)Z", (void *) (intptr_t) &Java_sun_misc_Unsafe_compareAndSwapLong },
{ "getObjectVolatile", "(Ljava/lang/Object;J)Ljava/lang/Object;", (void *) (intptr_t) &Java_sun_misc_Unsafe_getObjectVolatile },
+ { "putObjectVolatile", "(Ljava/lang/Object;JLjava/lang/Object;)V", (void *) (intptr_t) &Java_sun_misc_Unsafe_putObjectVolatile },
{ "getIntVolatile", "(Ljava/lang/Object;J)I", (void *) (intptr_t) &Java_sun_misc_Unsafe_getIntVolatile },
{ "getLongVolatile", "(Ljava/lang/Object;J)J", (void *) (intptr_t) &Java_sun_misc_Unsafe_getLongVolatile },
{ "unpark", "(Ljava/lang/Object;)V", (void *) (intptr_t) &Java_sun_misc_Unsafe_unpark },
}
+/*
+ * Class: sun/misc/Unsafe
+ * Method: putObjectVolatile
+ * Signature: (Ljava/lang/Object;JLjava/lang/Object;)V
+ */
+JNIEXPORT void JNICALL Java_sun_misc_Unsafe_putObjectVolatile(JNIEnv *env, sun_misc_Unsafe *this, java_lang_Object *o, int64_t offset, java_lang_Object *x)
+{
+ volatile void **p;
+
+ p = (volatile void **) (((uint8_t *) o) + offset);
+
+ *p = x;
+}
+
+
/*
* Class: sun/misc/Unsafe
* Method: getIntVolatile
## src/threads/native/Makefile.am
##
-## Copyright (C) 1996-2005, 2006, 2007 R. Grafl, A. Krall, C. Kruegel,
-## C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring,
-## E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich,
-## J. Wenninger, Institut f. Computersprachen - TU Wien
+## Copyright (C) 1996-2005, 2006, 2007, 2008
+## CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
##
## This file is part of CACAO.
##
libthreadsposix_la_SOURCES = \
lock.c \
lock.h \
+ threadlist-posix.c \
threads.c \
threads.h
## c-basic-offset: 4
## tab-width: 8
## compile-command: "automake --add-missing"
-## End:
+## End:
\ No newline at end of file
}
+/* TODO Move this function into threadlist.[ch]. */
+
static threadobject *threads_lookup_thread_id(int index)
{
threadobject *t;
- threads_list_lock();
+ threadlist_lock();
for (t = threadlist_first(); t != NULL; t = threadlist_next(t)) {
if (t->state == THREAD_STATE_NEW)
break;
}
- threads_list_unlock();
+ threadlist_unlock();
return t;
}
--- /dev/null
+/* src/threads/native/threadlist-posix.c - POSIX threadlist functions
+
+ Copyright (C) 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
+
+ This file is part of CACAO.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+*/
+
+
+#include "config.h"
+
+#include <pthread.h>
+
+#include "vm/vm.h"
+
+#include "vmcore/options.h"
+
+
+/* global variables ***********************************************************/
+
+/* global mutex for the thread list */
+static pthread_mutex_t threadlist_mutex;
+
+
+/* threadlist_impl_init ********************************************************
+
+ Do some early initialization of stuff required.
+
+*******************************************************************************/
+
+void threadlist_impl_init(void)
+{
+ int result;
+
+ TRACESUBSYSTEMINITIALIZATION("threadlist_impl_init");
+
+ /* Initialize the thread list mutex. */
+
+ result = pthread_mutex_init(&threadlist_mutex, NULL);
+
+ if (result != 0)
+ vm_abort_errnum(result, "threadlist_impl_init: pthread_mutex_init failed");
+}
+
+
+/* threadlist_lock *************************************************************
+
+ Enter the thread list mutex.
+
+ NOTE: We need this function as we can't use an internal lock for
+ the threads lists because the thread's lock is initialized in
+ threads_table_add (when we have the thread index), but we
+ already need the lock at the entry of the function.
+
+*******************************************************************************/
+
+void threadlist_lock(void)
+{
+ int result;
+
+ result = pthread_mutex_lock(&threadlist_mutex);
+
+ if (result != 0)
+ vm_abort_errnum(result, "threads_list_lock: pthread_mutex_lock failed");
+}
+
+
+/* threadlist_unlock *********************************************************
+
+ Leave the thread list mutex.
+
+*******************************************************************************/
+
+void threadlist_unlock(void)
+{
+ int result;
+
+ result = pthread_mutex_unlock(&threadlist_mutex);
+
+ if (result != 0)
+ vm_abort_errnum(result, "threadlist_unlock: pthread_mutex_unlock failed");
+}
+
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: c
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
/* GLOBAL VARIABLES */
/******************************************************************************/
-static methodinfo *method_thread_init;
-
/* the thread object of the current thread */
/* This is either a thread-local variable defined with __thread, or */
/* a thread-specific value stored with key threads_current_threadobject_key. */
pthread_key_t threads_current_threadobject_key;
#endif
-/* global mutex for the threads table */
-static pthread_mutex_t mutex_threads_list;
-
/* global mutex for stop-the-world */
static pthread_mutex_t stopworldlock;
/* lock the threads lists */
- threads_list_lock();
+ threadlist_lock();
#if defined(__DARWIN__)
/*threads_cast_darwinstop();*/
/* unlock the threads lists */
- threads_list_unlock();
+ threadlist_unlock();
unlock_stopworld();
}
vm_abort_errnum(result, "threads_impl_preinit: pthread_mutex_init failed");
#endif
- /* initialize the threads-list mutex */
-
- result = pthread_mutex_init(&mutex_threads_list, NULL);
- if (result != 0)
- vm_abort_errnum(result, "threads_impl_preinit: pthread_mutex_init failed");
-
#if !defined(HAVE___THREAD)
result = pthread_key_create(&threads_current_threadobject_key, NULL);
if (result != 0)
}
-/* threads_list_lock ***********************************************************
-
- Enter the threads table mutex.
-
- NOTE: We need this function as we can't use an internal lock for
- the threads lists because the thread's lock is initialized in
- threads_table_add (when we have the thread index), but we
- already need the lock at the entry of the function.
-
-*******************************************************************************/
-
-void threads_list_lock(void)
-{
- int result;
-
- result = pthread_mutex_lock(&mutex_threads_list);
-
- if (result != 0)
- vm_abort_errnum(result, "threads_list_lock: pthread_mutex_lock failed");
-}
-
-
-/* threads_list_unlock *********************************************************
-
- Leave the threads list mutex.
-
-*******************************************************************************/
-
-void threads_list_unlock(void)
-{
- int result;
-
- result = pthread_mutex_unlock(&mutex_threads_list);
-
- if (result != 0)
- vm_abort_errnum(result, "threads_list_unlock: pthread_mutex_unlock failed");
-}
-
-
/* threads_mutex_gc_lock *******************************************************
Enter the global GC mutex.
}
-/* threads_init ****************************************************************
+/* threads_impl_init ***********************************************************
- Initializes the threads required by the JVM: main, finalizer.
+ Initializes the implementation specific bits.
*******************************************************************************/
-bool threads_init(void)
+void threads_impl_init(void)
{
- threadobject *mainthread;
- java_handle_t *threadname;
- java_lang_Thread *t;
- java_handle_t *o;
-
-#if defined(ENABLE_JAVASE)
- java_lang_ThreadGroup *threadgroup;
- methodinfo *m;
-#endif
-
-#if defined(WITH_CLASSPATH_GNU)
- java_lang_VMThread *vmt;
-#endif
-
pthread_attr_t attr;
-
- TRACESUBSYSTEMINITIALIZATION("threads_init");
-
- /* get methods we need in this file */
-
-#if defined(WITH_CLASSPATH_GNU)
- method_thread_init =
- class_resolveclassmethod(class_java_lang_Thread,
- utf_init,
- utf_new_char("(Ljava/lang/VMThread;Ljava/lang/String;IZ)V"),
- class_java_lang_Thread,
- true);
-#elif defined(WITH_CLASSPATH_SUN)
- method_thread_init =
- class_resolveclassmethod(class_java_lang_Thread,
- utf_init,
- utf_new_char("(Ljava/lang/String;)V"),
- class_java_lang_Thread,
- true);
-#elif defined(WITH_CLASSPATH_CLDC1_1)
- method_thread_init =
- class_resolveclassmethod(class_java_lang_Thread,
- utf_init,
- utf_new_char("(Ljava/lang/String;)V"),
- class_java_lang_Thread,
- true);
-#else
-# error unknown classpath configuration
-#endif
-
- if (method_thread_init == NULL)
- return false;
-
- /* Get the main-thread (NOTE: The main threads is always the first
- thread in the list). */
-
- mainthread = threadlist_first();
-
- /* create a java.lang.Thread for the main thread */
-
- t = (java_lang_Thread *) builtin_new(class_java_lang_Thread);
-
- if (t == NULL)
- return false;
-
- /* set the object in the internal data structure */
-
- threads_thread_set_object(mainthread, (java_handle_t *) t);
-
-#if defined(ENABLE_INTRP)
- /* create interpreter stack */
-
- if (opt_intrp) {
- MSET(intrp_main_stack, 0, u1, opt_stacksize);
- mainthread->_global_sp = (Cell*) (intrp_main_stack + opt_stacksize);
- }
-#endif
-
- threadname = javastring_new(utf_new_char("main"));
-
-#if defined(ENABLE_JAVASE)
- /* allocate and init ThreadGroup */
-
- threadgroup = (java_lang_ThreadGroup *)
- native_new_and_init(class_java_lang_ThreadGroup);
-
- if (threadgroup == NULL)
- return false;
-#endif
-
-#if defined(WITH_CLASSPATH_GNU)
- /* create a java.lang.VMThread for the main thread */
-
- vmt = (java_lang_VMThread *) builtin_new(class_java_lang_VMThread);
-
- if (vmt == NULL)
- return false;
-
- /* set the thread */
-
- LLNI_field_set_ref(vmt, thread, t);
- LLNI_field_set_val(vmt, vmdata, (java_lang_Object *) mainthread);
-
- /* call java.lang.Thread.<init>(Ljava/lang/VMThread;Ljava/lang/String;IZ)V */
- o = (java_handle_t *) t;
-
- (void) vm_call_method(method_thread_init, o, vmt, threadname, NORM_PRIORITY,
- false);
-
-#elif defined(WITH_CLASSPATH_SUN)
-
- /* We trick java.lang.Thread.<init>, which sets the priority of
- the current thread to the parent's one. */
-
- t->priority = NORM_PRIORITY;
-
- /* Call java.lang.Thread.<init>(Ljava/lang/String;)V */
-
- o = (java_object_t *) t;
-
- (void) vm_call_method(method_thread_init, o, threadname);
-
-#elif defined(WITH_CLASSPATH_CLDC1_1)
-
- /* set the thread */
-
- t->vm_thread = (java_lang_Object *) mainthread;
-
- /* call public Thread(String name) */
-
- o = (java_handle_t *) t;
-
- (void) vm_call_method(method_thread_init, o, threadname);
-#else
-# error unknown classpath configuration
-#endif
-
- if (exceptions_get_exception())
- return false;
-
-#if defined(ENABLE_JAVASE)
- LLNI_field_set_ref(t, group, threadgroup);
-
-# if defined(WITH_CLASSPATH_GNU)
- /* add main thread to java.lang.ThreadGroup */
-
- m = class_resolveclassmethod(class_java_lang_ThreadGroup,
- utf_addThread,
- utf_java_lang_Thread__V,
- class_java_lang_ThreadGroup,
- true);
-
- o = (java_handle_t *) threadgroup;
-
- (void) vm_call_method(m, o, t);
-
- if (exceptions_get_exception())
- return false;
-# else
-# warning Do not know what to do here
-# endif
-#endif
+ int result;
threads_set_thread_priority(pthread_self(), NORM_PRIORITY);
- /* initialize the thread attribute object */
+ /* Initialize the thread attribute object. */
- if (pthread_attr_init(&attr) != 0)
- vm_abort("threads_init: pthread_attr_init failed: %s", strerror(errno));
-
- if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0)
- vm_abort("threads_init: pthread_attr_setdetachstate failed: %s",
- strerror(errno));
+ result = pthread_attr_init(&attr);
- DEBUGTHREADS("starting (main)", mainthread);
+ if (result != 0)
+ vm_abort_errnum(result, "threads_impl_init: pthread_attr_init failed");
- /* everything's ok */
+ result = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
- return true;
+ if (result != 0)
+ vm_abort_errnum(result, "threads_impl_init: pthread_attr_setdetachstate failed");
}
o = (java_handle_t *) t;
#if defined(WITH_CLASSPATH_GNU)
- (void) vm_call_method(method_thread_init, o, vmt, s, NORM_PRIORITY,
+ (void) vm_call_method(thread_method_init, o, vmt, s, NORM_PRIORITY,
isdaemon);
#elif defined(WITH_CLASSPATH_CLDC1_1)
- (void) vm_call_method(method_thread_init, o, s);
+ (void) vm_call_method(thread_method_init, o, s);
#endif
if (exceptions_get_exception())
threadobject *threads_get_current_threadobject(void);
-bool threads_init(void);
-
void threads_start_thread(threadobject *thread, functionptr function);
void threads_set_thread_priority(pthread_t tid, int priority);
list_thread = list_create(OFFSET(threadobject, linkage));
list_thread_free = list_create(OFFSET(threadobject, linkage_free));
list_thread_index_free = list_create(OFFSET(thread_index_t, linkage));
+
+ /* Initialize the implementation specific parts. */
+
+ threadlist_impl_init();
}
}
-/* threads_list_next ***********************************************************
+/* threadlist_next *************************************************************
Return the next entry in the thread list.
threadobject *t;
int nondaemons;
- /* Lock the threads lists. */
+ /* Lock the thread lists. */
- threads_list_lock();
+ threadlist_lock();
nondaemons = 0;
nondaemons++;
}
- /* Unlock the threads lists. */
+ /* Unlock the thread lists. */
- threads_list_unlock();
+ threadlist_unlock();
return nondaemons;
}
void threadlist_index_add(int index);
int threadlist_get_free_index(void);
+/* implementation specific functions */
+
+void threadlist_impl_init(void);
+
+void threadlist_lock(void);
+void threadlist_unlock(void);
+
#endif /* _THREADLIST_H */
#include "native/jni.h"
#include "native/llni.h"
+#include "native/native.h"
#include "native/include/java_lang_Object.h"
#include "native/include/java_lang_String.h"
#include "native/include/java_lang_Thread.h"
+#if defined(ENABLE_JAVASE)
+# include "native/include/java_lang_ThreadGroup.h"
+#endif
+
#if defined(WITH_CLASSPATH_GNU)
-# include "native/include/java_lang_Throwable.h"
# include "native/include/java_lang_VMThread.h"
#endif
#include "threads/threadlist.h"
#include "threads/threads-common.h"
-#include "toolbox/list.h"
-
#include "vm/builtin.h"
+#include "vm/exceptions.h"
#include "vm/stringlocal.h"
#include "vm/vm.h"
#include "vm/jit/stacktrace.h"
#include "vmcore/class.h"
+#include "vmcore/method.h"
#include "vmcore/options.h"
#if defined(ENABLE_STATISTICS)
/* global variables ***********************************************************/
+methodinfo *thread_method_init;
+
+#if defined(ENABLE_JAVASE)
+static java_lang_ThreadGroup *threadgroup_system;
+static java_lang_ThreadGroup *threadgroup_main;
+#endif
+
#if defined(__LINUX__)
/* XXX Remove for exact-GC. */
bool threads_pthreads_implementation_nptl;
#endif
+/* static functions ***********************************************************/
+
+static void thread_create_initial_threadgroups(void);
+static void thread_create_initial_thread(void);
+
+
/* threads_preinit *************************************************************
Do some early initialization of stuff required.
}
+/* threads_init ****************************************************************
+
+ Initialize the main thread.
+
+*******************************************************************************/
+
+void threads_init(void)
+{
+ TRACESUBSYSTEMINITIALIZATION("threads_init");
+
+ /* Create the system and main thread groups. */
+
+ thread_create_initial_threadgroups();
+
+ /* Cache the java.lang.Thread initialization method. */
+
+#if defined(WITH_CLASSPATH_GNU)
+ thread_method_init =
+ class_resolveclassmethod(class_java_lang_Thread,
+ utf_init,
+ utf_new_char("(Ljava/lang/VMThread;Ljava/lang/String;IZ)V"),
+ class_java_lang_Thread,
+ true);
+#elif defined(WITH_CLASSPATH_SUN)
+ thread_method_init =
+ class_resolveclassmethod(class_java_lang_Thread,
+ utf_init,
+ utf_java_lang_String__void,
+ class_java_lang_Thread,
+ true);
+#elif defined(WITH_CLASSPATH_CLDC1_1)
+ thread_method_init =
+ class_resolveclassmethod(class_java_lang_Thread,
+ utf_init,
+ utf_java_lang_String__void,
+ class_java_lang_Thread,
+ true);
+#else
+# error unknown classpath configuration
+#endif
+
+ if (thread_method_init == NULL)
+ vm_abort("threads_init: failed to resolve thread init method");
+
+ thread_create_initial_thread();
+}
+
+
+/* thread_create_initial_threadgroups ******************************************
+
+ Create the initial threadgroups.
+
+ GNU Classpath:
+ Create the main threadgroup only and set the system
+ threadgroup to the main threadgroup.
+
+ SUN:
+ Create the system and main threadgroup.
+
+ CLDC:
+ This function is a no-op.
+
+*******************************************************************************/
+
+static void thread_create_initial_threadgroups(void)
+{
+#if defined(ENABLE_JAVASE)
+# if defined(WITH_CLASSPATH_GNU)
+
+ /* Allocate and initialize the main thread group. */
+
+ threadgroup_main = (java_lang_ThreadGroup *)
+ native_new_and_init(class_java_lang_ThreadGroup);
+
+ if (threadgroup_main == NULL)
+ vm_abort("thread_create_initial_threadgroups: failed to allocate main threadgroup");
+
+ /* Use the same threadgroup for system as for main. */
+
+ threadgroup_system = threadgroup_main;
+
+# elif defined(WITH_CLASSPATH_SUN)
+
+ java_handle_t *name;
+ methodinfo *m;
+ java_handle_t *o;
+
+ /* Allocate and initialize the system thread group. */
+
+ threadgroup_system = (java_lang_ThreadGroup *)
+ native_new_and_init(class_java_lang_ThreadGroup);
+
+ if (threadgroup_system == NULL)
+ vm_abort("thread_create_initial_threadgroups: failed to allocate system threadgroup");
+
+ /* Allocate and initialize the main thread group. */
+
+ threadgroup_main =
+ (java_lang_ThreadGroup *) builtin_new(class_java_lang_ThreadGroup);
+
+ if (threadgroup_main == NULL)
+ vm_abort("thread_create_initial_threadgroups: failed to allocate main threadgroup");
+
+ name = javastring_new(utf_main);
+
+ m = class_resolveclassmethod(class_java_lang_ThreadGroup,
+ utf_init,
+ utf_Ljava_lang_ThreadGroup_Ljava_lang_String__V,
+ class_java_lang_ThreadGroup,
+ true);
+
+ if (m == NULL)
+ vm_abort("thread_create_initial_threadgroups: failed to resolve threadgroup init method");
+
+ o = (java_handle_t *) threadgroup_main;
+
+ (void) vm_call_method(m, o, threadgroup_system, name);
+
+ if (exceptions_get_exception())
+ vm_abort("thread_create_initial_threadgroups: exception while initializing main threadgroup");
+
+# else
+# error unknown classpath configuration
+# endif
+#endif
+}
+
+
+/* thread_create_initial_thread ***********************************************
+
+ Create the initial thread: main
+
+*******************************************************************************/
+
+static void thread_create_initial_thread(void)
+{
+ threadobject *mainthread;
+ java_handle_t *name;
+ java_lang_Thread *to; /* java.lang.Thread object */
+ java_handle_t *o;
+
+#if defined(WITH_CLASSPATH_GNU)
+ java_lang_VMThread *vmto; /* java.lang.VMThread object */
+ methodinfo *m;
+#endif
+
+ /* Get the main-thread (NOTE: The main thread is always the first
+ thread in the list). */
+
+ mainthread = threadlist_first();
+
+ /* Create a java.lang.Thread object for the main thread. */
+
+ to = (java_lang_Thread *) builtin_new(class_java_lang_Thread);
+
+ if (to == NULL)
+ vm_abort("threads_init: failed to allocate java.lang.Thread object");
+
+ /* set the object in the internal data structure */
+
+ threads_thread_set_object(mainthread, (java_handle_t *) to);
+
+#if defined(ENABLE_INTRP)
+ /* create interpreter stack */
+
+ if (opt_intrp) {
+ MSET(intrp_main_stack, 0, u1, opt_stacksize);
+ mainthread->_global_sp = (Cell*) (intrp_main_stack + opt_stacksize);
+ }
+#endif
+
+ name = javastring_new(utf_main);
+
+#if defined(WITH_CLASSPATH_GNU)
+ /* Create a java.lang.VMThread for the main thread. */
+
+ vmto = (java_lang_VMThread *) builtin_new(class_java_lang_VMThread);
+
+ if (vmto == NULL)
+ vm_abort("threads_init: failed to allocate java.lang.VMThread object");
+
+ /* Set the thread. */
+
+ LLNI_field_set_ref(vmto, thread, to);
+ LLNI_field_set_val(vmto, vmdata, (java_lang_Object *) mainthread);
+
+ /* Set the thread group. */
+
+ LLNI_field_set_ref(to, group, threadgroup_main);
+
+ /* Call:
+ java.lang.Thread.<init>(Ljava/lang/VMThread;Ljava/lang/String;IZ)V */
+
+ o = (java_handle_t *) to;
+
+ (void) vm_call_method(thread_method_init, o, vmto, name, NORM_PRIORITY,
+ false);
+
+ if (exceptions_get_exception())
+ vm_abort("threads_init: exception while initializing main thread");
+
+ /* Add main thread to main threadgroup. */
+
+ m = class_resolveclassmethod(class_java_lang_ThreadGroup,
+ utf_addThread,
+ utf_java_lang_Thread__V,
+ class_java_lang_ThreadGroup,
+ true);
+
+ o = (java_handle_t *) threadgroup_main;
+
+ (void) vm_call_method(m, o, to);
+
+ if (exceptions_get_exception())
+ vm_abort("threads_init: exception while adding main thread to main threadgroup");
+
+#elif defined(WITH_CLASSPATH_SUN)
+
+ /* Set the thread group and the priority. java.lang.Thread.<init>
+ requires it because it sets the priority of the current thread
+ to the parent's one (which is the current thread in this
+ case). */
+
+ LLNI_field_set_ref(to, group, threadgroup_main);
+ LLNI_field_set_val(to, priority, NORM_PRIORITY);
+
+ /* Call: java.lang.Thread.<init>(Ljava/lang/String;)V */
+
+ o = (java_object_t *) to;
+
+ (void) vm_call_method(thread_method_init, o, name);
+
+ if (exceptions_get_exception())
+ vm_abort("threads_init: exception while initializing main thread");
+
+#elif defined(WITH_CLASSPATH_CLDC1_1)
+
+ /* Set the thread. */
+
+ LLNI_field_set_val(to, vm_thread, (java_lang_Object *) mainthread);
+
+ /* Call public Thread(String name). */
+
+ o = (java_handle_t *) to;
+
+ (void) vm_call_method(thread_method_init, o, name);
+
+ if (exceptions_get_exception())
+ vm_abort("threads_init: exception while initializing main thread");
+
+#else
+# error unknown classpath configuration
+#endif
+
+ /* Initialize the implementation specific bits. */
+
+ threads_impl_init();
+
+ DEBUGTHREADS("starting (main)", mainthread);
+}
+
+
/* threads_thread_new **********************************************************
Allocates and initializes an internal thread data-structure and
int32_t index;
threadobject *t;
- /* lock the threads-lists */
+ /* Lock the thread lists */
- threads_list_lock();
+ threadlist_lock();
index = threadlist_get_free_index();
threadlist_add(t);
- /* Unlock the threads-lists. */
+ /* Unlock the thread lists. */
- threads_list_unlock();
+ threadlist_unlock();
return t;
}
void threads_thread_free(threadobject *t)
{
- /* Lock the threads lists. */
+ /* Lock the thread lists. */
- threads_list_lock();
+ threadlist_lock();
/* Remove the thread from the thread-list. */
threadlist_free_add(t);
- /* Unlock the threads lists. */
+ /* Unlock the thread lists. */
- threads_list_unlock();
+ threadlist_unlock();
}
bool threads_thread_start_internal(utf *name, functionptr f)
{
threadobject *t;
- java_lang_Thread *object;
+ java_lang_Thread *to; /* java.lang.Thread object */
#if defined(WITH_CLASSPATH_GNU)
- java_lang_VMThread *vmt;
+ java_lang_VMThread *vmto; /* java.lang.VMThread object */
#endif
/* Enter the join-mutex, so if the main-thread is currently
threads_mutex_join_unlock();
- /* create the java thread object */
+ /* Create the Java thread object. */
- object = (java_lang_Thread *) builtin_new(class_java_lang_Thread);
+ to = (java_lang_Thread *) builtin_new(class_java_lang_Thread);
/* XXX memory leak!!! */
- if (object == NULL)
+ if (to == NULL)
return false;
#if defined(WITH_CLASSPATH_GNU)
- vmt = (java_lang_VMThread *) builtin_new(class_java_lang_VMThread);
+
+ vmto = (java_lang_VMThread *) builtin_new(class_java_lang_VMThread);
/* XXX memory leak!!! */
- if (vmt == NULL)
+ if (vmto == NULL)
return false;
- LLNI_field_set_ref(vmt, thread, object);
- LLNI_field_set_val(vmt, vmdata, (java_lang_Object *) t);
+ LLNI_field_set_ref(vmto, thread, to);
+ LLNI_field_set_val(vmto, vmdata, (java_lang_Object *) t);
+
+ LLNI_field_set_ref(to, vmThread, vmto);
+
+#elif defined(WITH_CLASSPATH_SUN)
+
+ /* Nothing to do. */
- LLNI_field_set_ref(object, vmThread, vmt);
#elif defined(WITH_CLASSPATH_CLDC1_1)
- LLNI_field_set_val(object, vm_thread, (java_lang_Object *) t);
+
+ LLNI_field_set_val(to, vm_thread, (java_lang_Object *) t);
+
+#else
+# error unknown classpath configuration
#endif
- threads_thread_set_object(t, (java_handle_t *) object);
+ threads_thread_set_object(t, (java_handle_t *) to);
- /* set java.lang.Thread fields */
+ /* Set java.lang.Thread fields. */
#if defined(WITH_CLASSPATH_GNU)
- LLNI_field_set_ref(object, name , (java_lang_String *) javastring_new(name));
-#elif defined(WITH_CLASSPATH_CLDC1_1)
+
+ LLNI_field_set_ref(to, name, (java_lang_String *) javastring_new(name));
+
+#elif defined(WITH_CLASSPATH_SUN) || defined(WITH_CLASSPATH_CLDC1_1)
+
/* FIXME: In cldc the name is a char[] */
-/* LLNI_field_set_ref(object, name , (java_chararray *) javastring_new(name)); */
- LLNI_field_set_ref(object, name , NULL);
+/* LLNI_field_set_ref(to, name, (java_chararray *) name); */
+ LLNI_field_set_ref(to, name, NULL);
+
+#else
+# error unknow classpath configuration
#endif
+ LLNI_field_set_val(to, priority, NORM_PRIORITY);
+
#if defined(ENABLE_JAVASE)
- LLNI_field_set_val(object, daemon , true);
+ LLNI_field_set_val(to, daemon, true);
+ LLNI_field_set_ref(to, group, threadgroup_system);
#endif
- LLNI_field_set_val(object, priority, NORM_PRIORITY);
-
- /* start the thread */
+ /* Start the thread. */
threads_impl_thread_start(t, f);
void threads_thread_start(java_handle_t *object)
{
- java_lang_Thread *o;
- threadobject *thread;
+ java_lang_Thread *to;
+ threadobject *t;
#if defined(WITH_CLASSPATH_GNU)
- java_lang_VMThread *vmt;
+ java_lang_VMThread *vmto;
#endif
- o = (java_lang_Thread *) object;
+ to = (java_lang_Thread *) object;
/* Enter the join-mutex, so if the main-thread is currently
waiting to join all threads, the number of non-daemon threads
/* create internal thread data-structure */
- thread = threads_thread_new();
+ t = threads_thread_new();
/* this is a normal Java thread */
- thread->flags |= THREAD_FLAG_JAVA;
+ t->flags |= THREAD_FLAG_JAVA;
#if defined(ENABLE_JAVASE)
- /* is this a daemon thread? */
+ /* Is this a daemon thread? */
- if (LLNI_field_direct(o, daemon) == true)
- thread->flags |= THREAD_FLAG_DAEMON;
+ if (LLNI_field_direct(to, daemon) == true)
+ t->flags |= THREAD_FLAG_DAEMON;
#endif
/* The thread is flagged and (non-)daemon thread, we can leave the
threads_mutex_join_unlock();
- /* link the two objects together */
+ /* Link the two objects together. */
- threads_thread_set_object(thread, object);
+ threads_thread_set_object(t, object);
#if defined(WITH_CLASSPATH_GNU)
- LLNI_field_get_ref(o, vmThread, vmt);
- assert(vmt);
- assert(LLNI_field_direct(vmt, vmdata) == NULL);
+ /* Get the java.lang.VMThread object and do some sanity checks. */
+
+ LLNI_field_get_ref(to, vmThread, vmto);
+
+ assert(vmto);
+ assert(LLNI_field_direct(vmto, vmdata) == NULL);
+
+ LLNI_field_set_val(vmto, vmdata, (java_lang_Object *) t);
+
+#elif defined(WITH_CLASSPATH_SUN)
+
+ /* Nothing to do. */
- LLNI_field_set_val(vmt, vmdata, (java_lang_Object *) thread);
#elif defined(WITH_CLASSPATH_CLDC1_1)
- LLNI_field_set_val(o, vm_thread, (java_lang_Object *) thread);
+
+ LLNI_field_set_val(to, vm_thread, (java_lang_Object *) t);
+
+#else
+# error unknown classpath configuration
#endif
/* Start the thread. Don't pass a function pointer (NULL) since
we want Thread.run()V here. */
- threads_impl_thread_start(thread, NULL);
+ threads_impl_thread_start(t, NULL);
}
*******************************************************************************/
-#include "native/include/java_lang_ThreadGroup.h"
-
java_object_t *threads_get_current_object(void)
{
#if defined(ENABLE_THREADS)
threadobject *t;
-# if defined(ENABLE_JAVASE)
- java_lang_ThreadGroup *group;
-# endif
#endif
- java_lang_Thread *o;
+ java_handle_t *o;
#if defined(ENABLE_THREADS)
t = THREADOBJECT;
o = threads_thread_get_object(t);
-
-# if defined(ENABLE_JAVASE)
- /* TODO Do we really need this code? Or should we check, when we
- create the threads, that all of them have a group? */
- /* TWISTI No, we don't need this code! We need to allocate a
- ThreadGroup before we initialize the main thread. */
-
- LLNI_field_get_ref(o, group, group);
-
- if (group == NULL) {
- /* ThreadGroup of currentThread is not initialized */
-
- group = (java_lang_ThreadGroup *)
- native_new_and_init(class_java_lang_ThreadGroup);
-
- if (group == NULL)
- vm_abort("unable to create ThreadGroup");
-
- LLNI_field_set_ref(o, group, group);
- }
-# endif
#else
/* We just return a fake java.lang.Thread object, otherwise we get
NullPointerException's in GNU Classpath. */
{
/* Set the state inside a lock. */
- threads_list_lock();
+ threadlist_lock();
if (t->state != THREAD_STATE_TERMINATED)
t->state = THREAD_STATE_RUNNABLE;
DEBUGTHREADS("is RUNNABLE", t);
- threads_list_unlock();
+ threadlist_unlock();
}
{
/* Set the state inside a lock. */
- threads_list_lock();
+ threadlist_lock();
if (t->state != THREAD_STATE_TERMINATED)
t->state = THREAD_STATE_WAITING;
DEBUGTHREADS("is WAITING", t);
- threads_list_unlock();
+ threadlist_unlock();
}
{
/* Set the state inside a lock. */
- threads_list_lock();
+ threadlist_lock();
if (t->state != THREAD_STATE_TERMINATED)
t->state = THREAD_STATE_TIMED_WAITING;
DEBUGTHREADS("is TIMED_WAITING", t);
- threads_list_unlock();
+ threadlist_unlock();
}
{
/* set the state in the lock */
- threads_list_lock();
+ threadlist_lock();
t->state = THREAD_STATE_TERMINATED;
DEBUGTHREADS("is TERMINATED", t);
- threads_list_unlock();
+ threadlist_unlock();
}
/* XXX we should stop the world here */
- /* lock the threads lists */
+ /* Lock the thread lists. */
- threads_list_lock();
+ threadlist_lock();
printf("Full thread dump CACAO "VERSION":\n");
#endif
}
- /* unlock the threads lists */
+ /* Unlock the thread lists. */
- threads_list_unlock();
+ threadlist_unlock();
}
#endif
+/* global variables ***********************************************************/
+
+extern methodinfo *thread_method_init;
+
#if defined(__LINUX__)
/* XXX Remove for exact-GC. */
extern bool threads_pthreads_implementation_nptl;
/* function prototypes ********************************************************/
void threads_preinit(void);
+void threads_init(void);
threadobject *threads_thread_new(void);
void threads_thread_free(threadobject *t);
/* implementation specific functions */
void threads_impl_preinit(void);
-
-void threads_list_lock(void);
-void threads_list_unlock(void);
+void threads_impl_init(void);
#if defined(ENABLE_GC_CACAO)
void threads_mutex_gc_lock(void);
/* lock the threads lists */
- threads_list_lock();
+ threadlist_lock();
/* iterate over all started threads */
/* unlock the threads lists */
- threads_list_unlock();
+ threadlist_unlock();
}
}
#endif
#include "config.h"
#include <assert.h>
-#include <errno.h>
#include <signal.h>
#include <stdint.h>
#include <stdlib.h>
this thread. */
if (sigemptyset(&mask) != 0)
- vm_abort("signal_init: sigemptyset failed: %s", strerror(errno));
+ vm_abort_errno("signal_init: sigemptyset failed");
#if !defined(WITH_CLASSPATH_SUN)
/* Let OpenJDK handle SIGINT itself. */
if (sigaddset(&mask, SIGINT) != 0)
- vm_abort("signal_init: sigaddset failed: %s", strerror(errno));
+ vm_abort_errno("signal_init: sigaddset failed");
#endif
#if !defined(__FREEBSD__)
if (sigaddset(&mask, SIGQUIT) != 0)
- vm_abort("signal_init: sigaddset failed: %s", strerror(errno));
+ vm_abort_errno("signal_init: sigaddset failed");
#endif
if (sigprocmask(SIG_BLOCK, &mask, NULL) != 0)
- vm_abort("signal_init: sigprocmask failed: %s", strerror(errno));
+ vm_abort_errno("signal_init: sigprocmask failed");
#if defined(__LINUX__) && defined(ENABLE_THREADS)
/* XXX Remove for exact-GC. */
function = (void (*)(int, siginfo_t *, void *)) handler;
if (sigemptyset(&act.sa_mask) != 0)
- vm_abort("signal_register_signal: sigemptyset failed: %s",
- strerror(errno));
+ vm_abort_errno("signal_register_signal: sigemptyset failed");
act.sa_sigaction = function;
act.sa_flags = flags;
if (sigaction(signum, &act, NULL) != 0)
- vm_abort("signal_register_signal: sigaction failed: %s",
- strerror(errno));
+ vm_abort_errno("signal_register_signal: sigaction failed");
}
methodinfo *m;
java_handle_t *p;
+#if !defined(NDEBUG)
+ if (opt_TraceTraps)
+ log_println("[signal_handle: trap %d]", type);
+#endif
+
#if defined(ENABLE_VMLOG)
vmlog_cacao_signl_type(type);
#endif
t = THREADOBJECT;
if (sigemptyset(&mask) != 0)
- vm_abort("signal_thread: sigemptyset failed: %s", strerror(errno));
+ vm_abort_errno("signal_thread: sigemptyset failed");
#if !defined(WITH_CLASSPATH_SUN)
/* Let OpenJDK handle SIGINT itself. */
if (sigaddset(&mask, SIGINT) != 0)
- vm_abort("signal_thread: sigaddset failed: %s", strerror(errno));
+ vm_abort_errno("signal_thread: sigaddset failed");
#endif
#if !defined(__FREEBSD__)
if (sigaddset(&mask, SIGQUIT) != 0)
- vm_abort("signal_thread: sigaddset failed: %s", strerror(errno));
+ vm_abort_errno("signal_thread: sigaddset failed");
#endif
for (;;) {
revisit this code with our new exact-GC. */
/* if (sigwait(&mask, &sig) != 0) */
-/* vm_abort("signal_thread: sigwait failed: %s", strerror(errno)); */
+/* vm_abort_errno("signal_thread: sigwait failed"); */
(void) sigwait(&mask, &sig);
#if defined(ENABLE_THREADS)
s4 vms = 0; /* number of VMs created */
bool vm_initializing = false;
-bool vm_exiting = false;
+bool vm_created = false;
+bool vm_exiting = false;
char *mainstring = NULL;
classinfo *mainclass = NULL;
/* AFTER: threads_preinit */
- if (!utf8_init())
- vm_abort("vm_create: utf8_init failed");
+ utf8_init();
/* AFTER: thread_preinit */
#endif
#if defined(ENABLE_THREADS)
- if (!threads_init())
- vm_abort("vm_create: threads_init failed");
+ threads_init();
#endif
/* Initialize the native VM subsystem. */
}
#endif
- /* increment the number of VMs */
+ /* Increment the number of VMs. */
vms++;
- /* initialization is done */
+ /* Initialization is done, VM is created.. */
+ vm_created = true;
vm_initializing = false;
-#if !defined(NDEBUG)
/* Print the VM configuration after all stuff is set and the VM is
initialized. */
if (opt_PrintConfig)
vm_printconfig();
-#endif
/* everything's ok */
threads_join_all_threads();
#endif
+ /* VM is gone. */
+
+ vm_created = false;
+
/* everything's ok */
return 0;
extern _Jv_JNIEnv *_Jv_env;
extern bool vm_initializing;
+extern bool vm_created;
extern bool vm_exiting;
extern char *mainstring;
#include "config.h"
-#include <errno.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
int opt_TraceReplacement = 0;
#endif
int opt_TraceSubsystemInitialization = 0;
+int opt_TraceTraps = 0;
enum {
OPT_TraceLinkClass,
OPT_TraceReplacement,
OPT_TraceSubsystemInitialization,
+ OPT_TraceTraps,
OPT_Vmlog,
OPT_VmlogStrings,
OPT_VmlogIgnore
{ "TraceReplacement", OPT_TraceReplacement, OPT_TYPE_VALUE, "trace on-stack replacement with the given verbosity level (default: 1)" },
#endif
{ "TraceSubsystemInitialization", OPT_TraceSubsystemInitialization, OPT_TYPE_BOOLEAN, "trace initialization of subsystems" },
-
+ { "TraceTraps", OPT_TraceTraps, OPT_TYPE_BOOLEAN, "trace traps generated by JIT code" },
#if defined(ENABLE_VMLOG)
{ "Vmlog", OPT_Vmlog, OPT_TYPE_VALUE, "prefix for vmlog trace files (enables vmlog)" },
{ "VmlogStrings", OPT_VmlogStrings, OPT_TYPE_VALUE, "prefix of vmlog string file to load" },
file = fopen(filename, "w");
if (file == NULL)
- vm_abort("options_xx: fopen failed: %s", strerror(errno));
+ vm_abort_errno("options_xx: fopen failed");
opt_ProfileMemoryUsageGNUPlot = file;
break;
opt_TraceSubsystemInitialization = enable;
break;
+ case OPT_TraceTraps:
+ opt_TraceTraps = enable;
+ break;
+
#if defined(ENABLE_VMLOG)
case OPT_Vmlog:
if (value == NULL)
extern int opt_TraceReplacement;
#endif
extern int opt_TraceSubsystemInitialization;
+extern int opt_TraceTraps;
/* function prototypes ********************************************************/
utf *utf_clinit; /* <clinit> */
utf *utf_clone; /* clone */
utf *utf_finalize; /* finalize */
+utf *utf_main;
utf *utf_run; /* run */
utf *utf_add;
utf *utf_java_lang_String__java_lang_Class;
utf *utf_java_lang_Thread__V; /* (Ljava/lang/Thread;)V */
utf *utf_java_lang_Thread_java_lang_Throwable__V;
+utf *utf_Ljava_lang_ThreadGroup_Ljava_lang_String__V;
utf *utf_java_lang_Throwable__void; /* (Ljava/lang/Throwable;)V */
utf *utf_java_lang_Throwable__java_lang_Throwable;
*******************************************************************************/
-bool utf8_init(void)
+void utf8_init(void)
{
TRACESUBSYSTEMINITIALIZATION("utf8_init");
utf_clinit = utf_new_char("<clinit>");
utf_clone = utf_new_char("clone");
utf_finalize = utf_new_char("finalize");
+ utf_main = utf_new_char("main");
utf_run = utf_new_char("run");
utf_add = utf_new_char("add");
utf_java_lang_Thread_java_lang_Throwable__V =
utf_new_char("(Ljava/lang/Thread;Ljava/lang/Throwable;)V");
+ utf_Ljava_lang_ThreadGroup_Ljava_lang_String__V =
+ utf_new_char("(Ljava/lang/ThreadGroup;Ljava/lang/String;)V");
+
utf_java_lang_Throwable__void = utf_new_char("(Ljava/lang/Throwable;)V");
utf_java_lang_Throwable__java_lang_Throwable =
utf_null = utf_new_char("null");
utf_not_named_yet = utf_new_char("\t<not_named_yet>");
array_packagename = utf_new_char("\t<the array package>");
-
- /* everything's ok */
-
- return true;
}
/* src/vmcore/utf8.h - utf8 string functions
- Copyright (C) 1996-2005, 2006, 2007 R. Grafl, A. Krall, C. Kruegel,
- C. Oates, R. Obermaisser, M. Platter, M. Probst, S. Ring,
- E. Steiner, C. Thalinger, D. Thuernbeck, P. Tomsich, C. Ullrich,
- J. Wenninger, Institut f. Computersprachen - TU Wien
+ Copyright (C) 1996-2005, 2006, 2007, 2008
+ CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
This file is part of CACAO.
extern utf *utf_clinit;
extern utf *utf_clone;
extern utf *utf_finalize;
+extern utf *utf_main;
extern utf *utf_run;
extern utf *utf_add;
extern utf *utf_java_lang_String__java_lang_Class;
extern utf *utf_java_lang_Thread__V;
extern utf *utf_java_lang_Thread_java_lang_Throwable__V;
+extern utf *utf_Ljava_lang_ThreadGroup_Ljava_lang_String__V;
extern utf *utf_java_lang_Throwable__void;
extern utf *utf_java_lang_Throwable__java_lang_Throwable;
/* function prototypes ********************************************************/
/* initialize the utf8 subsystem */
-bool utf8_init(void);
+void utf8_init(void);
u4 utf_hashkey(const char *text, u4 length);
u4 utf_full_hashkey(const char *text, u4 length);