/*
 *  Copyright (C) 2022 Cojen.org
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package org.cojen.tupl.rows;

import org.cojen.tupl.util.Latch;

/**
 * Cache which makes missing entries on demand and uses an per entry latch to ensure that only
 * one thread does the work.
 *
 * @author Brian S O'Neill
 */
abstract class SoftLatchedCache<K, V, H> extends SoftCache<K, Object> {
    @SuppressWarnings({"unchecked"})
    public V obtain(K key, H helper) {
        Latch latch;
        while (true) {
            Object value = get(key);

            if (value != null) {
                if (!(value instanceof Latch)) {
                    return (V) value;
                }
                latch = (Latch) value;
            } else {
                synchronized (this) {
                    value = get(key);
                    if (value != null) {
                        if (!(value instanceof Latch)) {
                            return (V) value;
                        }
                        latch = (Latch) value;
                    } else {
                        latch = new Latch(Latch.EXCLUSIVE);
                        put(key, latch);
                        // Break out of the loop and do the work.
                        break;
                    }
                }
            }

            // Wait for another thread to do the work and try again.
            latch.acquireShared();
        }

        V value;
        Throwable ex = null;

        try {
            value = newValue(key, helper);
        } catch (Throwable e) {
            value = null;
            ex = e;
        }

        try {
            if (value != null) {
                put(key, value);
            } else {
                removeKey(key);
            }
        } catch (Throwable e) {
            if (ex == null) {
                ex = e;
            } else {
                ex.addSuppressed(e);
            }
        } finally {
            latch.releaseExclusive();
        }

        if (ex != null) {
            throw RowUtils.rethrow(ex);
        }

        return value;
    }

    protected abstract V newValue(K key, H helper);
}

@@ -847,12 +847,8 @@
  an 
-per 
 entr
@@ -878,15 +878,15 @@
 only
-%0A *
  one
+%0A *
  thr
@@ -2248,32 +2248,37 @@
       %7D%0A%0A       
+ put:
  try %7B%0A         
@@ -2309,32 +2309,58 @@
                 
+try %7B%0A                    
 put(key, value);
@@ -2363,34 +2363,16 @@
 ue);%0A   
-         %7D else %7B%0A
         
@@ -2383,22 +2383,18 @@
     
-removeKey(key)
+ break put
 ;%0A  
@@ -2402,27 +2402,21 @@
         
- %7D%0A
      
-   
 %7D catch 
@@ -2423,32 +2423,40 @@
 (Throwable e) %7B%0A
+        
             if (
@@ -2477,32 +2477,40 @@
                 
+        
 ex = e;%0A        
@@ -2505,32 +2505,40 @@
  e;%0A            
+  
+      
 %7D else %7B%0A       
@@ -2522,32 +2522,40 @@
        %7D else %7B%0A
+        
                 
@@ -2579,33 +2579,102 @@
 e);%0A            
+       
-%7D
+ %7D%0A                %7D%0A            %7D%0A%0A            removeKey(key);
 %0A        %7D final

Improve exception handling.