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; /** * 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. */ abstract public class MasterWorkerSocketsMaster { /* variables to be used by all threads */ protected boolean verbose = false; private TaskQueue taskQueue; private int totalTaskTime = 0; private int maxWorkerTotalTaskTime; private int minWorkerTotalTaskTime; /* methods for use by subclasses */ protected void initialize(int numTasks, int maxTaskTime, boolean _verbose) { verbose = _verbose; maxWorkerTotalTaskTime = 0; minWorkerTotalTaskTime = maxTaskTime * numTasks; } protected void buildTaskQueue(int numTasks, int maxTaskTime) { taskQueue = new TaskQueue(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 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 */ MasterWorkerSocketsWorker.Summary summary = (MasterWorkerSocketsWorker.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; } /** * Class for task queue (thread-safe). */ private static class TaskQueue { private List tasks; public TaskQueue(int numTasks, int maxTaskTime) { tasks = FakeTasks.randomTasks(numTasks, maxTaskTime); } /** Check whether queue is empty. */ public synchronized boolean isEmpty() { return tasks.isEmpty(); } /** Get a task (returns null if queue is empty). */ public synchronized FakeTasks.Task get() { if (tasks.isEmpty()) { notifyAll(); return null; } else { return tasks.remove(0); } } } }