All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.fusesource.hawtdispatch.internal.NioDispatchSource Maven / Gradle / Ivy

There is a newer version: 1.22
Show newest version
/**
 * Copyright (C) 2009, Progress Software Corporation and/or its
 * subsidiaries or affiliates.  All rights reserved.
 *
 * 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
 */
package org.fusesource.hawtdispatch.internal;

import org.fusesource.hawtdispatch.*;

import java.io.IOException;
import java.nio.channels.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.concurrent.atomic.AtomicBoolean;

import static java.lang.String.format;
import static org.fusesource.hawtdispatch.DispatchQueue.QueueType.THREAD_QUEUE;

/**
 * 

* Implements the DispatchSource interface. *

*

* Description: An NioDispatchSource is associated with one SelectableChannel * but supports being registered on selectors associated with different thread. * Usually just one at time tho. *

* * @author cmacnaug * @author Hiram Chirino */ final public class NioDispatchSource extends AbstractDispatchObject implements DispatchSource { public static final boolean DEBUG = false; final SelectableChannel channel; volatile DispatchQueue selectorQueue; final AtomicBoolean canceled = new AtomicBoolean(); final int interestOps; Runnable cancelHandler; Runnable eventHandler; // These fields are only accessed by the ioManager's thread. public static class KeyState { int readyOps; SelectionKey key; NioAttachment attachment; @Override public String toString() { return "{ready: "+opsToString(readyOps)+" }"; } } private static String opsToString(int ops) { ArrayList sb = new ArrayList(); if( (ops & SelectionKey.OP_ACCEPT) != 0) { sb.add("ACCEPT"); } if( (ops & SelectionKey.OP_CONNECT) != 0) { sb.add("CONNECT"); } if( (ops & SelectionKey.OP_READ) != 0) { sb.add("READ"); } if( (ops & SelectionKey.OP_WRITE) != 0) { sb.add("WRITE"); } return sb.toString(); } final ThreadLocal keyState = new ThreadLocal(); public NioDispatchSource(HawtDispatcher dispatcher, SelectableChannel channel, int interestOps, DispatchQueue targetQueue) { if( interestOps == 0 ) { throw new IllegalArgumentException("invalid interest ops"); } this.channel = channel; this.selectorQueue = pickThreadQueue(dispatcher, targetQueue); this.interestOps = interestOps; this.suspended.incrementAndGet(); this.setTargetQueue(targetQueue); } static private DispatchQueue pickThreadQueue(HawtDispatcher dispatcher, DispatchQueue targetQueue) { // Try to select a thread queue associated /w the target if available.. DispatchQueue selectorQueue = targetQueue; while( selectorQueue.getQueueType()!=THREAD_QUEUE && selectorQueue.getTargetQueue() !=null ) { selectorQueue = selectorQueue.getTargetQueue(); } // otherwise.. pick the thread queue with the fewest registered selection // keys. if( selectorQueue.getQueueType()!=THREAD_QUEUE ) { WorkerThread[] threads = dispatcher.DEFAULT_QUEUE.workers.getThreads(); WorkerThread min = threads[0]; int minSize = min.getNioManager().getSelector().keys().size(); for( int i=1; i < threads.length; i++) { int s = threads[i].getNioManager().getSelector().keys().size(); if( s < minSize ) { minSize = s; min = threads[i]; } } selectorQueue = min.getDispatchQueue(); } return selectorQueue; } @Override protected void onStartup() { if( eventHandler==null ) { throw new IllegalArgumentException("eventHandler must be set"); } register_on(selectorQueue); } public void cancel() { if( canceled.compareAndSet(false, true) ) { selectorQueue.execute(new Runnable(){ public void run() { internal_cancel(); } }); } } void internal_cancel() { key_cancel(); if( cancelHandler!=null ) { targetQueue.execute(cancelHandler); } } private void key_cancel() { // Deregister... KeyState state = keyState.get(); if( state==null ) { return; } debug("canceling source"); state.attachment.sources.remove(this); if( state.attachment.sources.isEmpty() ) { debug("canceling key."); // This will make sure that the key is removed // from the ioManager. state.key.cancel(); // Running a select to remove the canceled key. Selector selector = WorkerThread.currentWorkerThread().getNioManager().getSelector(); try { selector.selectNow(); } catch (CancelledKeyException ignore) { } catch (IOException e) { debug(e, "Error canceling"); } } debug("Canceled selector on "+WorkerThread.currentWorkerThread().getDispatchQueue().getLabel() ); keyState.remove(); } private void register_on(final DispatchQueue queue) { queue.execute(new Runnable(){ public void run() { assert keyState.get()==null; if(DEBUG) debug("Registering interest %s", opsToString(interestOps)); Selector selector = WorkerThread.currentWorkerThread().getNioManager().getSelector(); try { KeyState state = new KeyState(); state.key = channel.keyFor(selector); if( state.key==null ) { state.key = channel.register(selector, interestOps); state.attachment = new NioAttachment(); state.key.attach(state.attachment); } else { state.attachment = (NioAttachment)state.key.attachment(); } state.attachment.sources.add(NioDispatchSource.this); keyState.set(state); try { // the key could be canceled by now.. state.key.interestOps(state.key.interestOps()|interestOps); } catch (CancelledKeyException e) { state.attachment.cancel(state.key); } } catch (ClosedChannelException e) { debug(e, "could not register with selector"); } debug("Registered"); } }); } public void fire(final int readyOps) { final KeyState state = keyState.get(); if( state==null ) { return; } state.readyOps |= readyOps; if( state.readyOps!=0 && !isSuspended()&& !isCanceled() ) { state.readyOps = 0; targetQueue.execute(new Runnable() { public void run() { if( !isSuspended() && !isCanceled()) { if(DEBUG) debug("fired %s", opsToString(readyOps)); try { eventHandler.run(); } catch (Throwable e) { e.printStackTrace(); } updateInterest(); } } }); } } private void updateInterest() { if( isCurrent(selectorQueue) ) { if( !isSuspended() && !isCanceled() ) { if(DEBUG) debug("adding interest: %s", opsToString(interestOps)); KeyState state = keyState.get(); if( state==null ) { return; } if( state.key.isValid() ) { state.key.interestOps(state.key.interestOps()|interestOps); } } } else { selectorQueue.execute(new Runnable(){ public void run() { if( !isSuspended() && !isCanceled() ) { if(DEBUG) debug("adding interest: %d", opsToString(interestOps)); KeyState state = keyState.get(); if( state==null ) { return; } if( state.key.isValid() ) { state.key.interestOps(state.key.interestOps()|interestOps); } } } }); } } private boolean isCurrent(DispatchQueue q) { WorkerThread thread = WorkerThread.currentWorkerThread(); if( thread == null ) return false; return thread.getDispatchQueue() == q; } @Override protected void onSuspend() { debug("onSuspend"); super.onSuspend(); } @Override protected void onResume() { debug("onResume"); if( isCurrent(selectorQueue) ) { KeyState state = keyState.get(); if( state==null || state.readyOps==0 ) { updateInterest(); } else { fire(state.readyOps); } } else { selectorQueue.execute(new Runnable(){ public void run() { KeyState state = keyState.get(); if( state==null || state.readyOps==0 ) { updateInterest(); } else { fire(interestOps); } } }); } } public boolean isCanceled() { return canceled.get(); } public void setCancelHandler(Runnable cancelHandler) { this.cancelHandler = cancelHandler; } public void setEventHandler(Runnable eventHandler) { this.eventHandler = eventHandler; } public Void getData() { return null; } public void setTargetQueue(DispatchQueue next) { super.setTargetQueue(next); // The target thread queue might be different. Optimize by switching the selector to it. // Do we need to switch selector threads? DispatchQueue queue = next; while( queue.getQueueType()!=THREAD_QUEUE && queue.getTargetQueue() !=null ) { queue = queue.getTargetQueue(); } if( queue.getQueueType()==THREAD_QUEUE && queue!=selectorQueue ) { DispatchQueue previous = selectorQueue; debug("Switching to "+queue.getLabel()); register_on(queue); selectorQueue = queue; if( previous!=null ) { previous.execute(new Runnable(){ public void run() { key_cancel(); } }); } } } protected void debug(String str, Object... args) { if (DEBUG) { String thread = Thread.currentThread().getName(); String target =""; if( Dispatch.getCurrentQueue()!=null ) { target = Dispatch.getCurrentQueue().getLabel() + " | "; } System.out.println(format("DEBUG | %s | #%0#10x | %s%s", thread, System.identityHashCode(this), target, format(str, args))); } } protected void debug(Throwable thrown, String str, Object... args) { if (DEBUG) { if (str != null) { debug(str, args); } if (thrown != null) { thrown.printStackTrace(); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy