Interface JobScheduler
- All Known Implementing Classes:
ExecutorJobScheduler,ImmediateJobScheduler,OperationInitializerJobScheduler,UpdateGraphJobScheduler
The iteration methods come in two forms. iterateParallel(io.deephaven.engine.context.ExecutionContext, io.deephaven.base.log.LogOutputAppendable, java.util.function.Supplier<CONTEXT_TYPE>, int, int, io.deephaven.engine.table.impl.util.JobScheduler.IterateAction<CONTEXT_TYPE>, java.lang.Runnable, java.lang.Runnable, java.util.function.Consumer<java.lang.Exception>) and iterateSerial(io.deephaven.engine.context.ExecutionContext, io.deephaven.base.log.LogOutputAppendable, java.util.function.Supplier<CONTEXT_TYPE>, int, int, io.deephaven.engine.table.impl.util.JobScheduler.IterateResumeAction<CONTEXT_TYPE>, java.lang.Runnable, java.lang.Runnable, java.util.function.Consumer<java.lang.Exception>) report the outcome
through callbacks and may return before the iteration is over, although a scheduler that runs jobs on the submitting
thread can finish it first. invokeParallel(io.deephaven.engine.context.ExecutionContext, io.deephaven.base.log.LogOutputAppendable, java.util.function.Supplier<CONTEXT_TYPE>, int, int, io.deephaven.engine.table.impl.util.JobScheduler.IterateAction<CONTEXT_TYPE>) holds the calling thread, which runs tasks alongside the
scheduler's threads, and returns or throws only once the iteration is over.
An iteration fails when a task throws, when an JobScheduler.IterateResumeAction task reports a failure through its nested
error consumer, when a task context fails to close, or when it cannot be started because the context factory or
submit(io.deephaven.engine.context.ExecutionContext, java.lang.Runnable, io.deephaven.base.log.LogOutputAppendable, java.util.function.Consumer<java.lang.Exception>) throws. No new task starts once it has failed. The callback forms then call onError with the
first failure, and neither onComplete nor cleanup; a failure to start reaches their caller only that
way, unless it is not an Exception, an Error in practice, which is also rethrown.
invokeParallel throws the first failure instead, as it describes. Later failures are suppressed on the first.
-
Nested Class Summary
Nested ClassesModifier and TypeInterfaceDescriptionstatic interfaceHelper interface foriterateParallel(io.deephaven.engine.context.ExecutionContext, io.deephaven.base.log.LogOutputAppendable, java.util.function.Supplier<CONTEXT_TYPE>, int, int, io.deephaven.engine.table.impl.util.JobScheduler.IterateAction<CONTEXT_TYPE>, java.lang.Runnable, java.lang.Runnable, java.util.function.Consumer<java.lang.Exception>)andinvokeParallel(io.deephaven.engine.context.ExecutionContext, io.deephaven.base.log.LogOutputAppendable, java.util.function.Supplier<CONTEXT_TYPE>, int, int, io.deephaven.engine.table.impl.util.JobScheduler.IterateAction<CONTEXT_TYPE>).static interfaceHelper interface foriterateSerial(io.deephaven.engine.context.ExecutionContext, io.deephaven.base.log.LogOutputAppendable, java.util.function.Supplier<CONTEXT_TYPE>, int, int, io.deephaven.engine.table.impl.util.JobScheduler.IterateResumeAction<CONTEXT_TYPE>, java.lang.Runnable, java.lang.Runnable, java.util.function.Consumer<java.lang.Exception>)anditerateParallel(io.deephaven.engine.context.ExecutionContext, io.deephaven.base.log.LogOutputAppendable, java.util.function.Supplier<CONTEXT_TYPE>, int, int, io.deephaven.engine.table.impl.util.JobScheduler.IterateAction<CONTEXT_TYPE>, java.lang.Runnable, java.lang.Runnable, java.util.function.Consumer<java.lang.Exception>).static final classstatic interfaceA default context for the scheduled job actions.static interface -
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final JobScheduler.JobThreadContextstatic final Supplier<JobScheduler.JobThreadContext> static final ExceptionDelivered when a job fails with anErrorand even the wrapper for it cannot be allocated, which is to say when the heap is exhausted — the very failure this path exists for. -
Method Summary
Modifier and TypeMethodDescriptionstatic ExceptionasDeliverableException(@NotNull Throwable throwable) Convert a Throwable that escaped a scheduled job into something theConsumer<Exception>error handlers used throughout the scheduler can accept.<T> TawaitCompletion(@NotNull CompletableFuture<T> future) Wait forfuture, which this scheduler's jobs complete, and return its result asFuture.get()does.The performance statistics of all runnables that have been completed off-thread, or null if all were executed in the current thread.default <CONTEXT_TYPE extends JobScheduler.JobThreadContext>
voidinvokeParallel(@Nullable ExecutionContext executionContext, @Nullable LogOutputAppendable description, @NotNull Supplier<CONTEXT_TYPE> taskThreadContextFactory, int start, int count, @NotNull JobScheduler.IterateAction<CONTEXT_TYPE> action) Iterates over a range of values in parallel asiterateParallel(io.deephaven.engine.context.ExecutionContext, io.deephaven.base.log.LogOutputAppendable, java.util.function.Supplier<CONTEXT_TYPE>, int, int, io.deephaven.engine.table.impl.util.JobScheduler.IterateAction<CONTEXT_TYPE>, java.lang.Runnable, java.lang.Runnable, java.util.function.Consumer<java.lang.Exception>)does, except that the calling thread runs tasks too, and this method returns only once the iteration is over.default <CONTEXT_TYPE extends JobScheduler.JobThreadContext>
voiditerateParallel(@Nullable ExecutionContext executionContext, @Nullable LogOutputAppendable description, @NotNull Supplier<CONTEXT_TYPE> taskThreadContextFactory, int start, int count, @NotNull JobScheduler.IterateAction<CONTEXT_TYPE> action, @NotNull Runnable onComplete, @NotNull Runnable cleanup, @NotNull Consumer<Exception> onError) Provides a mechanism to iterate over a range of values in parallel using theJobSchedulerdefault <CONTEXT_TYPE extends JobScheduler.JobThreadContext>
voiditerateParallel(@Nullable ExecutionContext executionContext, @Nullable LogOutputAppendable description, @NotNull Supplier<CONTEXT_TYPE> taskThreadContextFactory, int start, int count, @NotNull JobScheduler.IterateResumeAction<CONTEXT_TYPE> action, @NotNull Runnable onComplete, @NotNull Runnable cleanup, @NotNull Consumer<Exception> onError) Provides a mechanism to iterate over a range of values in parallel using theJobScheduler.default <CONTEXT_TYPE extends JobScheduler.JobThreadContext>
voiditerateSerial(@Nullable ExecutionContext executionContext, @Nullable LogOutputAppendable description, @NotNull Supplier<CONTEXT_TYPE> taskThreadContextFactory, int start, int count, @NotNull JobScheduler.IterateResumeAction<CONTEXT_TYPE> action, @NotNull Runnable onComplete, @NotNull Runnable cleanup, @NotNull Consumer<Exception> onError) Provides a mechanism to iterate over a range of values serially using theJobScheduler.static voidrunJob(@Nullable ExecutionContext executionContext, @NotNull Runnable runnable, @Nullable LogOutputAppendable description, @NotNull Consumer<Exception> onError) Run a submitted job on whatever thread the scheduler has chosen for it: underexecutionContextwhen one is given; with anExceptionit throws delivered toonError, and shouldonErroritself throw, that failure, carrying the Exception as suppressed, reported to the global fatal error reporter, then rethrown; and with anything else it throws, anErrorin practice, delivered asasDeliverableException(java.lang.Throwable)makes it, then reported to the global fatal error reporter, then rethrown.submit(ExecutionContext executionContext, Runnable runnable, LogOutputAppendable description, Consumer<Exception> onError) Cause runnable to be executed.intHow many threads exist in the job scheduler? The job submitters can use this value to determine how many sub-jobs to split work into.
-
Field Details
-
DEFAULT_CONTEXT
-
DEFAULT_CONTEXT_FACTORY
-
UNREPORTABLE_JOB_ERROR
Delivered when a job fails with anErrorand even the wrapper for it cannot be allocated, which is to say when the heap is exhausted — the very failure this path exists for. Allocated once, when this interface is initialized, so that delivering a failure never depends on being able to allocate. Being shared, it takes no suppressed exceptions.
-
-
Method Details
-
asDeliverableException
Convert a Throwable that escaped a scheduled job into something theConsumer<Exception>error handlers used throughout the scheduler can accept. Exceptions pass through unchanged; anything else is wrapped, so that the thread waiting on the job's completion fails with a diagnostic instead of waiting forever for a completion that cannot happen. That is anErrorin practice, most often anOutOfMemoryError, though Groovy code can throw a plainThrowableas well.This never throws. The wrapper carries no stack trace of its own: filling one in is the largest allocation here, and the stack that matters belongs to the Error, which is kept as the cause. Should even that allocation fail,
UNREPORTABLE_JOB_ERRORis delivered instead — a caller that fails without a diagnostic is still far better than one that waits forever.- Parameters:
throwable- the Throwable that escaped the job- Returns:
throwableitself if it is an Exception, otherwise a wrapper holding it as its cause
-
runJob
static void runJob(@Nullable @Nullable ExecutionContext executionContext, @NotNull @NotNull Runnable runnable, @Nullable @Nullable LogOutputAppendable description, @NotNull @NotNull Consumer<Exception> onError) Run a submitted job on whatever thread the scheduler has chosen for it: underexecutionContextwhen one is given; with anExceptionit throws delivered toonError, and shouldonErroritself throw, that failure, carrying the Exception as suppressed, reported to the global fatal error reporter, then rethrown; and with anything else it throws, anErrorin practice, delivered asasDeliverableException(java.lang.Throwable)makes it, then reported to the global fatal error reporter, then rethrown. The default reporter ends the process and does not return, so the rethrow happens only under a reporter that does. Implementations wrap this in whatever performance accounting they keep.ImmediateJobScheduler, which runs every job on the submitting thread, does not use it: an Error from one of its jobs propagates to that thread undelivered and unreported.- Parameters:
executionContext- the execution context to run the job under, or null to run it under the thread's ownrunnable- the jobdescription- a description of the job, for the fatal error reportonError- the consumer to deliver a failure to
-
submit
JobScheduler.Submission submit(ExecutionContext executionContext, Runnable runnable, LogOutputAppendable description, Consumer<Exception> onError) Cause runnable to be executed.A scheduler runs every job it accepts: at once, after queuing it, or, when it has no thread free and no queue to hold the job, on the calling thread. A scheduler that cannot take a job at all throws instead, and the job does not run.
- Parameters:
executionContext- the execution context to run it underrunnable- the runnable to executedescription- a description for loggingonError- a routine to call if an exception occurs while running runnable- Returns:
- the accepted job, which the submitter may withdraw while no thread has started it
-
getAccumulatedPerformance
BasePerformanceEntry getAccumulatedPerformance()The performance statistics of all runnables that have been completed off-thread, or null if all were executed in the current thread.When initializing an operation, the
OperationInitializerJobSchedulerexecutes the completion callback as part of another task. Therefore, you must not read the accumulated performance from the completion callback when initializing an operation; as it could miss data from some of the tasks. Furthermore, even though the completion callback identifies the result as ready does not mean that the completion callback has actually completed. To guard against this theOperationInitializerJobSchedulerwaits for all jobs to be complete before returning theBasePerformanceEntry. Therefore, if you call this from a completion callback, then the operation will hang.ExecutorJobSchedulerwaits the same way, so the same applies to it.It never runs a job itself, so call it only once every job has started: after
awaitCompletion(java.util.concurrent.CompletableFuture<T>)orinvokeParallel(io.deephaven.engine.context.ExecutionContext, io.deephaven.base.log.LogOutputAppendable, java.util.function.Supplier<CONTEXT_TYPE>, int, int, io.deephaven.engine.table.impl.util.JobScheduler.IterateAction<CONTEXT_TYPE>), which leave no job waiting for a thread. Otherwise it may wait for a job queued behind threads that are themselves waiting, perhaps for a lock the calling thread holds. -
awaitCompletion
<T> T awaitCompletion(@NotNull @NotNull CompletableFuture<T> future) throws InterruptedException, ExecutionException Wait forfuture, which this scheduler's jobs complete, and return its result asFuture.get()does.Each scheduler decides how a caller waits, or whether it may:
OperationInitializerJobSchedulerandExecutorJobSchedulerreturn or throw only oncefutureis done and every job submitted to them has finished, so thatgetAccumulatedPerformance()has nothing left to wait for; one of their own jobs, which would wait for itself, fails instead of waiting. While they wait, they run on the calling thread any of their jobs that no other thread has started, and block only while every job not yet finished is running on another thread. The wait therefore never depends on a job queued behind threads that are themselves waiting, perhaps for a lock the calling thread holds. An interrupt cancelsfuture, so that jobs that check it can stop early, but the wait goes on until every job has finished, because the caller is about to release what they are using; thenInterruptedExceptionis thrown.ImmediateJobSchedulerhas run every job by the timesubmit(io.deephaven.engine.context.ExecutionContext, java.lang.Runnable, io.deephaven.base.log.LogOutputAppendable, java.util.function.Consumer<java.lang.Exception>)returns, sofuturemust already be done, and the call fails if it is not.UpdateGraphJobSchedulerruns its jobs as update graph notifications, which a thread must not block for, so the call fails.
- Parameters:
future- the future to wait for- Returns:
- the result of
future - Throws:
InterruptedException- if the calling thread is interrupted while waitingExecutionException- iffuturecompleted exceptionally
-
threadCount
int threadCount()How many threads exist in the job scheduler? The job submitters can use this value to determine how many sub-jobs to split work into. -
iterateParallel
@FinalDefault default <CONTEXT_TYPE extends JobScheduler.JobThreadContext> void iterateParallel(@Nullable @Nullable ExecutionContext executionContext, @Nullable @Nullable LogOutputAppendable description, @NotNull @NotNull Supplier<CONTEXT_TYPE> taskThreadContextFactory, int start, int count, @NotNull @NotNull JobScheduler.IterateAction<CONTEXT_TYPE> action, @NotNull @NotNull Runnable onComplete, @NotNull @NotNull Runnable cleanup, @NotNull @NotNull Consumer<Exception> onError) Provides a mechanism to iterate over a range of values in parallel using theJobScheduler- Parameters:
executionContext- the execution context for this taskdescription- the description to use for loggingtaskThreadContextFactory- the factory that suppliescontextsfor the threads handling the sub-tasksstart- the integer value from which to start iteratingcount- the number of times this task should be calledaction- the task to perform, the current iteration index is provided as a parameteronComplete- this will be called when all iterations are completecleanup- called after onComplete successfully returns. If the invocation of the cleanup throws an exception, onError will not be called.onError- error handler for the scheduler to use when the iteration fails, asJobSchedulerdescribes, or if onComplete throws an exception
-
iterateParallel
@FinalDefault default <CONTEXT_TYPE extends JobScheduler.JobThreadContext> void iterateParallel(@Nullable @Nullable ExecutionContext executionContext, @Nullable @Nullable LogOutputAppendable description, @NotNull @NotNull Supplier<CONTEXT_TYPE> taskThreadContextFactory, int start, int count, @NotNull @NotNull JobScheduler.IterateResumeAction<CONTEXT_TYPE> action, @NotNull @NotNull Runnable onComplete, @NotNull @NotNull Runnable cleanup, @NotNull @NotNull Consumer<Exception> onError) Provides a mechanism to iterate over a range of values in parallel using theJobScheduler. The advantage to using this over the other method is the resumption callable onactionthat will trigger the next execution. This allows the next iteration and the completion runnable to be delayed until dependent asynchronous serial or parallel scheduler jobs have completed.- Parameters:
executionContext- the execution context for this taskdescription- the description to use for loggingtaskThreadContextFactory- the factory that suppliescontextsfor the tasksstart- the integer value from which to start iteratingcount- the number of times this task should be calledaction- the task to perform, the current iteration index and a resume Runnable are parametersonComplete- this will be called when all iterations are completecleanup- called after onComplete successfully returns. If the invocation of the cleanup throws an exception, onError will not be called.onError- error handler for the scheduler to use when the iteration fails, asJobSchedulerdescribes, or if onComplete throws an exception
-
iterateSerial
@FinalDefault default <CONTEXT_TYPE extends JobScheduler.JobThreadContext> void iterateSerial(@Nullable @Nullable ExecutionContext executionContext, @Nullable @Nullable LogOutputAppendable description, @NotNull @NotNull Supplier<CONTEXT_TYPE> taskThreadContextFactory, int start, int count, @NotNull @NotNull JobScheduler.IterateResumeAction<CONTEXT_TYPE> action, @NotNull @NotNull Runnable onComplete, @NotNull @NotNull Runnable cleanup, @NotNull @NotNull Consumer<Exception> onError) Provides a mechanism to iterate over a range of values serially using theJobScheduler. The advantage to using this over a simple iteration is the resumption callable onactionthat will trigger the next execution. This allows the next iteration and the completion runnable to be delayed until dependent asynchronous serial or parallel scheduler jobs have completed.- Parameters:
executionContext- the execution context for this taskdescription- the description to use for loggingtaskThreadContextFactory- the factory that suppliescontextsfor the tasksstart- the integer value from which to start iteratingcount- the number of times this task should be calledaction- the task to perform, the current iteration index and a resume Runnable are parametersonComplete- this will be called when all iterations are completecleanup- called after onComplete successfully returns. If the invocation of the cleanup throws an exception, onError will not be called.onError- error handler for the scheduler to use when the iteration fails, asJobSchedulerdescribes, or if onComplete throws an exception
-
invokeParallel
@FinalDefault default <CONTEXT_TYPE extends JobScheduler.JobThreadContext> void invokeParallel(@Nullable @Nullable ExecutionContext executionContext, @Nullable @Nullable LogOutputAppendable description, @NotNull @NotNull Supplier<CONTEXT_TYPE> taskThreadContextFactory, int start, int count, @NotNull @NotNull JobScheduler.IterateAction<CONTEXT_TYPE> action) Iterates over a range of values in parallel asiterateParallel(io.deephaven.engine.context.ExecutionContext, io.deephaven.base.log.LogOutputAppendable, java.util.function.Supplier<CONTEXT_TYPE>, int, int, io.deephaven.engine.table.impl.util.JobScheduler.IterateAction<CONTEXT_TYPE>, java.lang.Runnable, java.lang.Runnable, java.util.function.Consumer<java.lang.Exception>)does, except that the calling thread runs tasks too, and this method returns only once the iteration is over. WhereiterateParallelreports its outcome through callbacks and may return first, this holds the calling thread and reports its outcome by returning or throwing; whatever a caller would do inonComplete,cleanuporonErrorit does after this call, or in acatchorfinallyaround it.Participation. Up to
min(count, threadCount()) - 1task invokers are submitted to the scheduler, and one more runs on the calling thread, each with a task context of its own fromtaskThreadContextFactory, which is called on the calling thread. The caller therefore works even when the scheduler accepts every submission. When the caller runs out of tasks it runs any submitted invoker that the scheduler has not started yet, and withdraws its job; a job the scheduler cannot withdraw does nothing when it starts. So the caller never waits on an invoker that has not started, whatever the scheduler does with a job it cannot start at once, and the scheduler does not wait for the jobs of the invokers the caller ran.Return. This returns only once every task has completed and every task context has been closed.
Failure. A task fails by throwing, as
JobScheduler.IterateActiondescribes, and so does the iteration, as it does when a task context fails to close or the iteration fails to start: no new task starts, and once every task already running has finished, the first failure is thrown, as itself when it is aRuntimeExceptionand wrapped in anUncheckedDeephavenExceptionotherwise. Later failures are attached to it as suppressed. AnError, or any other Throwable that is not an Exception, thrown by a task on a scheduler thread is still reported as fatal and rethrown there, and reaches the caller wrapped, asasDeliverableException(java.lang.Throwable)makes it; one thrown on the calling thread reaches the caller as itself, carrying the later failures, once the other tasks have finished. A submitted invoker that the scheduler runs on the calling thread, asExecutorJobSchedulerdoes with a job its executor refuses, is still the scheduler's job, and an Error from its tasks is reported as fatal as it would be on a scheduler thread.Interruption. The wait cannot be interrupted, because the caller is about to release what the running tasks are using; the interrupt is restored before this returns.
Nesting and schedulers. The caller waits only on tasks already running on another thread, never on one the scheduler has not started, so a task may itself call
invokeParallel, to any depth, on any scheduler and on whatever thread it runs on. The invocation completes as long as every task's own work does and no task waits on other work submitted to the scheduler, such as a callback-form iteration, which the caller cannot run on its behalf. Nor may a task wait on another task of the same invocation: with one thread, or no pool thread free, the caller runs them one after another. The update graph's scheduler accepts jobs only during an update cycle, so with more than one update thread this must be called during an update cycle. Its parallelism comes from the update graph's pool threads. Called from an update thread, as listener code is, the other tasks are dispatched to the remaining pool threads by the update graph's refresh thread. Called from the refresh thread itself, nothing is dispatched while it waits, so it runs every task itself, one after another.- Parameters:
executionContext- the execution context the tasks run under, on every thread that runs them, the calling thread's included; null to run them under each thread's owndescription- the description to use for loggingtaskThreadContextFactory- the factory that suppliescontextsfor the tasksstart- the integer value from which to start iteratingcount- the number of times this task should be calledaction- the task to perform, the current iteration index is provided as a parameter
-