All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.zeromq.ZLoop Maven / Gradle / Ivy

Go to download

The 0MQ lightweight messaging kernel is a library which extends the standard socket interfaces with features traditionally provided by specialised messaging middleware products. 0MQ sockets provide an abstraction of asynchronous message queues, multiple messaging patterns, message filtering (subscriptions), seamless access to multiple transport protocols and more. This package contains the Java Bindings for ZeroMQ.

The newest version!
/*
    Copyright (c) 1991-2011 iMatix Corporation 
    Copyright other contributors as noted in the AUTHORS file.

    This file is part of 0MQ.

    0MQ is free software; you can redistribute it and/or modify it under
    the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    0MQ is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see .
 */
package org.zeromq;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.zeromq.ZMQ.PollItem;
import org.zeromq.ZMQ.Poller;

/**
 * The ZLoop class provides an event-driven reactor pattern. The reactor handles zmq.PollItem items (pollers or writers,
 * sockets or fds), and once-off or repeated timers. Its resolution is 1 msec. It uses a tickless timer to reduce CPU
 * interrupts in inactive processes.
 */

public class ZLoop {

    public static interface IZLoopHandler {
        public int handle(ZLoop loop, PollItem item, Object arg);
    }

    private class SPoller {
        PollItem item;
        IZLoopHandler handler;
        Object arg;
        int errors; // If too many errors, kill poller

        protected SPoller(PollItem item, IZLoopHandler handler, Object arg) {
            this.item = item;
            this.handler = handler;
            this.arg = arg;
            errors = 0;
        }

    }

    ;

    private class STimer {
        int delay;
        int times;
        IZLoopHandler handler;
        Object arg;
        long when; // Clock time when alarm goes off

        public STimer(int delay, int times, IZLoopHandler handler, Object arg) {
            this.delay = delay;
            this.times = times;
            this.handler = handler;
            this.arg = arg;
            this.when = -1;
        }

    }

    private final List pollers; // List of poll items
    private final List timers; // List of timers
    private int pollSize; // Size of poll set
    private Poller pollset; // zmq_poll set
    private SPoller[] pollact; // Pollers for this poll set
    private boolean dirty; // True if pollset needs rebuilding
    private boolean verbose; // True if verbose tracing wanted
    private final List zombies; // List of timers to kill
    private final List newTimers; // List of timers to add

    public ZLoop() {
        pollers = new ArrayList();
        timers = new ArrayList();
        zombies = new ArrayList();
        newTimers = new ArrayList();
    }

    public void destroy() {
        // do nothing
    }

    // We hold an array of pollers that matches the pollset, so we can
    // register/cancel pollers orthogonally to executing the pollset
    // activity on pollers. Returns 0 on success, -1 on failure.

    private void rebuild() {
        pollact = null;

        pollSize = pollers.size();
        pollset = new Poller(pollSize);

        pollact = new SPoller[pollSize];

        int itemNbr = 0;
        for (SPoller poller : pollers) {
            pollset.register(poller.item);
            pollact[itemNbr] = poller;
            itemNbr++;
        }
        dirty = false;
    }

    private long ticklessTimer() {
        // Calculate tickless timer, up to 1 hour
        long tickless = System.currentTimeMillis() + 1000 * 3600;
        for (STimer timer : timers) {
            if (timer.when == -1)
                timer.when = timer.delay + System.currentTimeMillis();
            if (tickless > timer.when)
                tickless = timer.when;
        }
        long timeout = tickless - System.currentTimeMillis();
        if (timeout < 0)
            timeout = 0;
        if (verbose)
            System.out.printf("I: zloop: polling for %d msec\n", timeout);
        return timeout;
    }

    // --------------------------------------------------------------------------
    // Register pollitem with the reactor. When the pollitem is ready, will call
    // the handler, passing the arg. Returns 0 if OK, -1 if there was an error.
    // If you register the pollitem more than once, each instance will invoke its
    // corresponding handler.

    public int addPoller(PollItem item_, IZLoopHandler handler, Object arg) {

        PollItem item = item_;
        if (item.getRawSocket() == null && item.getSocket() == null)
            return -1;

        SPoller poller = new SPoller(item_, handler, arg);
        pollers.add(poller);

        dirty = true;
        if (verbose)
            System.out.printf("I: zloop: register %s poller (%s, %s)\n", item.getSocket() != null ? item.getSocket()
                    .getType() : "RAW", item.getSocket(), item.getRawSocket());
        return 0;
    }

    // --------------------------------------------------------------------------
    // Cancel a pollitem from the reactor, specified by socket or FD. If both
    // are specified, uses only socket. If multiple poll items exist for same
    // socket/FD, cancels ALL of them.

    public void removePoller(PollItem item_) {
        PollItem item = item_;

        Iterator it = pollers.iterator();
        while (it.hasNext()) {
            SPoller p = it.next();
            if (item.equals(p.item)) {
                it.remove();
                dirty = true;
            }
        }
        if (verbose)
            System.out.printf("I: zloop: cancel %s poller (%s, %s)", item.getSocket() != null ? item.getSocket()
                    .getType() : "RAW", item.getSocket(), item.getRawSocket());

    }

