I try to implement a client-server software in Java RMI to solve the dining philosophers problem (https://en.wikipedia.org/wiki/Dining_philosophers_problem) as part of my studies.
The network has to be able to recognize clients that accidentally disconnected (f. e. crashed) and recover its state. Currently my server application keeps track of all clients in the network (registering new clients, resetting the other clients to distribute the chairs) but not their states.
Therefore I want to use a proactive system where as soon as one of the clients can't connect to another client, it will use a RMI function on the server to remove the client (first deleting it from the ArrayList on the server, afterwards removing it completely by resetting all clients):
@Override
public synchronized boolean deleteClient(String IP) throws RemoteException, ServerNotActiveException {
if(clients.contains(IP)) {
log.log(Level.FINE, "Remove inactive client {0} from ArrayList...", IP);
clients.remove(IP);
return true;
}
return false;
}
@Override
public void removeClient(String IP) throws RemoteException, ServerNotActiveException {
log.log(Level.FINE, "Removing inactive client {0} from the system...", IP);
resetClientsForRemoval();
}
catch (ConnectException e) {
log.log(Level.SEVERE, "Client seems to be down\n", e);
if (server.deleteClient(IP)) {
new Thread(new Runnable() {
public void run() {
try {
server.removeClient(IP);
} catch (RemoteException e) {
log.log(Level.SEVERE, "Error during removal.\n", e);
} catch (ServerNotActiveException e) {
log.log(Level.SEVERE, "Error during removal.\n", e);
}
}
}).start();
}
}
This means I first check if I can delete the client from the ArrayList (or did someone else already remove it) and afterwards execute the final removal of the client by resetting all clients in the network.
But now I have a problem. The code snippet for Client.java is used by several functions that Threads from a class Philosopher.java call (f. e. during the acquisition of a fork or chair on a remote client). To reset all clients I first stop all threads in Client.java and join them:
public void terminate() {
running = false;
}
@Override
public void stopClient() throws RemoteException, ServerNotActiveException {
for (int i = 0; i < threads.size(); i++) {
log.log( Level.FINE, "{0} temporarily leaving the monastery.", philosophers.get(i).toString());
try {
philosophers.get(i).terminate();
threads.get(i).join();
} catch (InterruptedException e) {
log.log(Level.SEVERE, "Couldn't join thread!\n{0}", e);
}
}
log.log( Level.FINE, "Client stopped.");
}
But it seems like I can't join the thread that is calling the resetClientsForRemoval in the inner Thread class.
Is there a different way to call the removal functions to completely separate them from the calling thread? Else it would be a cyclic lock because I try to join a thread by calling the join function inside a function that this thread is executing.
Thanks in advance for all your help!
Cheers,
Stefan
@Override
public void resetClient(int numberOfChairs, String[] neighbors) throws RemoteException, ServerNotActiveException {
chairs = new Semaphore [numberOfChairs];
for (int i = 0; i < chairs.length; i++) {
chairs[i] = new Semaphore(1);
}
//generate local forks
forks = new Semaphore [numberOfChairs];
for (int i = 0; i < forks.length; i++) {
forks[i] = new Semaphore(1);
}
this.neighbors = neighbors;
threads = new ArrayList<Thread>();
for (int i = 0; i < philosophers.size(); i++)
threads.add(new Thread(philosophers.get(philosophers.size() - 1)));
int i = 0;
for (Thread t : threads) {
log.log(Level.FINE,"{0} entering the updated monastery.", philosophers.get(i).toString());
t.start();
i++;
}
}
My method to call removeClient() in a separate Thread was actually correct, but the way I stopped the Threads wasn't. Simply setting the boolean for running (while(running)) to false wasn't enough to stop every Thread in this scenario (I honestly don't know why). I had to call thread.interrupt() before calling join. Now the code works as intended => a Thread started in another Thread doesn't block the successful execution of join in the "parent" Thread.