This is the mail archive of the java-patches@gcc.gnu.org mailing list for the Java project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

PATCH: Clean up runtime initialization, fix stack trace bug


This patch fixes a long-standing runtime bug on the mainline: uncaught 
exceptions in the main thread will cause the runtime to abort due to 
lack of a default exception handler. I've fixed this by changing the way 
the first thread is started. A new interface for _Jv_AttachThread() is 
added, which takes an unstarted thread object object as a parameter. 
_Jv_RunMain now creates a gnu.gcj.runtime.FirstThread and passes it to 
this. The same thread start up code can now be used for both attached 
and regularly created threads, eliminating some code duplication. This 
approach also has the advantage of localising much of the code relating 
to looking up and calling main in FirstThread again. In addition, the 
two entry points we previously had into the runtime, JvRunMain and 
_Jv_RunMain, are now merged into one, with JvRunMain now a simple 
wrapper around the other.

I'm checking this in.

regards

Bryce

2001-08-26  Bryce McKinlay  <bryce@waitaki.otago.ac.nz>

	* Makefile.am: New friends for java/lang/Thread.h.
	* prims.cc (runFirst): Removed.
	(JvRunMain): Merged into _Jv_RunMain. Now just calls that.
	(_Jv_RunMain): Now takes either a klass or class name parameter.
	Create a gnu.gcj.runtime.FirstThread and attach the native thread
	to that, then run it using _Jv_ThreadRun. Remove special handling of
	jar files, instead pass is_jar parameter through to FirstThread.
	* gcj/javaprims.h: Add prototypes for _Jv_ThreadRun and new variant
	of _Jv_AttachCurrentThread.
	* gnu/gcj/runtime/FirstThread.java (FirstThread): Now extends Thread.
	(run): New method. Take care of looking up main class manifest 
	attribute and calling forName if neccessary. Then call call_main.
	(call_main): New native method.
	* gnu/gcj/runtime/natFirstThread.cc (call_main): New function, code 
	relocated from prims.cc. Look up and call main method. 
	* java/lang/Thread.java (run_): Removed.
	* java/lang/natThread.cc (run_): Renamed to...
	(_Jv_ThreadRun): this. JVMPI notification code moved to ...
	(_Jv_NotifyThreadStart): here. New function.
	(countStackFrames, destroy, resume, suspend, stop): Throw
	UnsupportedOperationExceptions rather than JvFail'ing.
	(_Jv_AttachCurrentThread): New variant takes a Thread argument.
	Existing version wraps new variant.

Index: Makefile.am
===================================================================
RCS file: /cvs/gcc/gcc/libjava/Makefile.am,v
retrieving revision 1.161
diff -u -r1.161 Makefile.am
--- Makefile.am	2001/08/15 20:46:47	1.161
+++ Makefile.am	2001/08/26 10:44:40
@@ -267,7 +267,7 @@
 java/lang/ClassLoader.h: java/lang/ClassLoader.class libgcj.jar
 	$(GCJH) -classpath $(top_builddir) \
 		-friend 'jclass _Jv_FindClass (_Jv_Utf8Const *name, java::lang::ClassLoader *loader);' \
-		-friend 'void _Jv_RunMain (const char *name, int argc, const char **argv, bool is_jar);' \
+		-friend 'void _Jv_RunMain (jclass klass, const char *name, int argc, const char **argv, bool is_jar);' \
 		$(basename $<)
 
 ## Our internal main program needs to be able to create a FirstThread.
@@ -284,6 +284,8 @@
 		-prepend '#define _JV_INTERRUPTED 2' \
 		-friend '_Jv_JNIEnv * _Jv_GetCurrentJNIEnv ();' \
 		-friend 'void _Jv_SetCurrentJNIEnv (_Jv_JNIEnv *env);' \
+		-friend 'void _Jv_ThreadRun (java::lang::Thread* thread);' \
+		-friend 'jint _Jv_AttachCurrentThread(java::lang::Thread* thread);' \
 		-friend 'java::lang::Thread* _Jv_AttachCurrentThread(jstring name, java::lang::ThreadGroup* group);' \
 		-friend 'jint _Jv_DetachCurrentThread ();' \
 		$(basename $<)
