class STimer
    {
        final int           delay;
        int           times;
        final IZLoopHandler handler;
        final 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 Context       context;   //  Context managing the pollers.
    private final List<SPoller> pollers;   //  List of poll items
    private final List<STimer>  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<Object>  zombies;   //  List of timers to kill
    private final List<STimer>  newTimers; //  List of timers to add

    public ZLoop(Context context)
    {
        Objects.requireNonNull(context, "Context has to be supplied for ZLoop");
        this.context = context;

        pollers = new ArrayList<>();
        timers = new ArrayList<>();
        zombies = new ArrayList<>();
        newTimers = new ArrayList<>();
    }

    public ZLoop(ZContext ctx)
    {
        this(ctx.getContext());
    }

    /**
     * @deprecated no-op behaviour
     */
    @Deprecated
    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();
        if (pollset != null) {
            pollset.close();
        }
        pollset = context.poller(pollSize);
        assert (pollset != null);

        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 pollItem, IZLoopHandler handler, Object arg)
    {<FILL_FUNCTION_BODY>

        if (pollItem.getRawSocket() == null && pollItem.getSocket() == null) {
            return -1;
        }

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

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