| /******************************************************************************* |
| * Copyright (c) 1997, 2018 by ProSyst Software GmbH |
| * http://www.prosyst.com |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * ProSyst Software GmbH - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.equinox.internal.util.impl.tpt.timer; |
| |
| import java.security.*; |
| import java.util.Hashtable; |
| import org.eclipse.equinox.internal.util.UtilActivator; |
| import org.eclipse.equinox.internal.util.impl.tpt.ServiceFactoryImpl; |
| import org.eclipse.equinox.internal.util.impl.tpt.threadpool.Executor; |
| import org.eclipse.equinox.internal.util.impl.tpt.threadpool.ThreadPoolFactoryImpl; |
| import org.eclipse.equinox.internal.util.pool.ObjectPool; |
| import org.eclipse.equinox.internal.util.ref.Log; |
| import org.eclipse.equinox.internal.util.timer.Timer; |
| import org.eclipse.equinox.internal.util.timer.TimerListener; |
| |
| /** |
| * @author Pavlin Dobrev |
| * @version 1.0 |
| */ |
| |
| public class TimerImpl implements Runnable { |
| |
| static Hashtable<TimerQueueNode, TimerQueueNode> nodes; |
| static ObjectPool nodePool; |
| |
| static ThreadPoolFactoryImpl threadPool; |
| |
| private TimerQueue queue; |
| private boolean terminated = false; |
| private Object sync = new Object(); |
| private Thread th; |
| |
| public TimerImpl(ThreadPoolFactoryImpl threadPool) { |
| nodePool = new ObjectPool(new TimerQueueNode(), 2, 4, 2); |
| TimerImpl.threadPool = threadPool; |
| nodes = new Hashtable<>(10); |
| queue = new TimerQueue(); |
| try { |
| th = ((ServiceFactoryImpl.privileged()) ? getOne() : new Thread(this, "[Timer] - Main Queue Handler")); |
| try { |
| String str = UtilActivator.bc.getProperty("equinox.timer.priority"); |
| if (str != null) |
| th.setPriority(Integer.parseInt(str)); |
| } catch (Throwable ignored) { |
| } |
| th.start(); |
| } catch (Exception e) { |
| throw new RuntimeException("Can not start Timer thread!" + e); |
| } |
| } |
| |
| @Override |
| public void run() { |
| TimerQueueNode n = null; |
| while (!terminated) { |
| synchronized (sync) { |
| if (n == null && queue.isEmpty()) { |
| try { |
| sync.wait(); |
| } catch (Exception e) { |
| } |
| // todo check if isEmpty is necessary |
| if (queue.isEmpty() || terminated) { |
| continue; |
| } |
| } |
| } |
| synchronized (queue) { |
| n = queue.getMin(); |
| while (n != null && !n.enabled) { |
| queue.removeMin(); |
| n.returnInPool(); |
| n = queue.getMin(); |
| } |
| if (n == null) |
| continue; |
| long current = System.currentTimeMillis(); |
| if (n.runOn <= current) { |
| switch (n.type) { |
| case (Timer.ONE_SHOT_TIMER) : { |
| threadPool.execute0(n, n.priority, n.getEName(), n.acc); |
| queue.removeMin(); |
| nodes.remove(n); |
| break; |
| } |
| case (Timer.ONE_SHOT_TIMER_NO_DELAY) : { |
| Executor e = threadPool.getExecutor(); |
| if (e != null) { |
| e.setPriorityI(n.priority); |
| e.setRunnable(n, n.getEName(), threadPool, n.acc); |
| } else { |
| Thread th = new Thread(n, n.getEName()); |
| th.setPriority(n.priority); |
| th.start(); |
| } |
| queue.removeMin(); |
| nodes.remove(n); |
| break; |
| } |
| case (Timer.PERIODICAL_TIMER) : { |
| threadPool.execute0(n, n.priority, n.getEName(), n.acc); |
| n.runOn += n.period; |
| if (n.runOn < current) { // time changed |
| n.runOn = current + n.period; |
| } |
| queue.rescheduleMin(n.runOn); |
| break; |
| } |
| case (Timer.PERIODICAL_TIMER_NO_DELAY) : { |
| Executor e = threadPool.getExecutor(); |
| if (e != null) { |
| e.setPriorityI(n.priority); |
| e.setRunnable(n, n.getEName(), threadPool, n.acc); |
| } else { |
| Thread th = new Thread(n, n.getEName()); |
| th.setPriority(n.priority); |
| th.start(); |
| } |
| if (n.runOn < current) { // time changed |
| n.runOn = current + n.period; |
| } |
| queue.rescheduleMin(n.runOn); |
| } |
| } |
| continue; |
| } |
| } |
| synchronized (sync) { |
| long tmpWait; |
| if (n != null && (tmpWait = n.runOn - System.currentTimeMillis()) > 0) { |
| try { |
| sync.wait(tmpWait); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } |
| TimerQueueNode tmp = null; |
| synchronized (queue) { |
| tmp = queue.getMin(); |
| if (tmp != n) { |
| n = tmp; |
| } |
| } |
| /* what's this for - we'll continue in any case */ |
| continue; |
| } |
| } |
| } // while (!terminated) |
| nodePool.clear(); |
| nodePool = null; |
| nodes.clear(); |
| nodes = null; |
| queue = null; |
| } |
| |
| public void terminate() { |
| terminated = true; |
| synchronized (sync) { |
| sync.notify(); |
| } |
| try { |
| th.join(); |
| } catch (InterruptedException ie) { |
| } |
| } |
| |
| private void put(TimerListener listener, int priority, int timerType, long periodMilis, int event, String name, AccessControlContext acc) { |
| if (terminated || nodePool == null) { |
| throw new RuntimeException("This Instance is a ZOMBIE!!!" + terminated + " " + nodePool); |
| } |
| |
| TimerQueueNode n = (TimerQueueNode) nodePool.getObject(); |
| n.setEvent(listener, priority, timerType, System.currentTimeMillis() + periodMilis, periodMilis, event, name, acc); |
| TimerQueueNode tmp = nodes.remove(n); |
| if (tmp != null) { |
| synchronized (queue) { |
| queue.removeTimerNode(tmp); |
| } |
| tmp.returnInPool(); |
| } |
| nodes.put(n, n); |
| TimerQueueNode nx; |
| synchronized (queue) { |
| queue.add(n); |
| nx = queue.getMin(); |
| } |
| if (nx == n) { |
| synchronized (sync) { |
| sync.notifyAll(); |
| } |
| } |
| } |
| |
| void addNotifyListener(TimerListener listener, int priority, int timerType, long periodMilis, int event, String name) { |
| if (timerType < Timer.ONE_SHOT_TIMER || timerType > Timer.PERIODICAL_TIMER_NO_DELAY) { |
| throw new IllegalArgumentException("Invalid Timer Type"); |
| } |
| if (listener != null) { |
| if (priority >= Thread.MIN_PRIORITY && priority <= Thread.MAX_PRIORITY) { |
| if (periodMilis > 0) { |
| AccessControlContext acc = Log.security() ? AccessController.getContext() : null; |
| put(listener, priority, timerType, periodMilis, event, name, acc); |
| } else { |
| throw new IllegalArgumentException("Time period must be positive!"); |
| } |
| } else { |
| throw new IllegalArgumentException("Priority must be between Thread.MIN_PRIORITY and Thread.MAX_PRIORITY!"); |
| } |
| } else { |
| throw new IllegalArgumentException("The timer listener is null"); |
| } |
| } |
| |
| public void removeListener(TimerListener listener, int event) { |
| TimerQueueNode rmTmp = (TimerQueueNode) nodePool.getObject(); |
| rmTmp.setEvent(listener, 0, 0, 0, 0, event, null, null); |
| TimerQueueNode old = nodes.remove(rmTmp); |
| if (old != null) { |
| synchronized (queue) { |
| queue.removeTimerNode(old); |
| } |
| old.returnInPool(); |
| } |
| rmTmp.returnInPool(); |
| } |
| |
| private class PrivilegedActionImpl implements PrivilegedAction<Thread> { |
| private Runnable runnable = null; |
| private boolean locked = false; |
| private boolean waiting = false; |
| |
| PrivilegedActionImpl() { |
| } |
| |
| public synchronized void set(Runnable runnable) { |
| while (locked) { |
| waiting = true; |
| try { |
| wait(); |
| } catch (Exception _) { |
| } |
| waiting = false; |
| } |
| locked = true; |
| this.runnable = runnable; |
| } |
| |
| @Override |
| public Thread run() { |
| Runnable runnableLocal = null; |
| synchronized (this) { |
| runnableLocal = this.runnable; |
| this.runnable = null; |
| locked = false; |
| if (waiting) |
| notifyAll(); |
| } |
| return new Thread(runnableLocal, "[Timer] - Main Queue Handler"); |
| } |
| } |
| |
| public Thread getOne() throws Exception { |
| if (action == null) |
| action = new PrivilegedActionImpl(); |
| action.set(this); |
| return AccessController.doPrivileged(action); |
| } |
| |
| PrivilegedActionImpl action = null; |
| } |