|
3 | 3 | import io.deephaven.base.log.LogOutputAppendable;
|
4 | 4 | import io.deephaven.engine.context.ExecutionContext;
|
5 | 5 | import io.deephaven.engine.table.impl.perf.BasePerformanceEntry;
|
6 |
| -import io.deephaven.io.log.impl.LogOutputStringImpl; |
7 | 6 | import io.deephaven.util.SafeCloseable;
|
8 |
| -import io.deephaven.util.process.ProcessEnvironment; |
9 | 7 |
|
| 8 | +import java.util.ArrayDeque; |
| 9 | +import java.util.Deque; |
| 10 | +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; |
10 | 11 | import java.util.function.Consumer;
|
11 | 12 |
|
12 | 13 | public class ImmediateJobScheduler implements JobScheduler {
|
13 |
| - public static final ImmediateJobScheduler INSTANCE = new ImmediateJobScheduler(); |
| 14 | + |
| 15 | + private volatile Thread processingThread; |
| 16 | + private static final AtomicReferenceFieldUpdater<ImmediateJobScheduler, Thread> PROCESSING_THREAD_UPDATER = |
| 17 | + AtomicReferenceFieldUpdater.newUpdater(ImmediateJobScheduler.class, Thread.class, "processingThread"); |
| 18 | + |
| 19 | + private final Deque<Runnable> pendingJobs = new ArrayDeque<>(); |
14 | 20 |
|
15 | 21 | @Override
|
16 | 22 | public void submit(
|
17 | 23 | final ExecutionContext executionContext,
|
18 | 24 | final Runnable runnable,
|
19 | 25 | final LogOutputAppendable description,
|
20 | 26 | final Consumer<Exception> onError) {
|
21 |
| - // We do not need to install the update context since we are not changing thread contexts. |
22 |
| - try (SafeCloseable ignored = executionContext != null ? executionContext.open() : null) { |
23 |
| - runnable.run(); |
24 |
| - } catch (Exception e) { |
25 |
| - onError.accept(e); |
26 |
| - } catch (Error e) { |
27 |
| - final String logMessage = new LogOutputStringImpl().append(description).append(" Error").toString(); |
28 |
| - ProcessEnvironment.getGlobalFatalErrorReporter().report(logMessage, e); |
29 |
| - throw e; |
| 27 | + final Thread thisThread = Thread.currentThread(); |
| 28 | + final boolean thisThreadIsProcessing = processingThread == thisThread; |
| 29 | + |
| 30 | + if (!thisThreadIsProcessing && !PROCESSING_THREAD_UPDATER.compareAndSet(this, null, thisThread)) { |
| 31 | + throw new IllegalCallerException("An unexpected thread submitted a job to this job scheduler"); |
| 32 | + } |
| 33 | + |
| 34 | + pendingJobs.addLast(() -> { |
| 35 | + // We do not need to install the update context since we are not changing thread contexts. |
| 36 | + try (SafeCloseable ignored = executionContext != null ? executionContext.open() : null) { |
| 37 | + runnable.run(); |
| 38 | + } catch (Exception e) { |
| 39 | + onError.accept(e); |
| 40 | + } |
| 41 | + }); |
| 42 | + |
| 43 | + if (thisThreadIsProcessing) { |
| 44 | + // We're already draining the queue in an ancestor stack frame |
| 45 | + return; |
| 46 | + } |
| 47 | + |
| 48 | + try { |
| 49 | + Runnable job; |
| 50 | + while ((job = pendingJobs.pollLast()) != null) { |
| 51 | + job.run(); |
| 52 | + } |
| 53 | + } finally { |
| 54 | + PROCESSING_THREAD_UPDATER.set(this, null); |
30 | 55 | }
|
31 | 56 | }
|
32 | 57 |
|
|
0 commit comments