Skip to main content

Re: Destroying Xlet Contexts

1 reply [Last post]
Anonymous

[ I'm hoping discussion like this will soon move to the
bd-j forum. A link can be found at http://hdcookbook.com .
While folks are transitioning, I'll send replies this way,
so they appear on the forum. ]

> I have a very quick question about xlet contexts, and you're the only one
> I know that might have an answer:
>
> Is an xlet suppose to completely finish all shutdown and clean-up prior
> to returning from a call to its destroyXlet() method?

Yes.

> For instance, if I have a fairly complex shutdown procedure that
> involves signalling various threads to stop, notifying other xlets
> via IXC that I'm stopping, and so on, do I need to make sure all of
> that is done before I return from destroyXlet()?

Yes, you should. Indeed, once you return from destroyXlet(),
it's possible that the implementation will do the equivalent
of a Unix "kill -9" quite quickly.

> The JavaDocs for JSR-927 seem to suggest that an Xlet's context is
> destroyed immediately upon return from destroyXlet(). This, in turn
> would affect things like DVBTest logging, entries on the IxcRegistry,
> etc.

Right.

> If this is the case, then what is the best way to wait for a thread
> to finish shutting down?

I thought you said "quick question" :-)

Two quick answers are "using a latch, or some more general condition
variable" or "using Thread.join()". Assuming the concurrency
management of the xlet is already good, then you'd:

* Notify all threads to terminate using shared state
that is appropriately synchronized

* Poll that shared state in every thread at a suitable interval,
paying attention to things like socket operations.

* Wait for threads to terminate, e.g. using Thread.join()

* If the threads don't terminate in a reasonable amount of
time (e.g. a second or two), take steps to make sure
they're not blocked on an I/O call. A blocked socket can
be un-stuck by calling Socket.close() out from under it.
More on this toward the end of the post.

A really good treatment of concurrency, and concepts like condition
variables and utilities like a latch is in the CPJ book. That's
"Concurrent Programming in Java, Second Edition" by Doug Lea
(ISBN 0-201-31009-0). For serious multithreaded programming,
I consider this to be an essential reference.

Putting this together, here's one bit of code that expresses some
of these ideas. Note that I haven't tried to compile it, so there
are probably a few typos... I also didn't deal with sockets, since
that would complicate the code quite a bit, but see the comments
under Thread.join()...

public class XletThreadManager {

private XletThreadManager theManager;

private Thread[] threads;

private boolean terminating = false;

private XletThreadManager(Thread[] threads) {
this.threads = threads;
}

/**
* Called on xlet creation, e.g. within Xlet.initXlet()
**/
public static setup(Thread[] threads) {
// assert theManager == null
theManager = new XletThreadManager(threads);
}

/**
* Polled by threads on a regular basis. If true, the
* caller should exit return out of the run() method
* soon (usually within a handful of seconds at most)
**/
public static boolean getTerminating() {
// assert theManager != null
synchronized(theManager) {
return theManager.terminating;
}
}

/**
* Called from Xlet.destroyXlet()
**/
public static void terminateThreads() {
// assert theManager != null
synchronized(theManager) {
// assert theManager.terminating == false
theManager.terminating = true;
}
for (int i = 0; i < theManager.threads.length; i++) {
try {
theManager.threads[i].join();
// In a real xlet doing socket I/O, I'd set a timeout
// on the join call, so after (say) two seconds elapsed
// time after terminateThreads() is called, I could take
// more drastic action, like callling Socket.close()
// on all of the xlet's open sockets.

} catch (InterruptedException ex) {

// If we're interrupted, it means the xlet has
// timed out, and the player's xlet manager is
// starting the process of terminating user threads.
// Like all well-behaved code, we should cooperate
// by immediately giving up what we're doing. In
// addition, we help it along by interrupting any other
// threads we're managing (except for ones we know have
// already terminated).

for ( ; i < theManager.threads.length; i++) {
theManager.threads[i].interrupt();
}
return;
}
}
}
}

Every utility thread should poll getTerminating() frequently - once per
frame for an animation loop; for I/O, before and after every significant
read or write.

I did a little research here about what to do when a socket is blocked.
Imagine, for example, that the xlet is waiting on a socket, but the
network connection is having trouble for some reason. A socket can go
a pretty long time before it realizes it's in trouble. The recommended
thing to do if another thread is blocked on a socket read (or write) is
to close the socket by calling Socket.close(). In the javadocs for
this method from JDK 1.4/PBP 1.1 and better, this method correctly
says:

