Tom Tromey wrote:

Instead of sleeping, how about wait/notify?

In the VM startup:

    if (debugging enabled)
       synchronized (whatever)
         while (! thread1_started || ! thread2_started)
           whatever.wait();

Then each thread will synchronize, set their flag, and notify().

For gcj specifically, this won't work because of the finnicky startup procedure. The JDWP threads must be created AND ready before the main user thread starts running.

So, for example, calling Object.wait causes a SEGV because there are no threads running yet. If it's a problem in gcj, I figure it's probably a problem in other VMs.

Perhaps some code will demonstrate approximately how gcj starts up. Here's _Jv_RunMain from gcj (I've left my original implementation for this intact but ifdef'd out):

void
_Jv_RunMain (JvVMInitArgs *vm_args, jclass klass, const char *name, int argc,
             const char **argv, bool is_jar)
{
#ifndef DISABLE_MAIN_ARGS
  _Jv_SetArgs (argc, argv);
#endif

  java::lang::Runtime *runtime = NULL;

  try
    {
      if (_Jv_CreateJavaVM (vm_args) < 0)
        {
          fprintf (stderr, "libgcj: couldn't create virtual machine\n");
          exit (1);
        }

      // 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 DISABLE_MAIN_ARGS
      arg_vec = JvConvertArgv (0, 0);
#else
      arg_vec = JvConvertArgv (argc - 1, argv + 1);
#endif

      // Start JDWP
      if (remoteDebug)
        {
          using namespace gnu::classpath::jdwp;
          Jdwp* jdwp = new Jdwp ();
          jdwp->setDaemon (true);
          jdwp->configure (JvNewStringLatin1 (jdwpOptions));
          jdwp->start ();
        
          // Wait for JDWP to initialize and start
#if 0   
          while (!gnu::classpath::jdwp::Jdwp::isInitialized ())
            {
              struct timespec t;
              t.tv_sec = 0;
              t.tv_nsec = 1000 * 1000 * 50;
              nanosleep (&t, NULL);
            }
#else
          gnu::classpath::jdwp::Jdwp::waitForInitialization ();
#endif
        }

      using namespace gnu::java::lang;
      if (klass)
        main_thread = new MainThread (klass, arg_vec);
      else
        main_thread = new MainThread (JvNewStringLatin1 (name),
                                      arg_vec, is_jar);

      // Send VmInit
      gnu::classpath::jdwp::event::VmInitEvent* event;
      event = new gnu::classpath::jdwp::event::VmInitEvent (main_thread);
      gnu::classpath::jdwp::Jdwp::notify (event);
    }
  catch (java::lang::Throwable *t)
    {
      java::lang::System::err->print (JvNewStringLatin1
        ("Exception during runtime initialization: "));
      java::lang::System::err->println(t);
      t->printStackTrace();
      if (runtime)
        runtime->exit (1);
      // In case the runtime creation failed.
      ::exit (1);
    }

  _Jv_AttachCurrentThread (main_thread);
  if (gnu::classpath::jdwp::Jdwp::isDebugging
      && gnu::classpath::jdwp::Jdwp::suspendOnStartup ())
    {
      // Suspend this thread for JDWP
      _Jv_ThreadDebugSuspend (_Jv_ThreadGetData (main_thread));
    }
  _Jv_ThreadRun (main_thread);

  if (::gnu::classpath::jdwp::Jdwp::isDebugging)
    {
      using namespace ::gnu::classpath::jdwp;
      event::VmDeathEvent* event = new event::VmDeathEvent ();
      Jdwp::notify (event);
    }

  int status = (int) java::lang::ThreadGroup::had_uncaught_exception;
  runtime->exit (status);

  // If we got here then something went wrong, as MainThread is not
  // supposed to terminate.
  ::exit (1);
}

As you can see, the startup sequence remains relatively unchanged. We just create some new threads when JDWP is initialized and send two events to indicate that the VM is live/dead.

Keith

Reply via email to