Old famous problem of killing a thread
Hi everyone.
I have an AbstractAgent object which implements Runnable.
The run() method just calls the abstract method iterate() which is implemented by the child Agent.
The AbstractAgent allows the child Agent access to a private mailbox through a final blocking method getMessage().
I want to kill this thread by sending it a killMessage when it is waiting on a mailbox.
My scheme is when getting a killMessage, throw an exception which will be caught inside the run() method.
The thing is that althugh the exception is created and thrown inside getMessage() and caught inside run(), which are both implemented at the base class, its route is through the child class, which I want for security reasons to be unable to catch or handle the exception. The child class must allow the exception to propagate gracefully to my handler.
Is it possible to implement this? Prevent the child class to handle exceptions that are created and caught inside the base class?
Thanks
Guy
Re: Old famous problem of killing a thread
Can you post a minimal code example showing what you mean? I'm not sure what the problem is, after all, the child class is the base class, extended.
If you want truly to understand something, try to change it...
K. Lewin
Re: Old famous problem of killing a thread
Here's the code:
Code:
abstract class AbstractAgent implements Runnable{
private MailBox m_mailbox;
protected final AbstractMessage getMessage(){
AbstractMessage message = this.m_mailbox.getMessage();
if (message.getType == KILL_MESSAGE){
throw new KillMessageException;
}
else{
return message;
}
}
protected abstract void iterate() throws KillMessageException;
public void run (){
try{
while(true){
this.iterate();
}
}
catch (KillMessageException){
return;
}
}
}
I only design the infrastructure for whoever implements the AbstractAgent. But regardless to the implementation, it is must that upon receiving a KillMessage, the simulation (It is a simulator for DISCOP algorithms) will stop. I want the implementer that writes iterate() and any other method that uses getMessage() wont be able to catch this exception and handle it by himself.
Can I do that?
Guy
Re: Old famous problem of killing a thread
Quote:
Originally Posted by
guyafe
I want the implementer that writes iterate() and any other method that uses getMessage() wont be able to catch this exception and handle it by himself.
Can I do that?
No, I don't think so. If the subclass calls a method that throws an exception, it can catch that exception.
Why don't you do it the traditional way with a private 'finished' boolean member variable in the superclass that you set true from getMessage and check in the while loop in the run method?
Code:
abstract class AbstractAgent implements Runnable {
private MailBox m_mailbox;
private boolean finished = false;
protected final AbstractMessage getMessage() {
AbstractMessage message = this.m_mailbox.getMessage();
if (message.getType == KILL_MESSAGE) {
finished = true;
}
else {
return message;
}
}
protected abstract void iterate();
public void run () {
while(!finished) {
this.iterate();
}
}
}
The unavoidable price of reliability is simplicity...
C.A.R. Hoare
Re: Old famous problem of killing a thread
Quote:
Originally Posted by
dlorde
Why don't you do it the traditional way with a private 'finished' boolean member variable in the superclass that you set true from getMessage and check in the while loop in the run method?
That's because getMessage() must return something or throw an exception.
Since I have no information at all about what's going on inside iterate(), For example (It is one of the possible scenarios) an Agent that receives a null message or no message at all may either crush or continue waiting for message forever. (Agents don't always know that the simulation is finished and must be shut down externally)
So AbstractAgent is the only participator who can shut down the thread when receiving a KillMessage
Guy
Re: Old famous problem of killing a thread
OK, I see now. I can't see how you can satisfy your requirements unless you take control over getting the message so you can handle the exception before it gets to their method implementation. This means you will have to pass the message to the 'iterate':
Code:
abstract class AbstractAgent implements Runnable {
// wrap the iterate method so we can ensure the caller only gets valid messages
private void innerIterate() throws KillMessageException {
AbstractMessage message = getMessage();
iterate(message);
}
protected abstract void iterate(AbstractMessage message);
public void run() {
try {
while (true) {
this.innerIterate();
}
}
catch (KillMessageException e) {
return;
}
}
}
The important thing in science is not so much to obtain new facts as to discover new ways of think about them...
W. Bragg
Re: Old famous problem of killing a thread
The thing is that I don't want to limit iterate(). It may ask for several messages at a time, or not ask at all at a single iteration. This is why I had iterate() to receive no arguments.
I thought of having KillMessageException a private inner class inside AbstractMessage so the Agent wont know it, and obviously wont be able to catch it.
Still this solution doesn't seem to be working, and I don't know why.
I guess I'll have to rely on the kindness of Agent not to catch this exception.
Guy
Re: Old famous problem of killing a thread
Quote:
Originally Posted by
guyafe
I thought of having KillMessageException a private inner class inside AbstractMessage so the Agent wont know it, and obviously wont be able to catch it.
Still this solution doesn't seem to be working, and I don't know why.
But how could it work? if AbstractAgent.iterate() must throw it, it can't be private to AbstractMessage - the AbstractAgent must be able to declare that it throws it and must also be able to catch it.
Quote:
I guess I'll have to rely on the kindness of Agent not to catch this exception.
Yes, I can't think of another way to do it with this design. However, you could have a callback design, where the user's processing is split into two methods. So the subclass overrides iterate() to get and process the messages it needs by calling superclass method processMessages(int n). The processMessages method calls getMessage the required number of times, and then calls the abstract method processMessageList, passing the message list. If getMessage throws an exception while processMessages is trying to fill the list, processMessages catches it, flags the thread to finish, and optionally throws a NoMoreMessages exception for the subclass to catch (so it knows to clean up). This way, the superclass exception handling isn't seen by the subclass, except for the optional NoMoreMessages exception which is just informative and can be omitted completely.
Code:
abstract class AbstractAgent implements Runnable {
private MailBox m_mailbox;
private boolean finished;
protected final AbstractMessage getMessage() throws KillMessageException {
AbstractMessage message = this.m_mailbox.getMessage();
if (message.getType() == MessageType.KILL_MESSAGE) {
throw new KillMessageException();
}
else {
return message;
}
}
// called by the subclass from iterate()
protected void processMessages(int numberOfMessages) throws NoMoreMessagesException {
List<AbstractMessage> messageList = new ArrayList<AbstractMessage>();
try {
for (int i = 0; i < numberOfMessages; i++) {
messageList.add(getMessage());
}
processMessageList(messageList);
}
catch (KillMessageException e) {
finished = true;
throw new NoMoreMessagesException();
}
}
protected abstract void iterate();
// overridden in subclass to process the list of messages
protected abstract void processMessageList(List<AbstractMessage> messageList);
public void run() {
while (!finished) {
this.iterate();
}
}
}
class Agent extends AbstractAgent {
@Override
protected void iterate() {
try {
processMessages(3); // ask for 3 messages to process
}
catch (NoMoreMessagesException e) {
e.printStackTrace();
}
}
@Override // called from superclass with messages to process
protected void processMessageList(List<AbstractMessage> messageList) {
// process the message list
for (AbstractMessage message : messageList) {
// do something.
}
}
}
That's about as close as I can get to your requirement - it still isn't quite as flexible as a single method, and the subclass need to co-ordinate the iterate and the processMessages methods to work together.
I, myself, have had many failures and I've learned that if you are not failing a lot, you are probably not being as creative as you could be -you aren't stretching your imagination...
J. Backus
Re: Old famous problem of killing a thread
Thanks a lot for all your patience!
Having this kind of handling as you posted, Still requires me to rely on the Agent's kindness, So I believe I will just throw the exception and hopefully, it will be caught in the right place.
Re: Old famous problem of killing a thread
Quote:
Originally Posted by
guyafe
Having this kind of handling as you posted, Still requires me to rely on the Agent's kindness, ...
Not quite sure what you mean by 'Agent's kindness' - it does satisfy the original requirement that the Agent subclass can't catch the KillMessageException - it just means that the Agent must request all the required messages at once - but I admit it is a bit limiting...
Quote:
So I believe I will just throw the exception and hopefully, it will be caught in the right place.
OK, no problem - it was an interesting problem to play around with ;)
Problems worthy
of attack
prove their worth
by hitting back...
P.Hein