Any thread currently blocked in an I/O operation upon this socket will
throw a SocketException.

Unfortunately, that documentation didn't make it back into PBP 1.0, but
it is the right behavior, even for JDK 1.3/PBP 1.0. Before we fixed it
in the dark, pre-1.3 days, Socket.close() wasn't guaranteed to be
"preemptive," that is, it wouldn't abort pending reads reliably.
This was fixed in the JDK 1.3 timeframe.
See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4344135 .

Note also that Socket.shutdownInput() isn't the right thing (which
was my first thought) - someone in the JDK team thought it was, as shown
in bug http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4370433 , but
the later http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4370523
is quite explicit that close() is the way to go.

HTH,

Bill

---------------------------------------------------------------------
To unsubscribe, e-mail: bd-j-dev-unsubscribe@hdcookbook.dev.java.net
For additional commands, e-mail: bd-j-dev-help@hdcookbook.dev.java.net

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
billf
Offline
Joined: 2004-02-13
Points: 0

(OK, the code above got scrambled and the message got clipped on the
forum, because the e-mail -> forum gateway is interpreting strings that start
with left-bracket as formatting commands. This doesn't work so well with
ASCII-formatted e-mail that includes code... We're seeing if that can be fixed.
In the meantime, here's the previous post, starting from the code that got scrambled,
and including the end of the post that got clipped off due to some kind of formatting
interaction.)

[code]

public class XletThreadManager {

private XletThreadManager theManager;

private Thread[] threads;

private boolean terminating = false;

private XletThreadManager(Thread[] threads) {
this.threads = threads;
}

/**
* Called on xlet creation, e.g. within Xlet.initXlet()
**/
public static setup(Thread[] threads) {
// assert theManager == null
theManager = new XletThreadManager(threads);
}

/**
* Polled by threads on a regular basis. If true, the
* caller should exit return out of the run() method
* soon (usually within a handful of seconds at most)
**/
public static boolean getTerminating() {
// assert theManager != null
synchronized(theManager) {
return theManager.terminating;
}
}

/**
* Called from Xlet.destroyXlet()
**/
public static void terminateThreads() {
// assert theManager != null
synchronized(theManager) {
// assert theManager.terminating == false
theManager.terminating = true;
}
for (int i = 0; i < theManager.threads.length; i++) {
try {
theManager.threads[i].join();
// In a real xlet doing socket I/O, I'd set a timeout
// on the join call, so after (say) two seconds elapsed
// time after terminateThreads() is called, I could take
// more drastic action, like callling Socket.close()
// on all of the xlet's open sockets.

} catch (InterruptedException ex) {

// If we're interrupted, it means the xlet has
// timed out, and the player's xlet manager is
// starting the process of terminating user threads.
// Like all well-behaved code, we should cooperate
// by immediately giving up what we're doing. In
// addition, we help it along by interrupting any other
// threads we're managing (except for ones we know have
// already terminated).

for ( ; i < theManager.threads.length; i++) {
theManager.threads[i].interrupt();
}
return;
}
}
}
}
[/code]

Every utility thread should poll getTerminating() frequently - once per
frame for an animation loop; for I/O, before and after every significant
read or write.

I did a little research here about what to do when a socket is blocked.
Imagine, for example, that the xlet is waiting on a socket, but the
network connection is having trouble for some reason. A socket can go
a pretty long time before it realizes it's in trouble. The recommended
thing to do if another thread is blocked on a socket read (or write) is
to close the socket by calling Socket.close(). In the javadocs for
this method from JDK 1.4/PBP 1.1 and better, this method correctly
says:

Any thread currently blocked in an I/O operation upon this socket will
throw a SocketException.

Unfortunately, that documentation didn't make it back into PBP 1.0, but
it is the right behavior, even for JDK 1.3/PBP 1.0. Before we fixed it
in the dark, pre-1.3 days, Socket.close() wasn't guaranteed to be
"preemptive," that is, it wouldn't abort pending reads reliably.
This was fixed in the JDK 1.3 timeframe.
See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4344135 .

Note also that Socket.shutdownInput() isn't the right thing (which
was my first thought) - someone in the JDK team thought it was, as shown
in bug http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4370433 , but
the later http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4370523
is quite explicit that close() is the way to go.

HTH,

Bill