blob: 30b27a6d93e64a144ff3948a1bdc829801b0a440 [file] [log] [blame]
/*******************************************************************************
* 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;
}