/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.sun.jini.mahalo; import com.sun.jini.mahalo.*; import com.sun.jini.thread.*; import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; /** * A <code>Job</code> manages the division of work for a problem * whose solution is obtained by assembling partial results to * original problem. * * @author Sun Microsystems, Inc. * */ public abstract class Job { private TaskManager pool; private WakeupManager wm; private int pending = -1; Object[] results; int[] attempts; private Map tasks = new HashMap(); //used to maintain account //of the tasks for which //the job is responsible static final Logger logger = TxnManagerImpl.participantLogger; /** * Create the <code>Job</code> object giving it the * <code>TaskManager</code> responsible for the pool of * threads which perform the necessary work. * * @param pool the <code>TaskManager</code> which provides the threads */ public Job(TaskManager pool, WakeupManager wm) { this.wm = wm; this.pool = pool; } /** * Used by a task to do a piece of work and record the * number of attempts. * * @param who The task which is performing the work * @param param A parameter used in performing the work */ boolean performWork(TaskManager.Task who, Object param) throws JobException { Integer tmp = null; synchronized (tasks) { tmp = (Integer)tasks.get(who); } if (tmp == null) throw new UnknownTaskException(); int rank = tmp.intValue(); synchronized (attempts) { attempts[rank]++; } Object result = doWork(who, param); if (result == null) return false; try { reportDone(who, result); } catch (UnknownTaskException e) { } catch (PartialResultException e) { } catch (JobException e) { } return true; } /** * Given a <code>TaskManager.Task</code>, this method * returns the current number of attempts it has made. * * @param who The task for which the number of attempts * is inquired */ int attempt(TaskManager.Task who) throws JobException { Integer tmp = null; synchronized(tasks) { tmp = (Integer)tasks.get(who); } if (tmp == null) throw new UnknownTaskException(); int rank = tmp.intValue(); synchronized(attempts) { return attempts[rank]; } } /** * The work performed is implemented here. * A null return value indicates failure * while a non-null return value indicates * success and contains the result. * * @param who The task performing the work * @param param A parameter used to do the work * */ abstract Object doWork(TaskManager.Task who, Object param) throws JobException; /** * Create the tasks required to compute all of the * <code>PartialResult</code> objects necessary for the * solution to the original problem. */ abstract TaskManager.Task[] createTasks(); /** * Schedules tasks for execution */ public void scheduleTasks() { TaskManager.Task[] tmp = createTasks(); if (tmp != null) { if (logger.isLoggable(Level.FINEST)) { logger.log(Level.FINEST, "Job:scheduleTasks with {0} tasks", new Integer(tmp.length)); } results = new Object[tmp.length]; attempts = new int[tmp.length]; setPending(tmp.length); for (int i = 0; i < tmp.length; i++) { //Record the position if each //task for later use when assembling //the partial results synchronized(tasks) { tasks.put(tmp[i],new Integer(i)); pool.add(tmp[i]); if (logger.isLoggable(Level.FINEST)) { logger.log(Level.FINEST, "Job:scheduleTasks added {0} to thread pool", tmp[i]); } attempts[i] = 0; } } } } private synchronized void awaitPending(long waitFor) { if (pending < 0) return; if (pending == 0) return; try { if (logger.isLoggable(Level.FINEST)) { logger.log(Level.FINEST, "Job:awaitPending waiting for {0} items", new Integer(pending)); } if (waitFor == Long.MAX_VALUE) { while (pending > 0) { wait(); if (logger.isLoggable(Level.FINEST)) { logger.log(Level.FINEST, "Job:awaitPending awoken"); } } } else { //When waiting for a given amount of time, //if notified, make sure that the desired //wait time has actually transpired. long start = System.currentTimeMillis(); long curr = start; while ((pending > 0) && ((curr - start) < waitFor)) { wait(waitFor - (curr - start)); curr = System.currentTimeMillis(); } } } catch (InterruptedException ie) { } } private synchronized void setPending(int num) { pending = num; if (pending <= 0) { if (logger.isLoggable(Level.FINEST)) { logger.log(Level.FINEST, "Job:setPending notifying, pending = {0}", new Integer(pending)); } notifyAll(); } } private synchronized void decrementPending() { pending--; if (pending <= 0) { if (logger.isLoggable(Level.FINEST)) { logger.log(Level.FINEST, "Job:decrementPending notifying, pending = {0}", new Integer(pending)); } notifyAll(); } } /** * Returns a reference to the <code>TaskManager</code> which * supplies the threads used to executed tasks created by * this <code>Job</code> */ protected TaskManager getPool() { return pool; } /** * Returns a reference to the <code>WakeupManager</code> which * provides the scheduling of tasks created by * this <code>Job</code> */ protected WakeupManager getMgr() { return wm; } /* * Tasks which perform work on behalf of the <code>Job</code> * report in that they are done using this method. */ private void reportDone(TaskManager.Task who, Object param) throws JobException { if (param == null) throw new NullPointerException("param must be non-null"); if (who == null) throw new NullPointerException("task must be non-null"); Integer position = null; synchronized(tasks) { position = (Integer) tasks.get(who); } if (position == null) throw new UnknownTaskException(); synchronized(results) { if (results[position.intValue()] == null) { if (logger.isLoggable(Level.FINEST)) { logger.log(Level.FINEST, "Job:reportDone who = {0}, param = {1}", new Object[] { who, param}); } results[position.intValue()] = param; decrementPending(); } else { throw new PartialResultException("result already set"); } } } /** * Check to see if the <code>Job</code> execution has * completed. * * @param waitFor The amount of time the caller is willing * to wait for the completion status to arrive. */ public boolean isCompleted(long waitFor) throws JobException { //If nothing has started, the //task could not have completed. //Less than zero means initial value //and greater than zero means there //are outstanding tasks. In each of //these cases, the Job is not done. awaitPending(waitFor); synchronized(this) { if (pending == 0) return true; if (pending < 0) throw new JobNotStartedException("No jobs started"); return false; } } /** * Generate the solution to the original problem. * The subclass decides how it computes the final * outcome. */ abstract Object computeResult() throws JobException; /** * Halt all of the work being performed by * the <code>Job</code> */ public void stop() { Set s = tasks.keySet(); Object[] vals = s.toArray(); //Remove and interrupt all tasks for (int i = 0; i < vals.length; i++) { TaskManager.Task t = (TaskManager.Task) vals[i]; pool.remove(t); } //Erase record of tasks, results and the //counting mechanism tasks = new HashMap(); setPending(-1); results = null; } }