@Override public void runTimer() throws Exception { if (index.traceOptimisticLockErrors) { tasks.defaultExecutor().fork(this::cleanOldRecordings); } }
@Override protected String doWork() throws Exception { long now = System.currentTimeMillis(); int numScheduled = 0; synchronized (waitingTasks) { Iterator<WaitingTask> iter = waitingTasks.iterator(); while (iter.hasNext()) { WaitingTask next = iter.next(); if (next.timeout > now) { return numScheduled > 0 ? "Re-Scheduled Tasks: " + numScheduled : null; } iter.remove(); tasks.executor(next.executor).start(next); numScheduled++; } } return null; }
private synchronized void schedule(ExecutionBuilder.TaskWrapper wrapper) { // As tasks often create a loop by calling itself (e.g. BackgroundLoop), we drop // scheduled tasks if the async framework is no longer running, as the tasks would be rejected and // dropped anyway... if (!running) { return; } Long lastInvocation = scheduleTable.get(wrapper.synchronizer); if (lastInvocation == null || (System.currentTimeMillis() - lastInvocation) > wrapper.intervalMinLength) { executeNow(wrapper); } else { if (dropIfAlreadyScheduled(wrapper)) { if (LOG.isFINE()) { LOG.FINE( "Dropping a scheduled task (%s), as for its synchronizer (%s) another task is already scheduled", wrapper.runnable, wrapper.synchronizer); } return; } wrapper.waitUntil = lastInvocation + wrapper.intervalMinLength; addToSchedulerQueue(wrapper); wakeSchedulerLoop(); } }
protected void execute(ExecutionBuilder.TaskWrapper wrapper) { if (wrapper.synchronizer == null) { executeNow(wrapper); } else { schedule(wrapper); } }
private void schedulerLoop() { while (running) { try { executeWaitingTasks(); idle(); } catch (Exception t) { Exceptions.handle(LOG, t); } } }
/** * Specifies to create a new CallContext while executing the given task. * * @param task the task to execute. * @return a {@code Future} representing the execution created by this builder. */ public Future start(Runnable task) { wrapper.runnable = task; wrapper.fork = false; tasks.execute(wrapper); return wrapper.promise; }
@Override public void awaitTermination() { for (Map.Entry<String, AsyncExecutor> e : executors.entrySet()) { AsyncExecutor exec = e.getValue(); if (!exec.isTerminated()) { blockUnitExecutorTerminates(e.getKey(), exec); } } executors.clear(); }
private void executeWaitingTasks() { synchronized (schedulerQueue) { Iterator<ExecutionBuilder.TaskWrapper> iter = schedulerQueue.iterator(); long now = System.currentTimeMillis(); while (iter.hasNext()) { ExecutionBuilder.TaskWrapper wrapper = iter.next(); if (wrapper.waitUntil <= now) { executeNow(wrapper); iter.remove(); } else { // The scheduler queue is sorted by "waitUntil" -> as soon as we discover the // first task which can not run yet, we can abort... return; } } } }
@SuppressWarnings({"squid:S2274", "squid:S899"}) @Explain("We neither need a loop nor the result here.") private void idle() { try { schedulerLock.lock(); try { long waitTime = computeWaitTime(); if (waitTime < 0) { // No work available -> wait for something to do... workAvailable.await(); } else if (waitTime > 0) { // No task can be executed in the next millisecond. Sleep for // "waitTime" (or more work) until the next check for executable work... workAvailable.await(waitTime, TimeUnit.MILLISECONDS); } } finally { schedulerLock.unlock(); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); Exceptions.ignore(e); } }
/** * Specifies to fork the current CallContext while executing the given task. * * @param task the task to execute. * @return a {@code Future} representing the execution created by this builder. */ public Future fork(Runnable task) { wrapper.runnable = task; wrapper.fork = true; tasks.execute(wrapper); return wrapper.promise; }
/** * Executes the action in its own executor */ public void execute() { CallContext.setCurrent(context); tasks.executor("index-delay").fork(cmd); } }
@Override public synchronized void runTimer() throws Exception { tasks.defaultExecutor().start(this::collectMetrics); }
/** * Calls {@link #executeWork()} in the determined executor. * <p> * This is kind of the main loop, as {@code executeWork()} will call {@code loop()} once the computation * ({@code doWork()} is finished. Using {@link ExecutionBuilder#frequency(Object, double)} this is limited to the * call frequency as determined by {@code maxCallFrequency()}. */ protected void loop() { tasks.executor(determineExecutor()).frequency(this, maxCallFrequency()).start(this::executeWork); }
@Override public void runTimer() throws Exception { tasks.defaultExecutor().start(this::runEviction); }
/** * Handles a delete of the given entity. * * @param entity the entity (which must be of type {@link #getReferencedClass()}) which is going to be deleted */ public void onDelete(final Entity entity) { if (refType.cascade() == Cascade.REJECT) { rejectDeleteIfNecessary(entity); } else if (refType.cascade() == Cascade.SET_NULL) { tasks.executor(IndexAccess.ASYNC_CATEGORY_INDEX_INTEGRITY).fork(() -> setNull(entity)); } else if (refType.cascade() == Cascade.CASCADE) { tasks.executor(IndexAccess.ASYNC_CATEGORY_INDEX_INTEGRITY).fork(() -> cascadeDelete(entity)); } }
/** * Performs a re-index of all indices into new ones starting with the given index prefix instead of the * currently active one. * * @param prefix the new index prefix to use */ public void reIndex(String prefix) { final String newPrefix = prefix.endsWith("-") ? prefix : prefix + "-"; access.tasks.defaultExecutor().fork(new ReIndexTask(this, newPrefix)); }
/** * Forks the given computation and returns a {@link Promise} for the computed value. * <p> * Forks the computation, which means that the current <tt>CallContext</tt> is transferred to the new thread, * and returns the result of the computation as promise. * <p> * If the executor for the given category is saturated (all threads are active and the queue is full) this * will drop the computation and the promise will be sent a <tt>RejectedExecutionException</tt>. * * @param category the category which implies which executor to use. * @param computation the computation which eventually yields the resulting value * @param <V> the type of the resulting value * @return a promise which will either be eventually supplied with the result of the computation or with an error */ public <V> Promise<V> fork(String category, final Supplier<V> computation) { final Promise<V> result = new Promise<>(); executor(category).dropOnOverload(() -> result.fail(new RejectedExecutionException())).fork(() -> { try { result.success(computation.get()); } catch (Exception t) { result.fail(t); } }); return result; }
@Override public void runTimer() throws Exception { tasks.defaultExecutor().start(() -> Sirius.getSetup().cleanOldLogFiles(logFileRetention.toMillis())); } }
/** * Updates the entity in the database asynchronous using a dedicated thread pool. * * @param entity the entity to be written into the DB * @param <E> the type of the entity to update * @return a {@link Promise} handling the update process */ public <E extends Entity> Promise<E> updateAsync(E entity) { Promise<E> promise = new Promise<>(); tasks.executor(ASYNC_UPDATER).start(() -> { try { update(entity); promise.success(entity); } catch (Exception e) { promise.fail(e); } }); return promise; }
private static void stopLifecycleParticipants() { LOG.INFO("Stopping lifecycles..."); LOG.INFO(SEPARATOR_LINE); for (int i = lifecycleStopParticipants.size() - 1; i >= 0; i--) { Stoppable stoppable = lifecycleStopParticipants.get(i); Future future = tasks.defaultExecutor().fork(() -> stopLifecycle(stoppable)); if (!future.await(Duration.ofSeconds(10))) { LOG.WARN("Lifecycle '%s' did not stop within 10 seconds....", stoppable.getClass().getName()); } } LOG.INFO(SEPARATOR_LINE); }