@@ -1343,6 +1345,7 @@
 gnu/gcj/convert/natOutput_SJIS.cc \
 gnu/gcj/io/natSimpleSHSStream.cc \
 gnu/gcj/io/shs.cc \
+gnu/gcj/runtime/natFirstThread.cc \
 java/io/natFile.cc \
 java/io/natFileDescriptor.cc \
 java/io/natObjectInputStream.cc \
Index: gij.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/gij.cc,v
retrieving revision 1.13
diff -u -r1.13 gij.cc
--- gij.cc	2001/05/23 18:10:30	1.13
+++ gij.cc	2001/08/26 10:44:40
@@ -136,5 +136,5 @@
       exit (1);
     }
 
-  _Jv_RunMain (argv[i], argc - i, argv + i, jar_mode);
+  _Jv_RunMain (NULL, argv[i], argc - i, argv + i, jar_mode);
 }
Index: prims.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/prims.cc,v
retrieving revision 1.56
diff -u -r1.56 prims.cc
--- prims.cc	2001/08/03 01:25:17	1.56
+++ prims.cc	2001/08/26 10:44:40
@@ -90,14 +90,12 @@
 #endif
 
 // The name of this executable.
-static char * _Jv_execName;
+static char *_Jv_execName;
 
 // Stash the argv pointer to benefit native libraries that need it.
 const char **_Jv_argv;
 int _Jv_argc;
 
-typedef void main_func (jobject);
-
 #ifdef ENABLE_JVMPI
 // Pointer to JVMPI notification functions.
 void (*_Jv_JVMPI_Notify_OBJECT_ALLOC) (JVMPI_Event *event);
@@ -643,7 +641,7 @@
 // it will only scan the qthreads stacks.
 
 // Command line arguments.
-static jobject arg_vec;
+static JArray<jstring> *arg_vec;
 
 // The primary thread.
 static java::lang::Thread *main_thread;
@@ -690,7 +688,6 @@
 
 #endif
 
-
 #ifndef DISABLE_GETENV_PROPERTIES
 
 static char *
@@ -885,93 +882,15 @@
   return 0;
 }
 
-static void
-runFirst (::java::lang::Class *klass, ::java::lang::Object *args)
-{
-  Utf8Const* main_signature = _Jv_makeUtf8Const ("([Ljava.lang.String;)V", 22);
-  Utf8Const* main_name = _Jv_makeUtf8Const ("main", 4);
-
-  _Jv_Method *meth = _Jv_GetMethodLocal (klass, main_name, main_signature);
-
-  // Some checks from Java Spec section 12.1.4.
-  const char *msg = NULL;
-  if (meth == NULL)
-    msg = "no suitable method `main' in class";
-  else if (! java::lang::reflect::Modifier::isStatic(meth->accflags))
-    msg = "`main' must be static";
-  else if (! java::lang::reflect::Modifier::isPublic(meth->accflags))
-    msg =  "`main' must be public";
-  if (msg != NULL)
-    {
-      fprintf (stderr, "%s\n", msg);
-      ::exit(1);
-    }
-
-#ifdef WITH_JVMPI
-  if (_Jv_JVMPI_Notify_THREAD_START)
-    {
-      JVMPI_Event event;
-
-      jstring thread_name = getName ();
-      jstring group_name = NULL, parent_name = NULL;
-      java::lang::ThreadGroup *group = getThreadGroup ();
-
-      if (group)
-	{
-	  group_name = group->getName ();
-	  group = group->getParent ();
-
-	  if (group)
-	    parent_name = group->getName ();
-	}
-
-      int thread_len = thread_name ? JvGetStringUTFLength (thread_name) : 0;
-      int group_len = group_name ? JvGetStringUTFLength (group_name) : 0;
-      int parent_len = parent_name ? JvGetStringUTFLength (parent_name) : 0;
-
-      char thread_chars[thread_len + 1];
-      char group_chars[group_len + 1];
-      char parent_chars[parent_len + 1];
-
-      if (thread_name)
-	JvGetStringUTFRegion (thread_name, 0, 
-			      thread_name->length(), thread_chars);
-      if (group_name)
-	JvGetStringUTFRegion (group_name, 0, 
-			      group_name->length(), group_chars);
-      if (parent_name)
-	JvGetStringUTFRegion (parent_name, 0, 
-			      parent_name->length(), parent_chars);
-
-      thread_chars[thread_len] = '\0';
-      group_chars[group_len] = '\0';
-      parent_chars[parent_len] = '\0';
-
-      event.event_type = JVMPI_EVENT_THREAD_START;
-      event.env_id = NULL;
-      event.u.thread_start.thread_name = thread_chars;
-      event.u.thread_start.group_name = group_chars;
-      event.u.thread_start.parent_name = parent_chars;
-      event.u.thread_start.thread_id = (jobjectID) this;
-      event.u.thread_start.thread_env_id = _Jv_GetCurrentJNIEnv ();
-
-      _Jv_DisableGC ();
-      (*_Jv_JVMPI_Notify_THREAD_START) (&event);
-      _Jv_EnableGC ();
-    }
-#endif
-
-  main_func *real_main = (main_func *) meth->ncode;
-  (*real_main) (args);
-}
-
 void
