/*
 * Copyright (c) 2012-2014 Spotify AB
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.spotify.netty4.util;

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

import io.netty.channel.Channel;
import io.netty.channel.EventLoop;

/**
 * A helper for doing opportunistic batching of netty channel flushes, allowing for a gathering
 * write to an underlying {@link java.nio.channels.GatheringByteChannel},
 * collapsing multiple writes into fewer syscalls.
 */
public class BatchFlusher {

  private static final int DEFAULT_MAX_PENDING = 64;

  private final Channel channel;
  private final EventLoop eventLoop;
  private final int maxPending;

  private final AtomicIntegerFieldUpdater<BatchFlusher> SCHEDULED =
      AtomicIntegerFieldUpdater.newUpdater(BatchFlusher.class, "scheduled");
  @SuppressWarnings("UnusedDeclaration") private volatile int scheduled;

  private int pending;

  private final Runnable flush = new Runnable() {
    @Override
    public void run() {
      scheduled = 0;
      channel.flush();
    }
  };

  public BatchFlusher(final Channel channel) {
    this(channel, DEFAULT_MAX_PENDING);
  }

  public BatchFlusher(final Channel channel, final int maxPending) {
    this.channel = channel;
    this.maxPending = maxPending;
    this.eventLoop = channel.eventLoop();
  }

  public void flush() {
    if (eventLoop.inEventLoop()) {
      pending++;
      if (pending >= maxPending) {
        pending = 0;
        channel.flush();
      }
    }
    if (scheduled == 0 && SCHEDULED.compareAndSet(this, 0, 1)) {
      eventLoop.execute(flush);
    }
  }
}

@@ -1230,25 +1230,21 @@
 lusher%3E 
-SCHEDULED
+WOKEN
  =%0A     
@@ -1302,25 +1302,21 @@
 class, %22
-scheduled
+woken
 %22);%0A  @S
@@ -1373,25 +1373,21 @@
 ile int 
-scheduled
+woken
 ;%0A%0A  pri
@@ -1397,32 +1397,118 @@
 e int pending;%0A%0A
+  /**%0A   * Used to flush all outstanding writes in the outbound channel buffer.%0A   */%0A
   private final 
@@ -1585,25 +1585,23 @@
 %7B%0A      
-scheduled
+pending
  = 0;%0A  
@@ -1633,16 +1633,465 @@
 %7D%0A  %7D;%0A%0A
+  /**%0A   * Used to wake up the event loop and schedule a flush to be performed after all outstanding%0A   * write tasks are run. The outstanding write tasks must be allowed to run before performing the%0A   * actual flush in order to ensure that their payloads have been written to the outbound buffer.%0A   */%0A  private Runnable wakeup = new Runnable() %7B%0A    @Override%0A    public void run() %7B%0A      woken = 0;%0A      eventLoop.execute(flush);%0A    %7D%0A  %7D;%0A%0A
   public
@@ -2344,32 +2344,108 @@
 entLoop();%0A  %7D%0A%0A
+  /**%0A   * Schedule an asynchronous opportunistically batching flush.%0A   */%0A
   public void fl
@@ -2611,35 +2611,27 @@
 if (
-scheduled == 0 && SCHEDULED
+woken == 0 && WOKEN
 .com
@@ -2652,24 +2652,41 @@
 s, 0, 1)) %7B%0A
+      woken = 1;%0A
       eventL
@@ -2689,36 +2689,37 @@
 entLoop.execute(
-flush
+wakeup
 );%0A    %7D%0A  %7D%0A%7D%0A
@@ -2717,9 +2717,8 @@
  %7D%0A  %7D%0A%7D
-%0A

fix batch flusher