/**
* Copyright 2014 Ricardo Padilha
*
* 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 net.dsys.snio.impl.handler;
import static net.dsys.snio.impl.handler.ExecutionType.DECOUPLED;
import static net.dsys.snio.impl.handler.ExecutionType.ZERO_COPY;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nonnull;
import net.dsys.commons.api.exception.Bug;
import net.dsys.commons.api.lang.Cleaner;
import net.dsys.commons.api.lang.Copier;
import net.dsys.commons.api.lang.Factory;
import net.dsys.commons.api.lang.Interruptible;
import net.dsys.snio.api.buffer.InterruptedByClose;
import net.dsys.snio.api.buffer.MessageBufferConsumer;
import net.dsys.snio.api.handler.MessageConsumer;
/**
* This implementation copies the information to be processed from the input
* buffer locally and releases it before processing it. This allows a measure of
* decoupling between input and output buffers. The down-side is that it requires
* an additional copy of the data to be processed.
*
* @author Ricardo Padilha
*/
final class ConsumerThread<T> implements Interruptible {
private final ExecutionType type;
private final MessageBufferConsumer<T> in;
private final MessageConsumer<T> consumer;
private final T holder;
private final Copier<T> copier;
private final Cleaner<T> cleaner;
private final AtomicBoolean interrupted;
ConsumerThread(@Nonnull final MessageBufferConsumer<T> in,
@Nonnull final MessageConsumer<T> consumer, @Nonnull final T holder,
@Nonnull final Copier<T> copier, @Nonnull final Cleaner<T> cleaner) {
if (in == null) {
throw new NullPointerException("in == null");
}
if (consumer == null) {
throw new NullPointerException("consumer == null");
}
if (holder == null) {
throw new NullPointerException("holder == null");
}
if (copier == null) {
throw new NullPointerException("copier == null");
}
if (cleaner == null) {
throw new NullPointerException("cleaner == null");
}
this.type = DECOUPLED;
this.in = in;
this.consumer = consumer;
this.holder = holder;
this.copier = copier;
this.cleaner = cleaner;
this.interrupted = new AtomicBoolean();
}
ConsumerThread(@Nonnull final MessageBufferConsumer<T> in, @Nonnull final MessageConsumer<T> consumer) {
if (in == null) {
throw new NullPointerException("in == null");
}
if (consumer == null) {
throw new NullPointerException("consumer == null");
}
this.type = ZERO_COPY;
this.in = in;
this.consumer = consumer;
this.holder = null;
this.copier = null;
this.cleaner = null;
this.interrupted = new AtomicBoolean();
}
static <T> ConsumerThreadFactory<T> createAsyncFactory(@Nonnull final Factory<T> factory,
@Nonnull final Copier<T> copier, @Nonnull final Cleaner<T> cleaner) {
return new ConsumerThreadFactory<T>() {
@Override
public Runnable newInstance(final MessageBufferConsumer<T> in, final MessageConsumer<T> consumer) {
return new ConsumerThread<>(in, consumer, factory.newInstance(), copier, cleaner);
}
};
}
static <T> ConsumerThreadFactory<T> createSyncFactory() {
return new ConsumerThreadFactory<T>() {
@Override
public Runnable newInstance(final MessageBufferConsumer<T> in, final MessageConsumer<T> consumer) {
return new ConsumerThread<>(in, consumer);
}
};
}
/**
* {@inheritDoc}
*/
@Override
public void interrupt() {
interrupted.lazySet(true);
}
/**
* {@inheritDoc}
*/
@Override
public void run() {
switch (type) {
case DECOUPLED: {
asyncRun();
break;
}
case ZERO_COPY: {
syncRun();
break;
}
default: {
throw new Bug("Unsuppported ThreadType: " + type);
}
}
}
private void asyncRun() {
final T holder = this.holder;
Object attachment;
while (!Thread.interrupted() && !interrupted.get()) {
try {
final long sequence = in.acquire();
try {
final T value = in.get(sequence);
copier.copy(value, holder);
attachment = in.attachment(sequence);
} finally {
in.release(sequence);
}
consumer.consume(holder, attachment);
cleaner.clear(holder);
} catch (final InterruptedByClose e) {
return;
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
return;
} catch (final Throwable t) {
System.err.println("Uncaught MessageHandler exception: " + t.getLocalizedMessage());
t.printStackTrace();
}
}
}
private void syncRun() {
while (!Thread.interrupted() && !interrupted.get()) {
try {
final long sequence = in.acquire();
try {
final T value = in.get(sequence);
final Object attachment = in.attachment(sequence);
consumer.consume(value, attachment);
} finally {
in.release(sequence);
}
} catch (final InterruptedByClose e) {
return;
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
return;
} catch (final Throwable t) {
System.err.println("Uncaught MessageHandler exception: " + t.getLocalizedMessage());
t.printStackTrace();
}
}
}
}