/*
* 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 SpinningInputThread extends Thread {
private static final ChannelReaders SHUTDOWN = new ChannelReaders();
private static final AtomicReferenceFieldUpdater<SpinningInputThread, ChannelReaders> CONNECTION_HANDLERS
= newUpdater(SpinningInputThread.class, ChannelReaders.class, "channelReaders");
private volatile ChannelReaders channelReaders = new ChannelReaders();
public SpinningInputThread(String hzName) {
super(ThreadUtil.createThreadName(hzName, "in-thread"));
}
public void addConnection(ChannelConnection connection) {
SpinningChannelReader reader = (SpinningChannelReader) connection.getChannelReader();
for (; ; ) {
ChannelReaders current = channelReaders;
if (current == SHUTDOWN) {
return;
}
int length = current.readers.length;
SpinningChannelReader[] newReaders = new SpinningChannelReader[length + 1];
arraycopy(current.readers, 0, newReaders, 0, length);
newReaders[length] = reader;
ChannelReaders update = new ChannelReaders(newReaders);
if (CONNECTION_HANDLERS.compareAndSet(this, current, update)) {
return;
}
}
}
public void removeConnection(ChannelConnection connection) {
SpinningChannelReader reader = (SpinningChannelReader) connection.getChannelReader();
for (; ; ) {
ChannelReaders current = channelReaders;
if (current == SHUTDOWN) {
return;
}
int indexOf = current.indexOf(reader);
if (indexOf == -1) {
return;
}
int length = current.readers.length;
SpinningChannelReader[] newReaders = new SpinningChannelReader[length - 1];
int destIndex = 0;
for (int sourceIndex = 0; sourceIndex < length; sourceIndex++) {
if (sourceIndex != indexOf) {
newReaders[destIndex] = current.readers[sourceIndex];
destIndex++;
}
}
ChannelReaders update = new ChannelReaders(newReaders);
if (CONNECTION_HANDLERS.compareAndSet(this, current, update)) {
return;
}
}
}
public void shutdown() {
channelReaders = SHUTDOWN;
interrupt();
}
@Override
public void run() {
for (; ; ) {
ChannelReaders readers = channelReaders;
if (readers == SHUTDOWN) {
return;
}
for (SpinningChannelReader reader : readers.readers) {
try {
reader.read();
} catch (Throwable t) {
reader.onFailure(t);
}
}
}
}
static class ChannelReaders {
final SpinningChannelReader[] readers;
public ChannelReaders() {
this(new SpinningChannelReader[0]);
}
public ChannelReaders(SpinningChannelReader[] readers) {
this.readers = readers;
}
public int indexOf(SpinningChannelReader readHandler) {
for (int k = 0; k < readers.length; k++) {
if (readers[k] == readHandler) {
return k;
}
}
return -1;
}
}
}