package cz.cuni.mff.d3s.been.socketworks.twoway;
import java.util.List;
import org.jeromq.ZMQ.Poller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import cz.cuni.mff.d3s.been.mq.Context;
import cz.cuni.mff.d3s.been.mq.MessagingException;
import cz.cuni.mff.d3s.been.mq.ZMQContext;
/**
* A thread that helps pipeline requests (events) on multiple connected 0MQ
* sockets using a single poll-driven loop.
*
* This class serves to unify the logic of the particular socket combination
* into one visible whole.
*
* @author darklight
*
*/
final class PollPipeline extends Thread {
private static final Logger log = LoggerFactory.getLogger(PollPipeline.class);
private static final long POLL_TIMEOUT = 300;
private static final int NOFLAGS = 0;
private final List<PollPartaker> partakers;
private final ZMQContext ctx;
private final FrameForwardMapper forwards;
private volatile boolean keepRunning = true;
private PollPipeline(ZMQContext context, List<PollPartaker> partakers, FrameForwardMapper forwards) {
this.ctx = context;
this.partakers = partakers;
this.forwards = forwards;
}
/**
* Create a 0MQ poll pipeline.
*
* @param partakers
* Participants of the polling
*
* @return The poll pipeline
*
* @throws MessagingException
* If an invalid context is in play while the pipeline gets created
*/
public static
PollPipeline
create(List<PollPartaker> partakers, FrameForwardMapper forwards) throws MessagingException {
ZMQContext ctx = Context.getReference();
return new PollPipeline(ctx, partakers, forwards);
}
public void doStop() {
keepRunning = false;
}
@Override
public void run() {
final StringBuilder sb = new StringBuilder();
boolean first = true;
for (PollPartaker partaker : partakers) {
if (!first) {
sb.append(", ");
}
first = false;
sb.append(partaker.getClass().getSimpleName());
}
setName(String.format("%s(%s)", getClass().getSimpleName(), sb.toString()));
super.run();
Poller poller = null;
try {
poller = ctx.poller(partakers.size());
} catch (MessagingException e) {
log.error("Cannot create ZMQ poller", e);
return;
}
for (PollPartaker partaker : partakers) {
poller.register(partaker.getSocket(), partaker.getPollType());
}
while (keepRunning) {
// Set up a timeout here so if this thread gets a stop request,
// we have a chance to find out.
if (poller.poll(POLL_TIMEOUT) == 0) {
continue;
}
// Poll found something, select the partaker and have him handle his thing.
for (int i = 0; i < partakers.size(); ++i) {
log.debug("Got something");
if (poller.pollin(i)) {
log.debug("It belongs to {}", partakers.get(i));
// Make sure to crunch the entire multi-frame message.
final Frames frames = Frames.create();
do {
frames.add(poller.getSocket(i).recv(NOFLAGS));
// TODO check whether the recv can't eat two consecutive messages within one cycle
} while (poller.getSocket(i).hasReceiveMore());
try {
partakers.get(i).receiveFromWire(frames);
} catch (MessagingException e) {
log.error("Failed to delegate frames {}", frames.toString(), e);
}
}
}
}
log.debug("Poll pipeline about to terminate");
for (PollPartaker partaker : partakers) {
poller.unregister(partaker.getSocket());
}
log.debug("Poll pipeline about to release ZMQ context");
try {
ctx.term();
} catch (MessagingException e) {
log.error("Failed to terminate ZMQContext", e);
}
log.debug("Poll pipeline terminated");
}
}