package csci3366sample.masterworker.sockets; import java.io.IOException; import java.io.Serializable; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.List; import csci3366sample.masterworker.FakeTasks; /** * Generic example of master/worker program. * "Tasks" just consist of sleeping for a specified time; times to wait are * generatedly randomly from 1 to a specified maximum. * * Common code for master process for client/server versions using sockets. * * Client/server interaction is as follows: * * (1) Server sends FakeTask.Task object to client. * (2) Client sends back FakeTask.TaskResult. * Steps (1) and (2) repeat while there are more tasks to assign. When * all tasks have been assigned, server sends a ShutdownTask object and * client responds with a Worker.Summary object. */ abstract public class Master { /* variables to be used by all threads */ protected int numTasks; protected int maxTaskTime; protected int port; protected boolean verbose = false; private FakeTasks.SynchTaskQueue taskQueue; private int totalTaskTime = 0; private int maxWorkerTotalTaskTime; private int minWorkerTotalTaskTime; /* methods for use by subclasses */ public Master(int _numTasks, int _maxTaskTime, int _port, boolean _verbose) { numTasks = _numTasks; maxTaskTime = _maxTaskTime; port = _port; verbose = _verbose; } protected void initialize() { maxWorkerTotalTaskTime = 0; minWorkerTotalTaskTime = maxTaskTime * numTasks; } protected void buildTaskQueue(int numTasks, int maxTaskTime) { taskQueue = new FakeTasks.SynchTaskQueue( FakeTasks.randomTasks(numTasks, maxTaskTime)); } protected void waitUntilTaskQueueEmpty() { synchronized (taskQueue) { while (!taskQueue.isEmpty()) { try { taskQueue.wait(); } catch (InterruptedException e) { System.err.println("this should not happen"); e.printStackTrace(); } } } } protected void waitForThreadToComplete(Thread t) { try { t.join(); } catch (InterruptedException e) { System.err.println("this should not happen"); e.printStackTrace(); } } protected void printSummaryInfo(int numTasks, int maxTaskTime) { System.out.printf("number of tasks = %d\n", numTasks); System.out.printf("max task time = %d\n", maxTaskTime); System.out.printf("total task time = %d\n", totalTaskTime); System.out.printf("total task time in workers ranges from %d to %d\n", minWorkerTotalTaskTime, maxWorkerTotalTaskTime); } /* methods for use by threads */ /** * Update global variables at end of worker. */ private synchronized void updateGlobals(int workerTotalTaskTime) { maxWorkerTotalTaskTime = Math.max(maxWorkerTotalTaskTime, workerTotalTaskTime); minWorkerTotalTaskTime = Math.min(minWorkerTotalTaskTime, workerTotalTaskTime); totalTaskTime += workerTotalTaskTime; } /* utility methods */ /** * Get hostname for socket. */ protected static String clientAddress(Socket s) { return s.getInetAddress().getHostName(); } /** * Class for threads to communicate with workers. */ protected class WorkerTalker implements Runnable { private int myID; private Socket socket; public WorkerTalker(int ID, Socket s) { myID = ID; socket = s; } public void run() { ObjectInputStream input = null; ObjectOutputStream output = null; try { output = new ObjectOutputStream(socket.getOutputStream()); input = new ObjectInputStream(socket.getInputStream()); /* send ID to worker process */ output.writeObject(new Integer(myID)); FakeTasks.Task t; /* get tasks from queue until queue is empty */ while ((t = taskQueue.get()) != null) { /* send task to worker process, receive result */ output.writeObject(t); FakeTasks.TaskResult result = (FakeTasks.TaskResult) input.readObject(); if (verbose) { System.out.printf("(worker %d) %s\n", myID, FakeTasks.toString(t, result)); } } /* * send shutdown task to worker * TODO: improve this? * seems like we should be able to just close the output * stream, but that produces "Broken pipe" exceptions in the * workers */ output.writeObject(new ShutdownTask()); /* receive summary */ Worker.Summary summary = (Worker.Summary) input.readObject(); System.out.printf( "worker %d number of tasks = %d, total task time = %d\n", myID, summary.numTasks, summary.totalTaskTime); /* thread-safe update to globals */ updateGlobals(summary.totalTaskTime); /* shut things down */ socket.shutdownOutput(); output.close(); input.close(); socket.close(); } catch (ClassNotFoundException e) { System.err.println("error in client " + myID + ":\n\t" + e); } catch (IOException e) { System.err.println("error in client " + myID + ":\n\t" + e); } } } /** * Class for shutdown task. * (Anything that's serializable and not a FakeTasks.Task would work.) */ public static class ShutdownTask implements Serializable { private static final long serialVersionUID = 1L; } }