package net.openhft.chronicle.core.io;

import net.openhft.chronicle.core.Jvm;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

public enum BackgroundResourceReleaser {
    ;
    static final boolean BG_RELEASER = Jvm.getBoolean("background.releaser", true);

    private static final BlockingQueue<Object> RESOURCES = new ArrayBlockingQueue<>(128);
    private static final Thread RELEASER = new Thread(BackgroundResourceReleaser::runReleaseResources,
            "background~resource~releaser");

    static {
        RELEASER.setDaemon(true);
        RELEASER.start();
    }

    private static void runReleaseResources() {
        try {
            for (; ; ) {
                Object o = RESOURCES.take();
                synchronized (BackgroundResourceReleaser.class) {
                    performRelease(o);
                }
            }
        } catch (InterruptedException e) {
            Jvm.warn().on(BackgroundResourceReleaser.class, "Died on interrupt");
        }
    }

    public static void release(AbstractCloseable closeable) {
        release0(closeable);
    }

    private static void release0(Object o) {
        if (RESOURCES.offer(o))
            return;
        performRelease(o);
    }

    public static void releasePendingResources() {
        try {
            for (; ; ) {
                Object o = RESOURCES.poll(1, TimeUnit.MILLISECONDS);
                if (o == null) {
                    synchronized (BackgroundResourceReleaser.class) {
                        return;
                    }
                }
                performRelease(o);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    public static void release(AbstractReferenceCounted referenceCounted) {
        release0(referenceCounted);
    }

    private static void performRelease(Object o) {
        if (o instanceof AbstractCloseable)
            ((AbstractCloseable) o).performClose();
        else if (o instanceof AbstractReferenceCounted)
            ((AbstractReferenceCounted) o).performRelease();
        else
            Jvm.warn().on(BackgroundResourceReleaser.class, "Don't know how to release a " + o.getClass());
    }
}

@@ -201,16 +201,63 @@
 imeUnit;
+%0Aimport java.util.concurrent.atomic.AtomicLong;
 %0A%0Apublic
@@ -472,16 +472,80 @@
 %3E(128);%0A
+    private static final AtomicLong COUNTER = new AtomicLong();%0A
     priv
@@ -901,78 +901,8 @@
 ();%0A
-                synchronized (BackgroundResourceReleaser.class) %7B%0A    
     
@@ -932,34 +932,16 @@
 ase(o);%0A
-                %7D%0A
         
@@ -1223,32 +1223,67 @@
 se0(Object o) %7B%0A
+        COUNTER.incrementAndGet();%0A
         if (RESO
@@ -1568,88 +1568,296 @@
     
-synchronized (BackgroundResourceReleaser.class) %7B%0A                        return
+for (int i = 0; i %3C 1000 && COUNTER.get() %3E 0; i++)%0A                        Jvm.pause(1);%0A                    long left = COUNTER.get();%0A                    if (left != 0)%0A                        Jvm.warn().on(BackgroundResourceReleaser.class, %22Still got %22 + left + %22 resources to clean%22)
 ;%0A  
@@ -1866,33 +1866,39 @@
                 
-%7D
+return;
 %0A               
@@ -2219,32 +2219,50 @@
 ase(Object o) %7B%0A
+        try %7B%0A    
         if (o in
@@ -2289,16 +2289,20 @@
 seable)%0A
+    
         
@@ -2349,24 +2349,28 @@
 ();%0A        
+    
 else if (o i
@@ -2409,32 +2409,36 @@
 ed)%0A            
+   
+ 
 ((AbstractRefere
@@ -2482,13 +2482,21 @@
     
+    
 else%0A
+    
     
@@ -2599,16 +2599,208 @@
 ass());%0A
+        %7D catch (Throwable e) %7B%0A            Jvm.warn().on(BackgroundResourceReleaser.class, %22Failed in release/close%22, e);%0A        %7D finally %7B%0A            COUNTER.decrementAndGet();%0A        %7D%0A
     %7D%0A%7D%0A

Use a counter to ensure the releasePendingResources() doesn't return while the background thread is busy.