/** * Copyright (C) 2012 FuseSource, Inc. * http://fusesource.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.fusesource.hawtdispatch.internal; import org.fusesource.hawtdispatch.DispatchQueue; import org.fusesource.hawtdispatch.Metrics; import org.fusesource.hawtdispatch.Task; import org.fusesource.hawtdispatch.TaskWrapper; import java.util.LinkedList; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; /** * * @author <a href="http://hiramchirino.com">Hiram Chirino</a> */ public class SerialDispatchQueue extends AbstractDispatchObject implements HawtDispatchQueue { protected volatile String label; protected final AtomicBoolean triggered = new AtomicBoolean(); protected final ConcurrentLinkedQueue<Task> externalQueue = new ConcurrentLinkedQueue<Task>(); private final LinkedList<Task> localQueue = new LinkedList<Task>(); private final LinkedList<Task> sourceQueue= new LinkedList<Task>(); private final ThreadLocal<Boolean> executing = new ThreadLocal<Boolean>(); private MetricsCollector metricsCollector = InactiveMetricsCollector.INSTANCE; private boolean profile=false; public SerialDispatchQueue(String label) { this.label = label; } public void execute(Task task) { assert task != null; enqueue(metricsCollector.track(task)); } @Deprecated public void execute(final Runnable runnable) { execute(new TaskWrapper(runnable)); } @Deprecated() public void executeAfter(long delay, TimeUnit unit, Runnable runnable) { this.executeAfter(delay, unit, new TaskWrapper(runnable)); } public LinkedList<Task> getSourceQueue() { return sourceQueue; } private void enqueue(Task runnable) { // We can take a shortcut... if( executing.get()!=null ) { localQueue.add(runnable); } else { externalQueue.add(runnable); triggerExecution(); } } public void run() { checkCollector(); HawtDispatchQueue original = HawtDispatcher.CURRENT_QUEUE.get(); HawtDispatcher.CURRENT_QUEUE.set(this); executing.set(Boolean.TRUE); try { Task runnable; while( (runnable = externalQueue.poll())!=null ) { localQueue.add(runnable); } while(true) { if( isSuspended() ) { return; } runnable = localQueue.poll(); if( runnable==null ) { return; } try { runnable.run(); } catch (Throwable e) { Thread thread = Thread.currentThread(); thread.getUncaughtExceptionHandler().uncaughtException(thread, e); } } } finally { // Posts any deferred events. This ensures // the next events generated by this dispatch // queue are received in order. for (Runnable runnable : sourceQueue) { runnable.run(); } sourceQueue.clear(); executing.remove(); HawtDispatcher.CURRENT_QUEUE.set(original); triggered.set(false); boolean empty = externalQueue.isEmpty() && localQueue.isEmpty(); if( !isSuspended() && !empty) { triggerExecution(); } } } protected void triggerExecution() { if( triggered.compareAndSet(false, true) ) { getTargetQueue().execute(this); } } public String getLabel() { return label; } public void setLabel(String label) { this.label = label; } public boolean isExecuting() { return executing.get()!=null; } public void assertExecuting() { assert isExecuting() : getDispatcher().assertMessage(getLabel()); } @Override protected void onStartup() { triggerExecution(); } @Override protected void onResume() { triggerExecution(); } public QueueType getQueueType() { return QueueType.SERIAL_QUEUE; } public void executeAfter(long delay, TimeUnit unit, Task task) { getDispatcher().timerThread.addRelative(task, this, delay, unit); } public DispatchQueue createQueue(String label) { DispatchQueue rc = getDispatcher().createQueue(label); rc.setTargetQueue(this); return rc; } public HawtDispatcher getDispatcher() { HawtDispatchQueue target = getTargetQueue(); if (target ==null ) { throw new UnsupportedOperationException(); } return target.getDispatcher(); } public SerialDispatchQueue isSerialDispatchQueue() { return this; } public ThreadDispatchQueue isThreadDispatchQueue() { return null; } public GlobalDispatchQueue isGlobalDispatchQueue() { return null; } public void profile(boolean profile) { this.profile = profile; checkCollector(); } public boolean profile() { return this.profile; } private void checkCollector() { if( profile() || getDispatcher().profile() ) { if( metricsCollector == InactiveMetricsCollector.INSTANCE ) { metricsCollector = new ActiveMetricsCollector(this); getDispatcher().track(this); } } else { if( metricsCollector != InactiveMetricsCollector.INSTANCE ) { metricsCollector = InactiveMetricsCollector.INSTANCE; getDispatcher().untrack(this); } } } public Metrics metrics() { return metricsCollector.metrics(); } private int drains() { return getDispatcher().drains; } @Override public String toString() { if( label == null ) { return "serial queue"; } else { return "serial queue { label: \""+label+"\" }"; } } }