private void reconnect() {
        if (this.reconnecting) return;

        final Manager self = this;
        this.attempts++;

        if (attempts > this._reconnectionAttempts) {
            logger.fine("reconnect failed");
            this.emitAll(EVENT_RECONNECT_FAILED);
            this.reconnecting = false;
        } else {
            long delay = this.attempts * this.reconnectionDelay();
            delay = Math.min(delay, this.reconnectionDelayMax());
            logger.fine(String.format("will wait %dms before reconnect attempt", delay));

            this.reconnecting = true;
            final Future timer = this.reconnectScheduler.schedule(new Runnable() {
                @Override
                public void run() {
                    EventThread.exec(new Runnable() {
                        @Override
                        public void run() {
                            logger.fine("attempting reconnect");
                            self.emitAll(EVENT_RECONNECT_ATTEMPT, self.attempts);
                            self.emitAll(EVENT_RECONNECTING, self.attempts);
                            self.open(new OpenCallback() {
                                @Override
                                public void call(Exception err) {
                                    if (err != null) {
                                        logger.fine("reconnect attempt error");
                                        self.reconnecting = false;
                                        self.reconnect();
                                        self.emitAll(EVENT_RECONNECT_ERROR, err);
                                    } else {
                                        logger.fine("reconnect success");
                                        self.onreconnect();
                                    }
                                }
                            });
                        }
                    });
                }
            }, delay, TimeUnit.MILLISECONDS);

            this.subs.add(new On.Handle() {
                @Override
                public void destroy() {
                    timer.cancel(false);
                }
            });
        }
    }