| /******************************************************************************* |
| * Copyright (c) 1997, 2018 by ProSyst Software GmbH |
| * http://www.prosyst.com |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * ProSyst Software GmbH - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.equinox.internal.util.event; |
| |
| import java.security.AccessController; |
| import java.security.PrivilegedAction; |
| import org.eclipse.equinox.internal.util.UtilActivator; |
| |
| /** |
| * Abstract class for asynchronous event dispatching |
| * |
| * @author Pavlin Dobrev |
| * @version 1.0 |
| */ |
| |
| public abstract class EventThread implements Runnable { |
| |
| /** |
| * The last callbacked listener. If the events thread is not responding, |
| * subclasses can take the appropriate actions - remove the listener for |
| * example |
| */ |
| public Object bad; |
| |
| /** |
| * The event queue. This object must be used for synchronization with the |
| * events thread's state |
| */ |
| protected Queue queue; |
| |
| /** |
| * The state of the thread. |
| * <li> bit 0: 0 = started / 1 = stopped; |
| * <li> bit 1: 0 not waiting / 1 = waiting |
| */ |
| protected int state; |
| |
| /** |
| * The time spent in the current callback, or 0 if the thread is not in a |
| * callback |
| */ |
| protected long time = 0; |
| /** |
| * Instancies counter. Subclasses must not modify it. |
| */ |
| protected int counter = 1; |
| |
| /** |
| * The event to be dispatched |
| */ |
| protected Object element; |
| protected String baseName; |
| protected String name; |
| protected Thread thread; |
| protected ThreadGroup group; |
| private static PrivilegedActionImpl privilegedAction = null; |
| |
| /** |
| * Constructs the first instance of the EventThread |
| * |
| * @param group |
| * The ThreadGroup of the thread, or null for the current thread |
| * group |
| * @param name |
| * The base name of the thread. The <code> counter </code> value |
| * will be added at the end of the string to construct the full |
| * name. |
| * @param size |
| * The initial number of elements of the events queue |
| */ |
| public EventThread(ThreadGroup group, String name, int size) { |
| makeThread(this.group = group, this.name = name + '0'); |
| baseName = name; |
| queue = new Queue(size); |
| int priority = getThreadPriority(); |
| if (priority != Thread.NORM_PRIORITY) |
| thread.setPriority(priority); |
| } |
| |
| /** |
| * Constructs the first instance of the EventThread |
| * |
| * @param group |
| * The ThreadGroup of the thread, or null for the current thread |
| * group |
| * @param name |
| * The base name of the thread. The <code> counter </code> value |
| * will be added at the end of the string to construct the full |
| * name. |
| * @param queue |
| * The events queue |
| */ |
| public EventThread(ThreadGroup group, String name, Queue queue) { |
| makeThread(this.group = group, this.name = name + '0'); |
| baseName = name; |
| this.queue = queue; |
| int priority = getThreadPriority(); |
| if (priority != Thread.NORM_PRIORITY) |
| thread.setPriority(priority); |
| } |
| |
| /** |
| * Constructs a new EventThread, after the <code> old </code> event thread |
| * has stopped responding |
| * |
| * @param old |
| * The previous instance |
| */ |
| protected EventThread(EventThread old) { |
| makeThread(group = old.thread.getThreadGroup(), name = old.baseName + old.counter++); |
| baseName = old.baseName; |
| counter = old.counter; |
| queue = old.queue; |
| int priority = getThreadPriority(); |
| if (priority != Thread.NORM_PRIORITY) |
| thread.setPriority(priority); |
| } |
| |
| public void start() { |
| thread.start(); |
| } |
| |
| /** |
| * Adds an event in the event queue. The method must be synchronized |
| * outside, on the <code> queue </code> field. |
| * |
| * @param event |
| * The event to add |
| * @param check |
| * If true, the method will check if the EventThread is still |
| * responding |
| */ |
| public void addEvent(Object event, boolean check) { |
| try { |
| queue.put(event); |
| } catch (Throwable t) { |
| print(t); |
| return; |
| } |
| if ((state & 2) != 0) |
| queue.notify(); |
| else if (check && checkTime()) |
| try { |
| state |= 1; |
| newEventDispatcher(); // must call start |
| } catch (Throwable t) { |
| print(t); |
| state &= 254; |
| } |
| } |
| |
| /** |
| * Processes the event queue. Sets the event to be dispathed in the |
| * <code> element </code> field and calls <cope> processEvent </code> |
| */ |
| @Override |
| public void run() { |
| synchronized (queue) { |
| queue.notifyAll(); |
| } |
| while (true) { |
| try { |
| synchronized (queue) { |
| if ((state & 1) != 0) |
| return; // closed |
| while ((element = queue.get()) == null) |
| try { |
| state |= 2; // waiting |
| queue.wait(); |
| if ((state & 1) != 0) |
| return; // closed |
| state &= 253; // not waiting |
| } catch (InterruptedException ie) { |
| } |
| } |
| processEvent(); |
| |
| } catch (Throwable t) { |
| print(t); |
| try { // fix memory leak |
| throw new Exception(); |
| } catch (Exception _) { |
| } |
| } |
| } |
| } |
| |
| private void makeThread(ThreadGroup threadGroup, String threadName) { |
| try { |
| if (privilegedAction == null) { |
| privilegedAction = new PrivilegedActionImpl(); |
| } |
| privilegedAction.set(threadGroup, this, threadName); |
| thread = AccessController.doPrivileged(privilegedAction); |
| // thread = new Thread(group, this, name); |
| // thread.setDaemon(false); |
| // if (!disableContextClassLoader) |
| // thread.setContextClassLoader(null); |
| } catch (RuntimeException re) { |
| throw re; |
| } catch (Exception exc) { |
| throw new RuntimeException(exc.toString()); |
| } |
| } |
| |
| public Thread getThread() { |
| return thread; |
| } |
| |
| public String getName() { |
| return name; |
| } |
| |
| /** |
| * Returns the desired thread priority. Called in the constructors of the |
| * class in order to set the returned value to the thread. |
| * |
| * @return priority of the thread |
| */ |
| public abstract int getThreadPriority(); |
| |
| /** |
| * Performs the actual event delivery. The event is stored in the |
| * <code> element </code> field. The method is supposed to perform the |
| * following for every listener: |
| * <li> synchronized on <code> queue </code> check the state of the thread - |
| * if it si closed - return |
| * <li> set the fields <code> bad and time <code> |
| * <li> callback |
| * <li> set bad to null and time to 0 |
| */ |
| public abstract void processEvent(); |
| |
| /** |
| * Checks if the thread is still active. The fields <code> time </code> and |
| * <code> bad </code> must be used. The method is called from the addEvent |
| * method - thus should be synchronizes on the <code> queue </code> field |
| * outside and additional synchronization is not needed. |
| */ |
| public abstract boolean checkTime(); |
| |
| /** |
| * The method must create a new EventThread instance, using |
| * <code> super.EventThread(this) </code> and start it. |
| */ |
| public abstract void newEventDispatcher(); |
| |
| /** |
| * Logs the error. |
| * |
| * @param t |
| */ |
| public abstract void print(Throwable t); |
| } |
| |
| class PrivilegedActionImpl implements PrivilegedAction<Thread> { |
| private ThreadGroup group; |
| private Runnable runnable; |
| private String name; |
| |
| private boolean locked = false; |
| private int waiting = 0; |
| |
| void set(ThreadGroup group, Runnable runnable, String name) { |
| lock(); |
| this.group = group; |
| this.runnable = runnable; |
| this.name = name; |
| } |
| |
| @Override |
| public Thread run() { |
| ThreadGroup group1 = this.group; |
| Runnable runnable1 = this.runnable; |
| String name1 = this.name; |
| unlock(); |
| Thread th = new Thread(group1, runnable1, name1); |
| if (!UtilActivator.getBoolean("equinox.disableContextClassLoader")) //$NON-NLS-1$ |
| th.setContextClassLoader(null); |
| th.setDaemon(false); |
| return th; |
| } |
| |
| private synchronized void lock() { |
| while (locked) |
| try { |
| waiting++; |
| wait(); |
| waiting--; |
| } catch (Exception exc) { |
| } |
| locked = true; |
| } |
| |
| private synchronized void unlock() { |
| locked = false; |
| group = null; |
| runnable = null; |
| name = null; |
| if (waiting > 0) |
| notifyAll(); |
| } |
| } |