Opened 3 years ago

Closed 2 years ago

#33 closed task (fixed)

Resolve the main thread issue

Reported by: dmik Owned by:
Priority: major Milestone: GA2
Component: general Version:
Severity: low Keywords:
Cc:

Description (last modified by dmik)

Java never starts VM execution on the main thread of the process. It instead fires off a new thread (right after performing basic parsing of command line arguments) and starts the VM there. The main thread then waits indefinitely for this new thread to terminate and once terminated, it returns from main().

This has one strange effect. Java uses many additional DLLs for implementing native methods and for providing utility functions and these DLLs often use C++ classes. These classes have static objects that get created at program startup and destroyed at program termination. These objects sometimes use various JVM calls.

The thing is that the destruction of static objects always happens on the main thread, after all other threads of the process have been stopped (at least this is the case for VAC/GCC on OS/2 where such destruction either takes place at DLL termination time (GCC) or during DosExitList? processing (VAC)).

So, in case of static objects in helper Java DLLs, their destruction happens after the JVM has been exited and therefore calling it will simply crash (becaus most internal structures have been destroyed by the exit routine).

This in particular happens with AWT native methods which are implemented with the help of C++ classes on Windows (and therefore on OS/2). A particular example is the static GDIHashable AwtPen::cache object (see awt_Pen.cpp). Upon destruction, it frees memory and in the debug build the free routine collects some extra information and uses the JVM monitor primitive to serialize access to its internal structures containing this information. But this primitive may not function when JVM is shut down and therefore the application crashes.

Change History (6)

comment:1 Changed 3 years ago by dmik

  • Description modified (diff)
  • Milestone set to Beta

JFTR. I found this problem when I discovered that the debug Java build always crashes in ODINCRTD.DLL when KERNEL32.DLL tries to write something to the odin32.log file. I didn't have time to work on that before (taking into account that it happens only in the debug build) but today I looked at it and found a problem which I described in http://svn.netlabs.org/odin32/ticket/19. I changed AWT.DLL so that it calls its destructors through DosExitList?, e.g. when Odin CRT is still in place and usable, and the ODINCRTD.DLL crash had gone. But then I discovered the problem I describe in this ticket.

comment:2 Changed 3 years ago by dmik

BTW, I tried changing the Java startup routine so that it does not fire up a new thread but instead creates the JVM on the main thread (which could theoretically avoid the issue in question). It seemed to work (I could do all normal operations in the test application) but when I attempted to exit the application, guess what: it hung in DosExitList?. And it's 100% reproducible (I'm on UNI now, not on SMP).

I really have to investigate all this.

I also wonder how this may work on Windows (which should be similar to OS/2 in terms of C++ object creation and destruction). I will check that tomorrow.

comment:3 Changed 3 years ago by dmik

I checked the Windows build. The difference is that on Windows any thread may end process execution, not only the first one. In case of Java, it's the JVM thread (which is thread #3, thread #2 is the JavaMain? thread started by the real main thread as described above) that exits the process when the Java program terminates.

Since all static C++ objects are destroyed by the thread which exits the process, it works well on Windows -- JVM is still operational (to the required extent) when JVM thread is in process of handling the exit().

AFAIR, Linux uses the same strategy regarding the process exit as Windows so it's a no issue for it too.

I have to think on a solution for OS/2, it cannot go like it is.

comment:4 Changed 3 years ago by dmik

  • Milestone changed from RC to Enhanced

It actually can go like this in the release build at least (which doesn't use printf for debug printing at exit). Postponing.

comment:5 Changed 3 years ago by dmik

  • Severity set to low

There is another related problem. If you attempt to terminate the Java process with Ctrl-Break, OS/2 will call the Ctrl-Break handler on thread 1 as well. And this makes similar problems: this thread is not known to Java at all (as it's not intended to call any JVM code) so that many JVM functions will not function properly. E.g. Thread::current() will always return NULL when called on thread 1 which breaks access to internal JVM structures and causes crashes. You may find an example of such a crashing application in #78.

comment:6 Changed 2 years ago by dmik

  • Milestone changed from Enhanced to GA2
  • Resolution set to fixed
  • Status changed from new to closed

I have fixed the issue in r341 by creating the JVM on thread 1, as planned. Now killing the Java process with Ctrl-C/Ctrl-Break doesn't hang (tested with OpenFire?).

Note: See TracTickets for help on using tickets.