| Matt Humphrey wrote:
| > | > | (e-mail address removed) wrote:
<snip prelude>
|
| Thanks for the reply, I'll look into those functions. I'll try and
describe
| the issue I'm working on (please bear with me, it's a little complicated
|
| I've written a java/JNI wrapper for an existing MFC program. Each MFC
| function that I want to be available to java has a corresponding JNI
function.
|
| On initialisation, the java code calls a JNI method which creates a
CWinThread
| and starts the MFC application within it - pretty standard stuff. Since
the
| MFC application is running in a separate thread, each JNI function uses a
| PostMessage() call to tell the MFC thread to call the appropriate
function.
| The JNI function are synchronous, i.e. the JNI function won't return until
the
| MFC program has responded to the message posted and responded
appropriately.
| This is necessary as the MFC code is not re-entrant.
|
| This all works fine.
|
| As a result of a JNI call, the MFC application may make a callback into
the
| java code to notify it of certain events (using GetMethodID,
CallVoidMethod
| and friends). Again this all works fine.
|
| The problem comes when a callback needs to make a JNI call itself. Since
the
| callback occurs in the thread of the MFC application, the original
synchronous
| JNI call (the one that triggered the callback in the first place) has not
| returned, so trying to make another JNI call will cause a deadlock :-(
Ok, I get all that. I'm not familiar with JNI to MFC, so let me just
confirm what I think you're saying. Are you saying that a 2nd JNI call made
from the MFC callback thread will block, but that a 2nd call made from a new
thread will not? I think you mean something slightly different, as I would
expect the 2nd call to block even when made from a separate thread. I can't
quite follow your UML ascii art, but further below I think you say that the
callback does not need to wait for the 2nd JNI call to complete. If that's
so, I think what you mean is that the 2nd call on a different thread will
block but eventually be answered asynchronously after the callback and
original JNI request have completed.
If that's the case, I think all you need to do is to create a
single-threaded executor service in your Java wrapper. You only need one of
these--don't create one each time you make a request and hang onto it for
you application.
ExecutorService executor = Executors.newSingleThreadExecutor ();
Then have your callback fire off the request
executor.submit (new Runnable () {
public void run () {
// Do your stuff here.
}
});
(To simply fire off the request in a separate thread, you can just do)
Thread t = new Thread (new Runnable () {
public void run () {
// Do your stuff here
}});
t.start ();
You don't need to wait for the executor to finish. It will simply queue up
your request and as the MFC thread becomes available each will be processed
in turn. You can check out the API to see how to manage shutdown, etc. (A
thread on its own is not guaranteed to execute in order--you may get a 2nd
request that executes before the first one.)
If the 2nd JNI call must be synchronous, I think you're SOL. I don't think
it will matter which thread it comes from, it will just block and you'll get
deadlock. I don't know how the MFC access system is designed to operate in
that regard.
| That's pretty much the crux of the problem, which made we wonder if the
| callback function can schedule a function call for when the original JNI
call
| has finished; that way it will be executed after func() returns.
|
| Another approach might be to avoid running the MFC application in its own
| thread, and instead periodically call a dummy JNI message pump from the
java
| code. I don't know how responsive that would be, though, not to mention
that
| making repeated JNI calls could be expensive.
I have systems that use hundreds of interlocked, signalling threads and I
don't have a problem with performance on that aspect. The more you can make
all requests asynchronous, the easier it becomes. For synchronous requests
you need only figure out how to respond to request completion and where to
put results. You can use the executor above to receive all requests and let
each one handle its final update itself. You'll have to decide what's
appropriate for the data structures you're trying to manage.
Cheers,
Matt Humphrey
http://www.iviz.com/