public void run() { while (stillAlive.get()) { synchronized (this) { try { ScheduledJob job = schedule.peek(); if (job == null) { // If null, wake up every minute or so to see if there's something to do. // Most likely there will not be. try { this.wait(TIMEOUT_MS); } catch (InterruptedException e) { // interruption should occur when items are added or removed from the queue. } } else { // We've passed the job execution time, so we will run. if (!job.getScheduledExecution().isAfterNow()) { // Run job. The invocation of jobs should be quick. ScheduledJob runningJob = schedule.poll(); logger.info("Scheduler attempting to run " + runningJob.getId()); // Execute the job here try { executionManager.execute(runningJob.getId(), runningJob.isDependencyIgnored()); } catch (JobExecutionException e) { logger.info("Could not run job. " + e.getMessage()); } // Immediately reschedule if it's possible. Let the execution manager // handle any duplicate runs. if (runningJob.updateTime()) { schedule.add(runningJob); saveSchedule(); } else { // No need to keep it in the schedule. removeScheduledJob(runningJob); } } else { // wait until job run long millisWait = Math.max( 0, job.getScheduledExecution().getMillis() - (new DateTime()).getMillis()); try { this.wait(Math.min(millisWait, TIMEOUT_MS)); } catch (InterruptedException e) { // interruption should occur when items are added or removed from the queue. } } } } catch (Exception e) { logger.error("Unexpected exception has been thrown in scheduler", e); } catch (Throwable e) { logger.error("Unexpected throwable has been thrown in scheduler", e); } } } }
private void handleMeasurement() { if (!userConsented()) { Logger.i("Skipping measurement - User has not consented"); return; } try { MeasurementTask task = taskQueue.peek(); // Process the head of the queue. if (task != null && task.timeFromExecution() <= 0) { taskQueue.poll(); Future<MeasurementResult> future; Logger.i("Processing task " + task.toString()); // Run the head task using the executor if (task.getDescription().priority == MeasurementTask.USER_PRIORITY) { sendStringMsg("Scheduling user task:\n" + task); // User task can override the power policy. So a different task wrapper is used. future = measurementExecutor.submit(new UserMeasurementTask(task)); } else { sendStringMsg("Scheduling task:\n" + task); future = measurementExecutor.submit(new PowerAwareTask(task, powerManager, this)); } synchronized (pendingTasks) { pendingTasks.put(task, future); } MeasurementDesc desc = task.getDescription(); long newStartTime = desc.startTime.getTime() + (long) desc.intervalSec * 1000; // Add a clone of the task if it's still valid. if (newStartTime < desc.endTime.getTime() && (desc.count == MeasurementTask.INFINITE_COUNT || desc.count > 1)) { MeasurementTask newTask = task.clone(); if (desc.count != MeasurementTask.INFINITE_COUNT) { newTask.getDescription().count--; } newTask.getDescription().startTime.setTime(newStartTime); submitTask(newTask); } } // Schedule the next measurement in the taskQueue task = taskQueue.peek(); if (task != null) { long timeFromExecution = Math.max(task.timeFromExecution(), Config.MIN_TIME_BETWEEN_MEASUREMENT_ALARM_MSEC); measurementIntentSender = PendingIntent.getBroadcast( this, 0, new UpdateIntent("", UpdateIntent.MEASUREMENT_ACTION), PendingIntent.FLAG_CANCEL_CURRENT); alarmManager.set( AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + timeFromExecution, measurementIntentSender); } } catch (IllegalArgumentException e) { // Task creation in clone can create this exception Logger.e("Exception when cloning task"); sendStringMsg("Exception when cloning task: " + e); } catch (Exception e) { // We don't want any unexpected exception to crash the process Logger.e("Exception when handling measurements", e); sendStringMsg("Exception running task: " + e); } persistState(); }