    // --------------------------------------------------------------------------
    // Register a timer that expires after some delay and repeats some number of
    // times. At each expiry, will call the handler, passing the arg. To
    // run a timer forever, use 0 times. Returns 0 if OK, -1 if there was an
    // error.

    public int addTimer(int delay, int times, IZLoopHandler handler, Object arg) {
        STimer timer = new STimer(delay, times, handler, arg);

        // We cannot touch self->timers because we may be executing that
        // from inside the poll loop. So, we hold the new timer on the newTimers
        // list, and process that list when we're done executing timers.
        newTimers.add(timer);
        if (verbose)
            System.out.printf("I: zloop: register timer delay=%d times=%d\n", delay, times);

        return 0;
    }

    // --------------------------------------------------------------------------
    // Cancel all timers for a specific argument (as provided in zloop_timer)
    // Returns 0 on success.

    public int removeTimer(Object arg) {
        assert (arg != null);

        // We cannot touch self->timers because we may be executing that
        // from inside the poll loop. So, we hold the arg on the zombie
        // list, and process that list when we're done executing timers.
        zombies.add(arg);
        if (verbose)
            System.out.printf("I: zloop: cancel timer\n");

        return 0;
    }

    // --------------------------------------------------------------------------
    // Set verbose tracing of reactor on/off
    public void verbose(boolean verbose) {
        this.verbose = verbose;
    }

    // --------------------------------------------------------------------------
    // Start the reactor. Takes control of the thread and returns when the 0MQ
    // context is terminated or the process is interrupted, or any event handler
    // returns -1. Event handlers may register new sockets and timers, and
    // cancel sockets. Returns 0 if interrupted, -1 if cancelled by a
    // handler, positive on internal error

    public int start() {
        int rc = 0;

        timers.addAll(newTimers);
        newTimers.clear();

        // Recalculate all timers now
        for (STimer timer : timers) {
            timer.when = timer.delay + System.currentTimeMillis();
        }

        // Main reactor loop
        while (!Thread.currentThread().isInterrupted()) {
            if (dirty) {
                // If s_rebuild_pollset() fails, break out of the loop and
                // return its error
                rebuild();
            }
            long wait = ticklessTimer();

            rc = pollset.poll(wait);

            if (rc == -1) {
                if (verbose)
                    System.out.printf("I: zloop: interrupted (%d)\n", rc);
                rc = 0;
                break; // Context has been shut down
            }
            // Handle any timers that have now expired
            Iterator it = timers.iterator();
            while (it.hasNext()) {
                STimer timer = it.next();
                if (System.currentTimeMillis() >= timer.when && timer.when != -1) {
                    if (verbose)
                        System.out.println("I: zloop: call timer handler");
                    rc = timer.handler.handle(this, null, timer.arg);
                    if (rc == -1)
                        break; // Timer handler signalled break
                    if (timer.times != 0 && --timer.times == 0) {
                        it.remove();
                    } else
                        timer.when = timer.delay + System.currentTimeMillis();
                }
            }
            if (rc == -1)
                break; // Some timer signalled break from the reactor loop

            // Handle any pollers that are ready
            for (int itemNbr = 0; itemNbr < pollSize; itemNbr++) {
                SPoller poller = pollact[itemNbr];
                if (pollset.getItem(itemNbr).isError()) {
                    if (verbose)
                        System.out.printf("I: zloop: can't poll %s socket (%s, %s)",
                                poller.item.getSocket() != null ? poller.item.getSocket().getType() : "RAW",
                                poller.item.getSocket(), poller.item.getRawSocket());
                    // Give handler one chance to handle error, then kill
                    // poller because it'll disrupt the reactor otherwise.
                    if (poller.errors++ > 0) {
                        removePoller(poller.item);
                    }
                } else
                    poller.errors = 0; // A non-error happened

                if (pollset.getItem(itemNbr).readyOps() > 0) {
                    if (verbose)
                        System.out.printf("I: zloop: call %s socket handler (%s, %s)\n",
                                poller.item.getSocket() != null ? poller.item.getSocket().getType() : "RAW",
                                poller.item.getSocket(), poller.item.getRawSocket());
                    rc = poller.handler.handle(this, poller.item, poller.arg);
                    if (rc == -1)
                        break; // Poller handler signalled break
                }
            }

            // Now handle any timer zombies
            // This is going to be slow if we have many zombies
            for (Object arg : zombies) {
                it = timers.iterator();
                while (it.hasNext()) {
                    STimer timer = it.next();
                    if (timer.arg == arg) {
                        it.remove();
                    }
                }
            }
            // Now handle any new timers added inside the loop
            timers.addAll(newTimers);
            newTimers.clear();

            if (rc == -1)
                break;
        }

        return rc;
    }

}