/** * 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. * * Command-line arguments: total tasks, maximum task time, seed * (optional) "--verbose" for verbose output * * Master process for client/server version using RMI. * * This version does not try to measure total execution time; it's not clear * that would be meaningful. * * RMI specifics in this example are loosely based on an example from * Sun's online tutorial. */ package csci3366.sample.masterworker.rmi; import java.net.MalformedURLException; import java.rmi.Naming; import java.rmi.NotBoundException; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.server.UnicastRemoteObject; import java.util.List; import csci3366.sample.masterworker.FakeTasks; public class Master implements MasterInterface { /* variables */ private int numTasks; private int maxTaskTime; private int seed; private boolean verbose = false; private FakeTasks.SynchTaskQueue taskQueue; private int nextWorker = 0; private int activeWorkers = 0; private int totalTaskTime = 0; private int maxWorkerTotalTaskTime; private int minWorkerTotalTaskTime; /* MasterInterface methods */ /** Register new worker (returns worker ID, or -1 if no more tasks). */ public int registerNewWorker(WorkerInterface w) throws RemoteException { int workerID = addWorker(); if (verbose) { System.out.printf("connection from %s, worker %d\n", w.getHostName(), workerID); } return workerID; } /** Get task from queue (null if none). */ public FakeTasks.Task getTask(WorkerInterface w) throws RemoteException { if (verbose) { System.out.printf("(worker %d) requested task\n", w.getID()); } return taskQueue.get(); } /** Accept result for single task from worker. */ public void reportResult(WorkerInterface w, FakeTasks.Task t, FakeTasks.TaskResult tr) throws RemoteException { if (verbose) { System.out.printf("(worker %d) sent result: %s\n", w.getID(), FakeTasks.toString(t, tr)); } } /** Accept summary result for worker. */ public void reportSummaryResults(WorkerInterface w, int numTasks, int totalTaskTime) throws RemoteException { System.out.printf( "worker %d number of tasks = %d, total task time = %d\n", w.getID(), numTasks, totalTaskTime); updateGlobals(totalTaskTime); removeWorker(); } /* other methods */ /** * Main method. */ public static void main(String[] args) { String usageMessage = "parameters: numTasks maxTaskTime seed [--verbose]"; int numTasks = 0; int maxTaskTime = 0; int seed = 0; boolean verbose = false; /* process command-line arguments */ if (args.length < 3) { System.err.println(usageMessage); System.exit(1); } try { numTasks = Integer.parseInt(args[0]); maxTaskTime = Integer.parseInt(args[1]); seed = Integer.parseInt(args[2]); } catch (NumberFormatException e) { System.err.println(usageMessage); System.exit(1); } if (args.length > 3) { if (args[3].equals("--verbose")) verbose = true; else { System.err.println(usageMessage); System.exit(1); } } /* do processing */ Master m = null; try { m = new Master(numTasks, maxTaskTime, seed, verbose); m.go(); } /* FIXME?: improve error handling? */ catch (Exception e) { System.err.println("error in master:\n\t" + e); } /* * shut down: be sure there are no remaining references to * remote objects, since otherwise RMI keeps process(es) running */ if (m != null) { m.cleanupRMI(); } m = null; } /** Constructor. */ public Master(int numTasks_, int maxTaskTime_, int seed_, boolean verbose_) { numTasks = numTasks_; maxTaskTime = maxTaskTime_; seed = seed_; verbose = verbose_; /* initialize variables */ maxWorkerTotalTaskTime = 0; minWorkerTotalTaskTime = maxTaskTime * numTasks; taskQueue = new FakeTasks.SynchTaskQueue( FakeTasks.randomTasks(numTasks, maxTaskTime, seed)); } /** Perform processing. */ public void go() throws MalformedURLException, NotBoundException, RemoteException { /* create registry (so no need to start separately) */ LocateRegistry.createRegistry(REGISTRY_PORT); /* register self as ready for remote calls */ MasterInterface stub = (MasterInterface) UnicastRemoteObject.exportObject(this, 0); LocateRegistry.getRegistry(REGISTRY_PORT).rebind(BINDING_NAME, stub); if (verbose) { System.out.println("waiting for workers"); } /* wait for all tasks to be assigned */ synchronized (taskQueue) { while (!taskQueue.isEmpty()) { try { taskQueue.wait(); } catch (InterruptedException e) { System.err.println("this should not happen"); e.printStackTrace(); } } } if (verbose) { System.out.println("all tasks assigned"); } /* wait for all workers to report results */ synchronized (this) { while (activeWorkers > 0) { try { wait(); } catch (InterruptedException e) { System.err.println("this should not happen"); e.printStackTrace(); } } } /* print summary information */ System.out.printf("\nRMI client/server version\n"); System.out.printf("number of workers = %d\n", nextWorker); 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); } /** * Clean up all RMI-related resources. * (If references still exist to remote objects, RMI keeps process(es) * running.) */ public void cleanupRMI() { /* FIXME?: improve error handling? */ /* unregister self */ try { UnicastRemoteObject.unexportObject(this, false); } catch (Exception e) { System.err.println("error shutting down master:\n\t" + e); } try { LocateRegistry.getRegistry(REGISTRY_PORT).unbind(BINDING_NAME); } catch (Exception e) { System.err.println("error shutting down master:\n\t" + e); } } /** * Add worker and return ID (thread-safe). */ private synchronized int addWorker() { int workerID = nextWorker++; ++activeWorkers; return workerID; } /** * Update globals when worker finishes (thread-safe). */ private synchronized void updateGlobals(int workerTotalTaskTime) { maxWorkerTotalTaskTime = Math.max(maxWorkerTotalTaskTime, workerTotalTaskTime); minWorkerTotalTaskTime = Math.min(minWorkerTotalTaskTime, workerTotalTaskTime); totalTaskTime += workerTotalTaskTime; } /** * Remove worker (thread-safe). */ private synchronized void removeWorker() { if (--activeWorkers == 0) { notifyAll(); } } }