class ChannelForwardHolder
    {
        private final AtomicInteger handleSource = new AtomicInteger(0);
        private final Map<Integer, WeakReference<SelectableChannel>> map = new ConcurrentHashMap<>();
        // The WeakReference is empty when the reference is empty, so keep a reverse empty to clean the direct map.
        private final Map<WeakReference<SelectableChannel>, Integer> reversemap = new ConcurrentHashMap<>();
        private final ReferenceQueue<SelectableChannel> queue = new ReferenceQueue<>();
    }

    private ChannelForwardHolder forwardHolder = null;

    public Ctx()
    {
        active = true;
        terminating = false;
        reaper = null;
        slotCount = 0;
        slots = null;
        maxSockets = ZMQ.ZMQ_MAX_SOCKETS_DFLT;
        ioThreadCount = ZMQ.ZMQ_IO_THREADS_DFLT;
        threadFactory = this::createThread;

        ipv6 = false;
        blocky = true;
        slotSync = new ReentrantLock();
        endpointsSync = new ReentrantLock();
        optSync = new ReentrantLock();

        termMailbox = new Mailbox(this, "terminater", -1);

        emptySlots = new ArrayDeque<>();
        ioThreads = new ArrayList<>();
        sockets = new ArrayList<>();
        endpoints = new HashMap<>();
    }

    private void destroy() throws IOException
    {
        assert (sockets.isEmpty());

        for (IOThread it : ioThreads) {
            it.stop();
        }
        for (IOThread it : ioThreads) {
            it.close();
        }
        ioThreads.clear();

        selectorSync.lock();
        try {
            for (Selector selector : selectors) {
                if (selector != null) {
                    selector.close();
                }
            }
            selectors.clear();
        }
        finally {
            selectorSync.unlock();
        }

        //  Deallocate the reaper thread object.
        if (reaper != null) {
            reaper.close();
        }
        //  Deallocate the array of mailboxes. No special work is
        //  needed as mailboxes themselves were deallocated with their
        //  corresponding io_thread/socket objects.
        termMailbox.close();

        active = false;
    }

    /**
     * @return false if {@link #terminate()}terminate() has been called.
     */
    public boolean isActive()
    {
        return active;
    }

    /**
     * @return false if {@link #terminate()}terminate() has been called.
     * @deprecated use {@link #isActive()} instead
     */
    @Deprecated
    public boolean checkTag()
    {
        return active;
    }

    //  This function is called when user invokes zmq_term. If there are
    //  no more sockets open it'll cause all the infrastructure to be shut
    //  down. If there are open sockets still, the deallocation happens
    //  after the last one is closed.

    public void terminate()
    {<FILL_FUNCTION_BODY>

        slotSync.lock();
        try {
            // Connect up any pending inproc connections, otherwise we will hang
            for (Entry<PendingConnection, String> pending : pendingConnections.entries()) {
                SocketBase s = createSocket(ZMQ.ZMQ_PAIR);
                // create_socket might fail eg: out of memory/sockets limit reached
                assert (s != null);
                s.bind(pending.getValue());
                s.close();
            }

            if (!starting.get()) {
                //  Check whether termination was already underway, but interrupted and now
                //  restarted.
                boolean restarted = terminating;
                terminating = true;

                //  First attempt to terminate the context.
                if (!restarted) {
                    //  First send stop command to sockets so that any blocking calls
                    //  can be interrupted. If there are no sockets we can ask reaper
                    //  thread to stop.
                    for (SocketBase socket : sockets) {
                        socket.stop();
                    }
                    if (sockets.isEmpty()) {
                        reaper.stop();
                    }
                }
            }
        }
        finally {
            slotSync.unlock();
        }

        if (!starting.get()) {
            //  Wait till reaper thread closes all the sockets.
            Command cmd = termMailbox.recv(WAIT_FOREVER);
            if (cmd == null) {
                throw new ZMQException(errno.get());
            }
            assert (cmd.type == Command.Type.DONE) : cmd;

            slotSync.lock();
            try {
                assert (sockets.isEmpty());
            }
            finally {
                slotSync.unlock();
            }
        }

        //  Deallocate the resources.
        try {
            destroy();
        }
        catch (IOException e) {
            throw new ZError.IOException(e);
        }
    
<no_super_class>