-JvRunMain (jclass klass, int argc, const char **argv)
+_Jv_RunMain (jclass klass, const char *name, int argc, const char **argv, 
+	     bool is_jar)
 {
   _Jv_argv = argv;
   _Jv_argc = argc;
 
-  _Jv_CreateJavaVM (NULL);
+  java::lang::Runtime *runtime = NULL;
+
 #ifdef HAVE_PROC_SELF_EXE
   char exec_name[20];
   sprintf (exec_name, "/proc/%d/exe", getpid ());
@@ -979,69 +898,52 @@
 #else
   _Jv_ThisExecutable (argv[0]);
 #endif
-
-  // Get the Runtime here.  We want to initialize it before searching
-  // for `main'; that way it will be set up if `main' is a JNI method.
-  java::lang::Runtime *rtime = java::lang::Runtime::getRuntime ();
-
-  main_thread = _Jv_AttachCurrentThread (JvNewStringLatin1 ("main"), NULL);
-  arg_vec = JvConvertArgv (argc - 1, argv + 1);
-  runFirst (klass, arg_vec);
-  _Jv_ThreadWait ();
 
-  int status = (int) java::lang::ThreadGroup::had_uncaught_exception;
-    
-  rtime->_exit (status);
-}
-
-void
-_Jv_RunMain (const char *name, int argc, const char **argv, bool is_jar)
-{
-  jstring class_name;
+  try
+    {
+      _Jv_CreateJavaVM (NULL);
 
-  _Jv_CreateJavaVM (NULL);
+      // Get the Runtime here.  We want to initialize it before searching
+      // for `main'; that way it will be set up if `main' is a JNI method.
+      runtime = java::lang::Runtime::getRuntime ();
 
-#ifdef HAVE_PROC_SELF_EXE
-  char exec_name[20];
-  sprintf (exec_name, "/proc/%d/exe", getpid ());
-  _Jv_ThisExecutable (exec_name);
-#endif
+      arg_vec = JvConvertArgv (argc - 1, argv + 1);
+      
+      if (klass)
+	main_thread = new gnu::gcj::runtime::FirstThread (klass, arg_vec);
+      else
+	main_thread = new gnu::gcj::runtime::FirstThread 
+			(JvNewStringLatin1 (name), arg_vec, is_jar);
 
-  // Get the Runtime here.  We want to initialize it before searching
-  // for `main'; that way it will be set up if `main' is a JNI method.
-  java::lang::Runtime *rtime = java::lang::Runtime::getRuntime ();
-
-  main_thread = _Jv_AttachCurrentThread (JvNewStringLatin1 ("main"), NULL);
-
-  if (is_jar)
-    {
-      // name specifies a jar file.  We must now extract the
-      // Main-Class attribute from the jar's manifest file.
-      // This is done by gnu.gcj.runtime.FirstThread.getMain.
-      _Jv_Jar_Class_Path = strdup (name);
-      jstring jar_name = JvNewStringLatin1 (name);
-      // FirstThread.getMain extracts the main class name.
-      class_name = gnu::gcj::runtime::FirstThread::getMain (jar_name);
-
-      // We need a new ClassLoader because the classpath must be the
-      // jar file only.  The easiest way to do this is to lose our
-      // reference to the previous classloader.
-      java::lang::ClassLoader::system = NULL;
+      if (is_jar)
+	{
+	  // We need a new ClassLoader because the classpath must be the
+	  // jar file only.  The easiest way to do this is to lose our
+	  // reference to the previous classloader.
+	  _Jv_Jar_Class_Path = strdup (name);
+	  java::lang::ClassLoader::system = NULL;
+	}
     }
-  else
-    class_name = JvNewStringLatin1 (name);
-
-  arg_vec = JvConvertArgv (argc - 1, argv + 1);
-
-  if (class_name)
+  catch (java::lang::Throwable *t)
     {
-      runFirst(java::lang::Class::forName (class_name), arg_vec);
-      _Jv_ThreadWait ();
+      java::lang::System::err->println (JvNewStringLatin1 
+        ("Exception during runtime initialization"));
+      t->printStackTrace();
+      runtime->exit (1);
     }
 
+  _Jv_AttachCurrentThread (main_thread);
+  _Jv_ThreadRun (main_thread);
+  _Jv_ThreadWait ();
+
   int status = (int) java::lang::ThreadGroup::had_uncaught_exception;
+  runtime->exit (status);
+}
 
-  rtime->exit (status);
+void
+JvRunMain (jclass klass, int argc, const char **argv)
+{
+  _Jv_RunMain (klass, NULL, argc, argv, false);
 }
 
 
Index: gcj/javaprims.h
===================================================================
RCS file: /cvs/gcc/gcc/libjava/gcj/javaprims.h,v
retrieving revision 1.24
diff -u -r1.24 javaprims.h
--- javaprims.h	2001/08/15 20:46:48	1.24
+++ javaprims.h	2001/08/26 10:44:40
@@ -398,6 +398,11 @@
 extern "C" jsize _Jv_GetStringUTFRegion (jstring, jsize, jsize, char *);
 
 extern jint _Jv_CreateJavaVM (void* /*vm_args*/);
+
+void
+_Jv_ThreadRun (java::lang::Thread* thread);
+jint
+_Jv_AttachCurrentThread(java::lang::Thread* thread);
 extern "C" java::lang::Thread*
 _Jv_AttachCurrentThread(jstring name, java::lang::ThreadGroup* group);
 extern "C" jint _Jv_DetachCurrentThread (void);
Index: gnu/gcj/runtime/FirstThread.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/gnu/gcj/runtime/FirstThread.java,v
retrieving revision 1.8
diff -u -r1.8 FirstThread.java
--- FirstThread.java	2001/05/22 06:47:48	1.8
+++ FirstThread.java	2001/08/26 10:44:40
@@ -17,12 +17,45 @@
  * @date August 24, 1998 
  */
 
-// This is entirely internal to our implementation.
-
-final class FirstThread
+final class FirstThread extends Thread
 {
-  public static String getMain (String name)
+  public FirstThread (Class k, String[] args)
+  {
+    super (null, null, "main");
+    klass = k;
+    this.args = args;
+  }
+
+  public FirstThread (String class_name, String[] args, boolean is_jar)
+  {
+    super (null, null, "main");
+    klass_name = class_name;
+    this.args = args;
+    this.is_jar = is_jar;
+  }
+  
+  public void run()
   {
+    if (is_jar)
+      klass_name = getMain(klass_name);
+    
+    if (klass == null)
+      {
+        try
+	  {
+	    klass = Class.forName(klass_name);
+	  }
+	catch (ClassNotFoundException x)
+	  {
+	    throw new NoClassDefFoundError(klass_name);
+	  }
+      }
+    
+    call_main();
+  }
+
+  private String getMain (String name)
+  {
     String mainName = null;
     try {
 
@@ -37,15 +70,21 @@
     }
 
     if (mainName == null)
-      System.err.println ("Failed to load Main-Class manifest attribute from\n"
-			  + name);
+      {
+	System.err.println ("Failed to load Main-Class manifest attribute from\n"
+			    + name);
+	System.exit(1);
+      }
     return mainName;
   }
 
+  private native void call_main ();
+
   // Private data.
   private Class klass;
   private String klass_name;
   private Object args;
+  private boolean is_jar;
 
   // If the user links statically then we need to ensure that these
   // classes are linked in.  Otherwise bootstrapping fails.  These
Index: include/jvm.h
===================================================================
RCS file: /cvs/gcc/gcc/libjava/include/jvm.h,v
retrieving revision 1.36
diff -u -r1.36 jvm.h
--- jvm.h	2001/05/29 22:18:41	1.36
+++ jvm.h	2001/08/26 10:44:40
@@ -166,7 +166,8 @@
 void _Jv_SetMaximumHeapSize (const char *arg);
 
 extern "C" void JvRunMain (jclass klass, int argc, const char **argv);
-void _Jv_RunMain (const char* name, int argc, const char **argv, bool is_jar);
+void _Jv_RunMain (jclass klass, const char *name, int argc, const char **argv, 
+		  bool is_jar);
 
 // This function is used to determine the hash code of an object.
 inline jint
@@ -276,7 +277,9 @@
 void _Jv_SetCurrentJNIEnv (_Jv_JNIEnv *);
 
 struct _Jv_JavaVM;
-_Jv_JavaVM *_Jv_GetJavaVM ();
+_Jv_JavaVM *_Jv_GetJavaVM (); 
+
+extern jint _Jv_CreateJavaVM (void* /*vm_args*/); // prims.cc
 
 #ifdef ENABLE_JVMPI
 #include "jvmpi.h"
Index: java/lang/Thread.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/lang/Thread.java,v
retrieving revision 1.18
diff -u -r1.18 Thread.java
--- Thread.java	2001/05/22 06:47:48	1.18
+++ Thread.java	2001/08/26 10:44:40
@@ -109,8 +109,6 @@
 
   public final native void resume ();
 
-  // This method exists only to avoid a warning from the C++ compiler.
-  private static final native void run_ (Object obj);
   private final native void finish_ ();
 
   // Check the thread's interrupted status. If clear_flag is true, the 
Index: java/lang/natThread.cc
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/lang/natThread.cc,v
retrieving revision 1.22
diff -u -r1.22 natThread.cc
--- natThread.cc	2001/05/22 06:47:48	1.22
+++ natThread.cc	2001/08/26 10:44:40
@@ -87,7 +87,8 @@
 java::lang::Thread::countStackFrames (void)
 {
   // NOTE: This is deprecated in JDK 1.2.
-  JvFail ("java::lang::Thread::countStackFrames unimplemented");
+  throw new UnsupportedOperationException
+    (JvNewStringLatin1 ("Thread.countStackFrames unimplemented"));
   return 0;
 }
 
@@ -102,7 +103,8 @@
 {
   // NOTE: This is marked as unimplemented in the JDK 1.2
   // documentation.
-  JvFail ("java::lang::Thread::destroy unimplemented");
+  throw new UnsupportedOperationException
+    (JvNewStringLatin1 ("Thread.destroy unimplemented"));
 }
 
 void
@@ -142,7 +144,8 @@
 java::lang::Thread::resume (void)
 {
   checkAccess ();
-  JvFail ("java::lang::Thread::resume unimplemented");
+  throw new UnsupportedOperationException
+    (JvNewStringLatin1 ("Thread.resume unimplemented"));
 }
 
 void
@@ -213,12 +216,11 @@
   _Jv_MutexUnlock (&nt->join_mutex);  
 }
 
-void
-java::lang::Thread::run_ (jobject obj)
+// Run once at thread startup, either when thread is attached or when 
+// _Jv_ThreadRun is called.
+static void
+_Jv_NotifyThreadStart (java::lang::Thread* thread)
 {
-  java::lang::Thread *thread = (java::lang::Thread *) obj;
-  try
-    {
 #ifdef ENABLE_JVMPI
       if (_Jv_JVMPI_Notify_THREAD_START)
 	{
@@ -272,7 +274,14 @@
 	  _Jv_EnableGC ();
 	}
 #endif
+}
 
+void
+_Jv_ThreadRun (java::lang::Thread* thread)
+{
+  try
+    {
+      _Jv_NotifyThreadStart (thread);
       thread->run ();
     }
   catch (java::lang::Throwable *t)
@@ -304,14 +313,14 @@
   alive_flag = true;
   startable_flag = false;
   natThread *nt = (natThread *) data;
-  _Jv_ThreadStart (this, nt->thread, (_Jv_ThreadStartFunc *) &run_);
+  _Jv_ThreadStart (this, nt->thread, (_Jv_ThreadStartFunc *) &_Jv_ThreadRun);
 }
 
 void
 java::lang::Thread::stop (java::lang::Throwable *)
 {
   throw new UnsupportedOperationException
-    (JvNewStringLatin1 ("java::lang::Thread::stop unimplemented"));
+    (JvNewStringLatin1 ("Thread.stop unimplemented"));
 }
 
 void
@@ -319,7 +328,7 @@
 {
   checkAccess ();
   throw new UnsupportedOperationException 
-    (JvNewStringLatin1 ("java::lang::Thread::suspend unimplemented"));
+    (JvNewStringLatin1 ("Thread.suspend unimplemented"));
 }
 
 static int nextThreadNumber = 0;
@@ -373,6 +382,20 @@
   ((natThread *) t->data)->jni_env = env;
 }
 
+// Attach the current native thread to an existing (but unstarted) Thread 
+// object. Returns -1 on failure, 0 upon success.
+jint
+_Jv_AttachCurrentThread(java::lang::Thread* thread)
+{
+  if (thread == NULL || thread->startable_flag == false)
+    return -1;
+  thread->startable_flag = false;
+  thread->alive_flag = true;
+  natThread *nt = (natThread *) thread->data;
+  _Jv_ThreadRegister (nt->thread);
+  return 0;
+}
+
 java::lang::Thread*
 _Jv_AttachCurrentThread(jstring name, java::lang::ThreadGroup* group)
 {
@@ -382,10 +405,8 @@
   if (name == NULL)
     name = java::lang::Thread::gen_name ();
   thread = new java::lang::Thread (NULL, group, NULL, name);
-  thread->startable_flag = false;
-  thread->alive_flag = true;
-  natThread *nt = (natThread *) thread->data;
-  _Jv_ThreadRegister (nt->thread);
+  _Jv_AttachCurrentThread (thread);
+  _Jv_NotifyThreadStart (thread);
   return thread;
 }
 
// natFirstThread.cc - Implementation of FirstThread native methods.

/* Copyright (C) 1998, 1999, 2000, 2001  Free Software Foundation

   This file is part of libgcj.

This software is copyrighted work licensed under the terms of the
Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
details.  */

#include <config.h>

#include <stdio.h>
#include <stdlib.h>

#include <gcj/cni.h>
#include <jvm.h>

#include <gnu/gcj/runtime/FirstThread.h>

typedef void main_func (jobject);

void
gnu::gcj::runtime::FirstThread::call_main (void)
{
  Utf8Const* main_signature = _Jv_makeUtf8Const ("([Ljava.lang.String;)V", 22);
  Utf8Const* main_name = _Jv_makeUtf8Const ("main", 4);

  _Jv_Method *meth = _Jv_GetMethodLocal (klass, main_name, main_signature);

  // Some checks from Java Spec section 12.1.4.
  const char *msg = NULL;
  if (meth == NULL)
    msg = "no suitable method `main' in class";
  else if (! java::lang::reflect::Modifier::isStatic(meth->accflags))
    msg = "`main' must be static";
  else if (! java::lang::reflect::Modifier::isPublic(meth->accflags))
    msg =  "`main' must be public";
  if (msg != NULL)
    {
      fprintf (stderr, "%s\n", msg);
      ::exit(1);
    }

  main_func *real_main = (main_func *) meth->ncode;
  (*real_main) (args);
}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]