/*
* Licensed 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.
*
* Contributions from 2013-2017 where performed either by US government
* employees, or under US Veterans Health Administration contracts.
*
* US Veterans Health Administration contributions by government employees
* are work of the U.S. Government and are not subject to copyright
* protection in the United States. Portions contributed by government
* employees are USGovWork (17USC ยง105). Not subject to copyright.
*
* Contribution by contractors to the US Veterans Health Administration
* during this period are contractually contributed under the
* Apache License, Version 2.0.
*
* See: https://www.usa.gov/government-works
*
* Contributions prior to 2013:
*
* Copyright (C) International Health Terminology Standards Development Organisation.
* Licensed under the Apache License, Version 2.0.
*
*/
package sh.isaac.api.task;
//~--- JDK imports ------------------------------------------------------------
import java.lang.invoke.MethodHandle;
import java.lang.reflect.Method;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
//~--- non-JDK imports --------------------------------------------------------
import javafx.concurrent.Task;
//~--- JDK imports ------------------------------------------------------------
import static java.lang.invoke.MethodHandles.publicLookup;
//~--- non-JDK imports --------------------------------------------------------
import sh.isaac.api.ProgressTracker;
import sh.isaac.api.ticker.Ticker;
import sh.isaac.api.util.FortifyFun;
//~--- classes ----------------------------------------------------------------
/**
* The Class TimedTaskWithProgressTracker.
*
* @author kec
* @param <T> Type that the completed task returns.
*/
public abstract class TimedTaskWithProgressTracker<T>
extends TimedTask<T>
implements ProgressTracker {
/** The Constant MH_SET_TOTAL_WORK. */
static final MethodHandle MH_SET_TOTAL_WORK;
/** The Constant MH_SET_PROGRESS. */
static final MethodHandle MH_SET_PROGRESS;
/** The Constant MH_SET_WORK_DONE. */
static final MethodHandle MH_SET_WORK_DONE;
//~--- static initializers -------------------------------------------------
static {
try {
final Method setTotalWork = Task.class.getDeclaredMethod("setTotalWork", double.class);
FortifyFun.fixAccessible(setTotalWork); // setTotalWork.setAccessible(true);
MH_SET_TOTAL_WORK = publicLookup().unreflect(setTotalWork);
final Method setProgress = Task.class.getDeclaredMethod("setProgress", double.class);
FortifyFun.fixAccessible(setProgress); // setProgress.setAccessible(true);
MH_SET_PROGRESS = publicLookup().unreflect(setProgress);
final Method setWorkDone = Task.class.getDeclaredMethod("setWorkDone", double.class);
FortifyFun.fixAccessible(setWorkDone); // setWorkDone.setAccessible(true);
MH_SET_WORK_DONE = publicLookup().unreflect(setWorkDone);
} catch (IllegalAccessException | NoSuchMethodException | SecurityException ex) {
throw new RuntimeException(ex);
}
}
//~--- fields --------------------------------------------------------------
/** The progress ticker. */
private final Ticker progressTicker = new Ticker();
/** The completed units of work. */
LongAdder completedUnitsOfWork = new LongAdder();
/** The total work. */
AtomicLong totalWork = new AtomicLong();
/** The last total work. */
AtomicLong lastTotalWork = new AtomicLong();
//~--- methods -------------------------------------------------------------
/**
* Adds the to total work.
*
* @param amountOfWork the amount of work
*/
@Override
public void addToTotalWork(long amountOfWork) {
this.totalWork.addAndGet(amountOfWork);
}
/**
* Completed unit of work.
*/
@Override
public void completedUnitOfWork() {
this.completedUnitsOfWork.increment();
}
/**
* Completed units of work.
*
* @param unitsCompleted the units completed
*/
@Override
public void completedUnitsOfWork(long unitsCompleted) {
this.completedUnitsOfWork.add(unitsCompleted);
}
/**
* Done.
*/
@Override
protected void done() {
super.done();
this.progressTicker.stop();
}
/**
* Running.
*/
@Override
protected void running() {
super.running();
final long currentTotalWork = this.totalWork.get();
this.progressTicker.start(progressUpdateIntervalInSecs,
(value) -> {
try {
if (currentTotalWork > 0) {
MH_SET_WORK_DONE.invoke(this, this.completedUnitsOfWork.doubleValue());
MH_SET_PROGRESS.invoke(this,
this.completedUnitsOfWork.doubleValue() / this.totalWork.doubleValue());
MH_SET_TOTAL_WORK.invoke(this, this.totalWork.doubleValue());
} else {
MH_SET_WORK_DONE.invoke(this, -1d);
MH_SET_PROGRESS.invoke(this, -1d);
MH_SET_TOTAL_WORK.invoke(this, -1d);
}
} catch (final Throwable throwable) {
throw new RuntimeException(throwable);
}
});
}
/**
* Will throw an UnsupportedOperationException("call completedUnitOfWork and addToTotalWork instead. ");
* Use {@code completedUnitOfWork()} and {@code addToTotalWork(long amountOfWork)} to update progress.
* @param workDone not used
* @param max not used
*/
@Override
protected void updateProgress(double workDone, double max) {
throw new UnsupportedOperationException("call completedUnitOfWork() and addToTotalWork instead. ");
}
/**
* Will throw an UnsupportedOperationException("call completedUnitOfWork and addToTotalWork instead. ");
* Use {@code completedUnitOfWork()} and {@code addToTotalWork(long amountOfWork)} to update progress.
* @param workDone not used
* @param max not used
*/
@Override
protected void updateProgress(long workDone, long max) {
throw new UnsupportedOperationException("call completedUnitOfWork() and addToTotalWork instead. ");
}
}