private DefaultChannelHandlerContext remove(
      final DefaultChannelHandlerContext ctx, final boolean forward) {
    assert ctx != head && ctx != tail;

    DefaultChannelHandlerContext context;
    Future<?> future;

    synchronized (this) {
      if (!ctx.channel().isRegistered() || ctx.executor().inEventLoop()) {
        remove0(ctx, forward);
        return ctx;
      } else {
        future =
            ctx.executor()
                .submit(
                    new Runnable() {
                      @Override
                      public void run() {
                        synchronized (DefaultChannelPipeline.this) {
                          remove0(ctx, forward);
                        }
                      }
                    });
        context = ctx;
      }
    }

    // Run the following 'waiting' code outside of the above synchronized block
    // in order to avoid deadlock

    waitForFuture(future);

    return context;
  }
  @Override
  public ChannelPipeline addAfter(
      EventExecutorGroup group, String baseName, final String name, ChannelHandler handler) {
    final DefaultChannelHandlerContext ctx;
    final DefaultChannelHandlerContext newCtx;

    synchronized (this) {
      ctx = getContextOrDie(baseName);
      checkDuplicateName(name);
      newCtx = new DefaultChannelHandlerContext(this, group, name, handler);

      if (!newCtx.channel().isRegistered() || newCtx.executor().inEventLoop()) {
        addAfter0(name, ctx, newCtx);
        return this;
      }
    }

    // Run the following 'waiting' code outside of the above synchronized block
    // in order to avoid deadlock

    newCtx.executeOnEventLoop(
        new Runnable() {
          @Override
          public void run() {
            synchronized (DefaultChannelPipeline.this) {
              checkDuplicateName(name);
              addAfter0(name, ctx, newCtx);
            }
          }
        });

    return this;
  }
  private ChannelHandler replace(
      final DefaultChannelHandlerContext ctx,
      final String newName,
      ChannelHandler newHandler,
      final boolean forward) {

    assert ctx != head && ctx != tail;

    Future<?> future;
    synchronized (this) {
      boolean sameName = ctx.name().equals(newName);
      if (!sameName) {
        checkDuplicateName(newName);
      }

      final DefaultChannelHandlerContext newCtx =
          new DefaultChannelHandlerContext(this, ctx.executor, newName, newHandler);

      if (!newCtx.channel().isRegistered() || newCtx.executor().inEventLoop()) {
        replace0(ctx, newName, newCtx, forward);

        return ctx.handler();
      } else {
        future =
            newCtx
                .executor()
                .submit(
                    new Runnable() {
                      @Override
                      public void run() {
                        synchronized (DefaultChannelPipeline.this) {
                          replace0(ctx, newName, newCtx, forward);
                        }
                      }
                    });
      }
    }

    // Run the following 'waiting' code outside of the above synchronized block
    // in order to avoid deadlock

    waitForFuture(future);

    return ctx.handler();
  }