/** * Copyright 2007-2015, Kaazing Corporation. 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 * limitations under the License. */ package org.kaazing.k3po.driver.internal.netty.bootstrap.agrona; import static java.lang.Thread.currentThread; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.jboss.netty.channel.Channels.fireChannelBound; import static org.jboss.netty.channel.Channels.fireChannelClosed; import static org.jboss.netty.channel.Channels.fireChannelUnbound; import java.util.Deque; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.CountDownLatch; import org.jboss.netty.channel.ChannelFactory; import org.jboss.netty.channel.ChannelFuture; import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.channel.ChannelPipelineFactory; import org.jboss.netty.channel.Channels; import org.kaazing.k3po.driver.internal.netty.channel.agrona.AgronaChannelAddress; import org.agrona.concurrent.BackoffIdleStrategy; import org.agrona.concurrent.IdleStrategy; public final class AgronaServerBoss implements Runnable { private static final long MAX_PARK_NS = MILLISECONDS.toNanos(100L); private static final long MIN_PARK_NS = MILLISECONDS.toNanos(1L); private static final int MAX_YIELDS = 30; private static final int MAX_SPINS = 20; private final Deque<Runnable> taskQueue; private final CountDownLatch shutdownLatch = new CountDownLatch(1); private volatile boolean shutdown; AgronaServerBoss() { this.taskQueue = new ConcurrentLinkedDeque<>(); } void bind( final AgronaServerChannel channel, final AgronaChannelAddress localAddress, final ChannelFuture future) { registerTask(new BindTask(channel, localAddress, future)); } void unbind( final AgronaServerChannel channel, final ChannelFuture future) { registerTask(new UnbindTask(channel, future)); } void close(AgronaServerChannel channel) { registerTask(new CloseTask(channel)); } @Override public void run() { final IdleStrategy idleStrategy = new BackoffIdleStrategy(MAX_SPINS, MAX_YIELDS, MIN_PARK_NS, MAX_PARK_NS); while (!shutdown) { int workCount = 0; workCount += executeTasks(); idleStrategy.idle(workCount); } shutdownLatch.countDown(); } public void shutdown() { shutdown = true; try { shutdownLatch.await(); } catch (InterruptedException e) { currentThread().interrupt(); } } private int executeTasks() { int workCount = 0; Runnable task; while ((task = taskQueue.poll()) != null) { task.run(); workCount++; } return workCount; } private void registerTask(Runnable task) { taskQueue.offer(task); } private static final class BindTask implements Runnable { private final AgronaServerChannel serverChannel; private final AgronaChannelAddress localAddress; private final ChannelFuture bindFuture; public BindTask( AgronaServerChannel serverChannel, AgronaChannelAddress localAddress, ChannelFuture bindFuture) { this.serverChannel = serverChannel; this.bindFuture = bindFuture; this.localAddress = localAddress; } @Override public void run() { serverChannel.setLocalAddress(localAddress); serverChannel.setBound(); fireChannelBound(serverChannel, serverChannel.getLocalAddress()); try { ChannelPipelineFactory pipelineFactory = serverChannel.getConfig().getPipelineFactory(); ChannelPipeline pipeline = pipelineFactory.getPipeline(); bindFuture.setSuccess(); // fire child channel opened ChannelFactory channelFactory = serverChannel.getFactory(); AgronaChildChannelSink channelSink = new AgronaChildChannelSink(); AgronaChildChannel childChannel = new AgronaChildChannel(serverChannel, channelFactory, pipeline, channelSink, serverChannel.worker); childChannel.setLocalAddress(serverChannel.getLocalAddress()); fireChannelBound(childChannel, childChannel.getLocalAddress()); childChannel.setRemoteAddress(childChannel.getLocalAddress()); Channels.fireChannelConnected(childChannel, childChannel.getRemoteAddress()); childChannel.worker.register(childChannel); } catch (Exception e) { bindFuture.setFailure(e); } } } private static final class UnbindTask implements Runnable { private final AgronaServerChannel serverChannel; private final ChannelFuture unbindFuture; public UnbindTask( AgronaServerChannel serverChannel, ChannelFuture unbindFuture) { this.serverChannel = serverChannel; this.unbindFuture = unbindFuture; } @Override public void run() { fireChannelUnbound(serverChannel); unbindFuture.setSuccess(); } } private static final class CloseTask implements Runnable { private final AgronaServerChannel serverChannel; public CloseTask(AgronaServerChannel serverChannel) { this.serverChannel = serverChannel; } @Override public void run() { serverChannel.setClosed(); fireChannelUnbound(serverChannel); fireChannelClosed(serverChannel); serverChannel.getCloseFuture().setSuccess(); } } }