Thread overview
D called from Java
Aug 18, 2007
Frank Benoit
Aug 18, 2007
Sean Kelly
Aug 19, 2007
Frank Benoit
Aug 19, 2007
Sean Kelly
Aug 19, 2007
Juan Jose Comellas
Aug 19, 2007
Juan Jose Comellas
Aug 19, 2007
Frank Benoit
Aug 19, 2007
Juan Jose Comellas
August 18, 2007
I am working on a framework to automate the process of binding Java and D. See the dsource project TioLink.

The main problem i see currently is, that both available D runtime libraries - Phobos and Tango - do not have support for registering Threads that are created "outside".

If a dll (or .so) is called from Java, it is called always with several threads. Even in a single threaded Java application. Java runs the finalizer in an own Thread, so it is essential to be able to register all calling threads on the library entry points. Only with this, the D GC can savely do its job.

Hopefully both runtime libs will have support for multithreaded libraries in near future :)
August 18, 2007
Frank Benoit wrote:
> I am working on a framework to automate the process of binding Java and
> D. See the dsource project TioLink.
> 
> The main problem i see currently is, that both available D runtime
> libraries - Phobos and Tango - do not have support for registering
> Threads that are created "outside".
...
> Hopefully both runtime libs will have support for multithreaded
> libraries in near future :)

Tango will have this once I have time for it.  Likely not long after the conference :-)


Sean
August 19, 2007
Sean Kelly schrieb:
> Tango will have this once I have time for it.  Likely not long after the conference :-)

The more i think about it, how will it work?

The d runtime (drt) needs to know all running threads, for the following
reason:
 * be able to stop all threads while the GC is running
 * provide a thread local storage container.
 * synchronization?

But does the drt also need to know about the termination of a thread?

If so, the registration must be paired with a deregistration on each library entry point. This also means, register and deregister need to be really fast because they are called every time the lib is entered or left.

export extern(System)
jint Java_test_Test_test( JNIEnv* env, jobject obj ) {
    Thread.register();
    scope( exit ) Thread.deregister();
    // ...
}

In the scenario, were the D GC is actually running and has all known threads suspended, the Thread.register() call needs to block until the GC completes. Probably the methods are better called like enterD/leaveD.

export extern(System)
jint Java_test_Test_test( JNIEnv* env, jobject obj ) {
    Thread.enterD();
    scope( exit ) Thread.leaveD();
    // ...
}

Hopefully this will not raise some new deadlock conditions.
1. D ctor remove held Java global references
2. this removal triggers the Java finalizer
3. another java object.finalize calls therefore the D lib, to signal the
end of the javas object lifecycle.
4. the call blocks and the dtor call from (1) does not return.



August 19, 2007
Frank Benoit wrote:
> Sean Kelly schrieb:
>> Tango will have this once I have time for it.  Likely not long after the
>> conference :-)
> 
> The more i think about it, how will it work?
> 
> The d runtime (drt) needs to know all running threads, for the following
> reason:
>  * be able to stop all threads while the GC is running
>  * provide a thread local storage container.
>  * synchronization?

The user would call some routine from each thread he wants to register, or perhaps simply pass the thread id.  The threads would have to be de-registered when they terminate as well.

> But does the drt also need to know about the termination of a thread?

Yes.  It may be possible to get away without explicitly notifying the GC of thread termination on Windows, based on how that code works, but on Unix I think such notification is necessary.

> If so, the registration must be paired with a deregistration on each
> library entry point. This also means, register and deregister need to be
> really fast because they are called every time the lib is entered or left.

The registration would involve a memory allocation for the proxy thread object and a mutex lock to place it in a list.

> export extern(System)
> jint Java_test_Test_test( JNIEnv* env, jobject obj ) {
>     Thread.register();
>     scope( exit ) Thread.deregister();
>     // ...
> }
> 
> In the scenario, were the D GC is actually running and has all known
> threads suspended, the Thread.register() call needs to block until the
> GC completes. Probably the methods are better called like enterD/leaveD.

I /think/ the only risk of being blocked by the GC will be allocating the proxy thread object.  It should be okay if a GC cycle is running after this time when the object is enlisted, though it would mean an attempted resume on a running thread.  This should work fine given how things are implemented, but I'll have to see if I can test it somehow to be sure.

> export extern(System)
> jint Java_test_Test_test( JNIEnv* env, jobject obj ) {
>     Thread.enterD();
>     scope( exit ) Thread.leaveD();
>     // ...
> }
> 
> Hopefully this will not raise some new deadlock conditions.
> 1. D ctor remove held Java global references
> 2. this removal triggers the Java finalizer
> 3. another java object.finalize calls therefore the D lib, to signal the
> end of the javas object lifecycle.
> 4. the call blocks and the dtor call from (1) does not return.

If the D GC is running then all threads it knows about will be suspended until the collection completes.  The mutex guarding memory allocations will be locked as well.  So operations in D object dtors must consider this.  It's possible to deadlock even without this Java integration if an attempt is made to enter a synchronized block that may already be held by a suspended thread.  In fact, this is why Tango does not have notifyRegister--using the routine to unregister a slot could easily deadlock for this exact reason.


Sean
August 19, 2007
You might have a problem if you have the D runtime and the JVM running within the same process on Linux/Unix. Both the D and the Java garbage collector (at least on JDK 1.4) use the SIGUSR1 and SIGUSR2 signals to suspend and resume the garbage collection process, so you might want to check whether there is any problem if a set of threads created in one section of the process (e.g. D runtime) get a signal to start the garbage collection from the other part of the process (e.g. JVM). I'm not sure if the D runtime is prepared to handle this situation.

I had a problem with this when embedding the JVM in a program written in C that already used these signals for its own purposes.


Frank Benoit wrote:

> I am working on a framework to automate the process of binding Java and D. See the dsource project TioLink.
> 
> The main problem i see currently is, that both available D runtime libraries - Phobos and Tango - do not have support for registering Threads that are created "outside".
> 
> If a dll (or .so) is called from Java, it is called always with several threads. Even in a single threaded Java application. Java runs the finalizer in an own Thread, so it is essential to be able to register all calling threads on the library entry points. Only with this, the D GC can savely do its job.
> 
> Hopefully both runtime libs will have support for multithreaded libraries in near future :)

August 19, 2007
Sorry, a small correction, both the D and the Java garbage collectors use the SIGUSR1 and SIGUSR2 Unix signals to suspend and resume the process' threads while the garbage collector is running.

Juan Jose Comellas wrote:

> You might have a problem if you have the D runtime and the JVM running within the same process on Linux/Unix. Both the D and the Java garbage collector (at least on JDK 1.4) use the SIGUSR1 and SIGUSR2 signals to suspend and resume the garbage collection process, so you might want to check whether there is any problem if a set of threads created in one section of the process (e.g. D runtime) get a signal to start the garbage collection from the other part of the process (e.g. JVM). I'm not sure if the D runtime is prepared to handle this situation.
> 
> I had a problem with this when embedding the JVM in a program written in C that already used these signals for its own purposes.
> 
> 
> Frank Benoit wrote:
> 
>> I am working on a framework to automate the process of binding Java and D. See the dsource project TioLink.
>> 
>> The main problem i see currently is, that both available D runtime libraries - Phobos and Tango - do not have support for registering Threads that are created "outside".
>> 
>> If a dll (or .so) is called from Java, it is called always with several threads. Even in a single threaded Java application. Java runs the finalizer in an own Thread, so it is essential to be able to register all calling threads on the library entry points. Only with this, the D GC can savely do its job.
>> 
>> Hopefully both runtime libs will have support for multithreaded libraries in near future :)

August 19, 2007
Is this a show stopper for Java/D bridges on linux?

Strange is, that there are only these two signals available for application use.

Well you said 'same process'. I don't know, do you think it would be possible spawning a new process and use IPC? Sounds very expensive.
August 19, 2007
I don't know if it's a showstopper with D because I really haven't tried it, but it was a showstopper for me. There is a way to remap the signals used by the JVM but it was very cumbersome and it still presented problems because the signals generated by the JVM kept interrupting system calls and I had to patch a lot of code that did not belong to me to make everything work (I was embedding the JVM in an Asterisk module).

I finally moved the JVM out of the process and communicated with it via a socket. This change solved all my problems and made the system much easier to debug. It did introduce more latency but it was acceptable for what I needed to do. Besides avoiding the difficult task of analyzing crashes where the Java code was part of the call stack, I also could forget about dealing with all the JVM/JNI quirks (e.g. if you load the same dynamic library from your C code and from the Java code at the same time all hell breaks loose, etc.)

To sum it up, I'd really think twice before embedding the JVM inside another process again.


Frank Benoit wrote:

> Is this a show stopper for Java/D bridges on linux?
> 
> Strange is, that there are only these two signals available for application use.
> 
> Well you said 'same process'. I don't know, do you think it would be possible spawning a new process and use IPC? Sounds very expensive.