Class ExecutorJobScheduler

java.lang.Object
io.deephaven.engine.table.impl.util.ExecutorJobScheduler
All Implemented Interfaces:
JobScheduler

public class ExecutorJobScheduler extends Object implements JobScheduler
A JobScheduler over a plain Executor. A job the executor refuses with a RejectedExecutionException, as a bounded pool with no queue does when all of its threads are busy, runs on the submitting thread instead, so no job is ever dropped; over a pool with no queue, such as newHelperPool(int, java.util.concurrent.ThreadFactory) makes, no job ever waits in a queue behind another either.

It is meant for a pool shaped like newHelperPool(int, java.util.concurrent.ThreadFactory), which may be small and shared by many callers, and on which a job may itself invoke a nested iteration; the pool's size bounds the threads a caller adds to its own, never the work it can finish.

A caller of JobScheduler.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>) idles only once no task is left for it to claim: it then waits for the tasks that helpers already started. It never waits on a helper that has not started, since it runs any such helper itself, so this holds for any executor. A task that itself calls invokeParallel is such a caller too, and works through its nested iteration the same way; when the pool has no free thread, its nested helpers run on its own thread and it does not wait at all.

Like the other schedulers, an instance accumulates the performance of the jobs that ran off the submitting thread, so make one per operation, over an executor that outlives it.

  • Constructor Details

    • ExecutorJobScheduler

      public ExecutorJobScheduler(@NotNull @NotNull Executor executor, int threadCount)
      Parameters:
      executor - runs the jobs; it may refuse one with a RejectedExecutionException, which then runs on the submitting thread
      threadCount - the most threads, the submitting thread included, that one iteration on this scheduler should use; an iteration asks the executor for one fewer than this when the caller takes part
  • Method Details

    • newHelperPool

      public static ThreadPoolExecutor newHelperPool(int maxThreads, @NotNull @NotNull ThreadFactory threadFactory)
      Make the pool this scheduler is designed for: up to maxThreads threads, made when needed and retired after a minute without work, and no queue, so that a job either gets a thread at once or is refused. The pool is never shut down by this class; give it daemon threads.
      Parameters:
      maxThreads - the most threads the pool holds
      threadFactory - makes the pool's threads
      Returns:
      the pool
    • submit

      public void submit(ExecutionContext executionContext, Runnable runnable, LogOutputAppendable description, Consumer<Exception> onError)
      Description copied from interface: JobScheduler
      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.

      Specified by:
      submit in interface JobScheduler
      Parameters:
      executionContext - the execution context to run it under
      runnable - the runnable to execute
      description - a description for logging
      onError - a routine to call if an exception occurs while running runnable
    • getAccumulatedPerformance

      public BasePerformanceEntry getAccumulatedPerformance()
      Description copied from interface: JobScheduler
      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 OperationInitializerJobScheduler executes 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 the OperationInitializerJobScheduler waits for all jobs to be complete before returning the BasePerformanceEntry. Therefore, if you call this from a completion callback, then the operation will hang. ExecutorJobScheduler waits the same way, so the same applies to it.

      Specified by:
      getAccumulatedPerformance in interface JobScheduler
    • threadCount

      public int threadCount()
      Description copied from interface: JobScheduler
      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.
      Specified by:
      threadCount in interface JobScheduler