/* * Copyright (c) 2008-2017, Hazelcast, Inc. 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 com.hazelcast.internal.networking.spinning; import com.hazelcast.internal.networking.ChannelConnection; import com.hazelcast.util.ThreadUtil; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import static java.lang.System.arraycopy; import static java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater; public class SpinningOutputThread extends Thread { private static final ChannelWriters SHUTDOWN = new ChannelWriters(); private static final AtomicReferenceFieldUpdater<SpinningOutputThread, ChannelWriters> CONNECTION_HANDLERS = newUpdater(SpinningOutputThread.class, ChannelWriters.class, "channelWriters"); private volatile ChannelWriters channelWriters = new ChannelWriters(); public SpinningOutputThread(String hzName) { super(ThreadUtil.createThreadName(hzName, "out-thread")); } void addConnection(ChannelConnection connection) { SpinningChannelWriter writer = (SpinningChannelWriter) connection.getChannelWriter(); for (; ; ) { ChannelWriters current = channelWriters; if (current == SHUTDOWN) { return; } int length = current.writers.length; SpinningChannelWriter[] newWriters = new SpinningChannelWriter[length + 1]; arraycopy(current.writers, 0, newWriters, 0, length); newWriters[length] = writer; ChannelWriters update = new ChannelWriters(newWriters); if (CONNECTION_HANDLERS.compareAndSet(this, current, update)) { return; } } } void removeConnection(ChannelConnection connection) { SpinningChannelWriter writer = (SpinningChannelWriter) connection.getChannelWriter(); for (; ; ) { ChannelWriters current = channelWriters; if (current == SHUTDOWN) { return; } int indexOf = current.indexOf(writer); if (indexOf == -1) { return; } int length = current.writers.length; SpinningChannelWriter[] newWriters = new SpinningChannelWriter[length - 1]; int destIndex = 0; for (int sourceIndex = 0; sourceIndex < length; sourceIndex++) { if (sourceIndex != indexOf) { newWriters[destIndex] = current.writers[sourceIndex]; destIndex++; } } ChannelWriters update = new ChannelWriters(newWriters); if (CONNECTION_HANDLERS.compareAndSet(this, current, update)) { return; } } } public void shutdown() { channelWriters = SHUTDOWN; interrupt(); } @Override public void run() { for (; ; ) { ChannelWriters writers = channelWriters; if (writers == SHUTDOWN) { return; } for (SpinningChannelWriter writer : writers.writers) { try { writer.write(); } catch (Throwable t) { writer.onFailure(t); } } } } private static class ChannelWriters { final SpinningChannelWriter[] writers; ChannelWriters() { this(new SpinningChannelWriter[0]); } ChannelWriters(SpinningChannelWriter[] writers) { this.writers = writers; } public int indexOf(SpinningChannelWriter handler) { for (int k = 0; k < writers.length; k++) { if (writers[k] == handler) { return k; } } return -1; } } }