/** * The method being executes when the status of the workContainer is RUNNABLE. There is no check * whether or not the status is runnable before executing this method, so checking if the status * is actually RUNNABLE is a good check. */ @Override public void run() { // If it isn't runnable, it should't run. (Never happened besides from aborted jobs). if (status.getStatus() != Status.RUNNABLE) { // If it was just aborted, then we just quit, say nothing about it. if (status.getStatus() != Status.ABORTED) { // Hasn't happened yet. If it does, then that is very very bad! throw new RuntimeException( "The workContainer was told to work, although its status is currently " + status); } return; } resetSubJobId(); subJobsCommitted = false; // Running the thing is easy, it should be. // work() and join() gets treated completely the same. Only difference being if earlier jobs are // passed on. E result; if (results == null) { result = job.work(scheduler); } else { result = job.join(results, scheduler); } if (!subJobsCommitted) { // We just need to submit an result. this.result = result; // I'm only submitting the result, if i can change the status to HAS_RESULT. And it is // synchronized because i changes the state of the object. synchronized (this) { if (status.setStatus(Status.HAS_RESULT)) { env.submitResult(this); } } } else { subJobLock.lock(); try { // This is if all the results have already gotten back, since that happens multithreaded, // that can happen. if (missingJobs.size() == 0) { runAgain(); // Not running it again right now, just putting it in the queue. } else { status.setStatus(Status.NEED_RESULTS); // Waiting. } } finally { subJobLock.unlock(); } } }
/** * Submits a result that this workcontainer has been waiting for. This method is thread safe. * * @param id of the job. (The ID thats only unique inside this workcontainer). * @param result */ public void putResult(int id, E result) { subJobLock.lock(); try { // Making the list if it isn't there. if (results == null) { results = new ArrayList<E>(); } // Expanding it if we need more room. while (id >= results.size()) { results.add(null); } Integer key = new Integer(id); if (missingJobs.containsKey(key)) { missingJobs.remove(key); results.set(id, result); // Everything that is a state change of this object, is synchronized to the object. synchronized (this) { if (missingJobs.size() == 0 && status.getStatus() == Status.NEED_RESULTS) { runAgain(); } } } } finally { subJobLock.unlock(); } }
/** * This method is called when all subJobs have been completed, and we need to put this job back in * the jobQueue. */ private void runAgain() { // Only putting it in, if i'm able to change the status to RUNNABLE. synchronized (this) { if (status.setStatus(Status.RUNNABLE)) { try { env.putJobInQueue(this); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } else { System.out.println("Not running again, i couldn't set the status to Runnable"); } // In any case, removing it from the idle jobs. env.removeIdleJob(this.getId()); } }
/** * Hold the result when it is ready, if there is no result ready, it will throw an * RuntimeException. * * @return the result calculated by the job this WorkContainer contains. */ public E getResult() { if (status.getStatus() != Status.HAS_RESULT) System.out.println("Status is " + status + ", but yet someone accessed the result. "); return result; }
/** * Returns the current status of this job, if it is RUNNABLE, NEED_RESULTS or HAS_RESULTS. * * @return current status of this job. */ public Status getStatus() { return status.getStatus(); }