All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.hk2.utilities.general.internal.WeakHashClockImpl Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2015, 2024 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package org.glassfish.hk2.utilities.general.internal;

import java.lang.ref.ReferenceQueue;
import java.util.LinkedList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;

import org.glassfish.hk2.utilities.cache.CacheKeyFilter;
import org.glassfish.hk2.utilities.general.WeakHashClock;

/**
 * Implementation of WeakHashClock as needed by the CAR algorithm
 * 
 * @author jwells
 *
 */
public class WeakHashClockImpl implements WeakHashClock {
    private final boolean isWeak;
    private final ConcurrentHashMap> byKeyNotWeak;
    private final WeakHashMap> byKey;

    private final ReentrantLock lock = new ReentrantLock();
    private final ReferenceQueue myQueue = new ReferenceQueue();
    
    private DoubleNode head;
    private DoubleNode tail;
    private DoubleNode dot;
    
    public WeakHashClockImpl(boolean isWeak) {
        this.isWeak = isWeak;
        if (isWeak) {
            byKey = new WeakHashMap>();
            byKeyNotWeak = null;
        }
        else {
            byKeyNotWeak = new ConcurrentHashMap>();
            byKey = null;
        }
    }
    
    private DoubleNode addBeforeDot(K key, V value) {
        DoubleNode toAdd = new DoubleNode(key,value, myQueue);
        
        if (dot == null) {
            // List is empty
            head = toAdd;
            tail = toAdd;
            dot = toAdd;
            return toAdd;
        }
        
        if (dot.getPrevious() == null) {
            // Dot is currently at the head
            dot.setPrevious(toAdd);
            
            toAdd.setNext(dot);
            head = toAdd;
            return toAdd;
        }
        
        // Otherwise just add it.  Note it will NEVER be added as
        // the tail because it is always being added before something
        toAdd.setNext(dot);
        toAdd.setPrevious(dot.getPrevious());
        
        dot.getPrevious().setNext(toAdd);
        dot.setPrevious(toAdd);
        
        return toAdd;
    }
    
    private void removeFromDLL(DoubleNode removeMe) {
        if (removeMe.getPrevious() != null) {
            removeMe.getPrevious().setNext(removeMe.getNext());
        }
        if (removeMe.getNext() != null) {
            removeMe.getNext().setPrevious(removeMe.getPrevious());
        }
        
        if (removeMe == head) {
            head = removeMe.getNext();
        }
        if (removeMe == tail) {
            tail = removeMe.getPrevious();
        }
        
        if (removeMe == dot) {
            dot = removeMe.getNext();
            if (dot == null) dot = head;
        }
        
        removeMe.setNext(null);
        removeMe.setPrevious(null);
    }

    /* (non-Javadoc)
     * @see org.glassfish.hk2.utilities.general.WeakHashClock#put(java.lang.Object, java.lang.Object)
     */
    @Override
    public void put(final K key, final V value) {
        if (key == null || value == null) throw new IllegalArgumentException("key " + key + " or value " + value + " is null");
        
        lock.lock();
        try {
            if (isWeak) {
                removeStale();
            }
            
            DoubleNode addMe = addBeforeDot(key, value);
        
            if (isWeak) {
                byKey.put(key, addMe);
            }
            else {
                byKeyNotWeak.put(key, addMe);
            }
        } finally {
            lock.unlock();
        }
    }

    /* (non-Javadoc)
     * @see org.glassfish.hk2.utilities.general.WeakHashClock#get(java.lang.Object)
     */
    @Override
    public V get(K key) {
        if (key == null) return null;
        
        DoubleNode node;
        if (isWeak) {
            lock.lock();
            try {
                removeStale();
        
                node = byKey.get(key);
            } finally {
                lock.unlock();
            }
        }
        else {
            node = byKeyNotWeak.get(key);
        }
        
        if (node == null) return null;
        
        return node.getValue();
    }

    /* (non-Javadoc)
     * @see org.glassfish.hk2.utilities.general.WeakHashClock#remove(java.lang.Object)
     */
    @Override
    public V remove(K key) {
        if (key == null) return null;
        
        lock.lock();
        try {
            DoubleNode node;
            if (isWeak) {
                removeStale();
                
                node = byKey.remove(key);
            }
            else {
                node = byKeyNotWeak.remove(key);
            }
                
            if (node == null) return null;
            
            removeFromDLL(node);
            
            return node.getValue();
        } finally {
            lock.unlock();
        }
    }
    
    /* (non-Javadoc)
     * @see org.glassfish.hk2.utilities.general.WeakHashClock#releaseMatching(org.glassfish.hk2.utilities.cache.CacheKeyFilter)
     */
    @Override
    public void releaseMatching(CacheKeyFilter filter) {
        lock.lock();
        try {
            if (filter == null) return;
            
            if (isWeak) {
                removeStale();
            }
            
            LinkedList removeMe = new LinkedList();
            DoubleNode current = head;
            while (current != null) {
                K key = current.getWeakKey().get();
                if (key != null && filter.matches(key)) {
                    removeMe.add(key);
                }
                
                current = current.getNext();
            }
            
            for (K removeKey : removeMe) {
                remove(removeKey);
            }
        } finally {
            lock.unlock();
        }
    }

    /* (non-Javadoc)
     * @see org.glassfish.hk2.utilities.general.WeakHashClock#size()
     */
    @Override
    public int size() {
        if (isWeak) {
            lock.lock();
            try {
                removeStale();
        
                return byKey.size();
            } finally {
                lock.unlock();
            }
        }
        
        return byKeyNotWeak.size();
    }
    
    private DoubleNode moveDot() {
        if (dot == null) return null;
        
        DoubleNode returnSource = dot;
        dot = returnSource.getNext();
        if (dot == null) dot = head;
        
        return returnSource;
    }
    
    private DoubleNode moveDotNoWeak() {
        DoubleNode original = moveDot();
        DoubleNode retVal = original;
        if (retVal == null) return null;
        
        K key;
        while ((key = retVal.getWeakKey().get()) == null) {
            retVal = moveDot();
            if (retVal == null) return null;
            if (retVal == original) return null; // All the way around
        }
        
        retVal.setHardenedKey(key);
        
        return retVal;
    }

    /* (non-Javadoc)
     * @see org.glassfish.hk2.utilities.general.WeakHashClock#next()
     */
    @Override
    public Entry next() {
        lock.lock();
        try {
            DoubleNode hardenedNode = moveDotNoWeak();
            if (hardenedNode == null) return null;
            try {
                final K key = hardenedNode.getHardenedKey();
                final V value = hardenedNode.getValue();
                
                return new Map.Entry() {

                    @Override
                    public K getKey() {
                        return key;
                    }

                    @Override
                    public V getValue() {
                        return value;
                    }

                    @Override
                    public V setValue(V value) {
                        throw new AssertionError("not implemented");
                    }
                    
                };
            }
            finally {
                hardenedNode.setHardenedKey(null);
                
                removeStale();
            }
        } finally {
            lock.unlock();
        }
    }
    
    /* (non-Javadoc)
     * @see org.glassfish.hk2.utilities.general.WeakHashClock#clear()
     */
    @Override
    public void clear() {
        lock.lock();
        try {
            if (isWeak) {
                byKey.clear();
            }
            else {
                byKeyNotWeak.clear();
            }
            
            head = tail = dot = null;
        } finally {
            lock.unlock();
        }
    }
    
    /* (non-Javadoc)
     * @see org.glassfish.hk2.utilities.general.WeakHashClock#clearStaleReferences()
     */
    @Override
    public void clearStaleReferences() {
        lock.lock();
        try {
            removeStale();
        } finally {
            lock.unlock();
        }
    }
    
    private void removeStale() {
        boolean goOn = false;
        while (myQueue.poll() != null) {
            goOn = true;
        }
        
        if (!goOn) return;
        
        DoubleNode current = head;
        while (current != null) {
            DoubleNode next = current.getNext();
            
            if (current.getWeakKey().get() == null) {
                removeFromDLL(current);
            }
            
            current = next;
        }
    }
    
    /* (non-Javadoc)
     * @see org.glassfish.hk2.utilities.general.WeakHashClock#hasWeakKeys()
     */
    @Override
    public boolean hasWeakKeys() {
        return isWeak;
    }
    
    @Override
    public String toString() {
        lock.lock();
        try {
            StringBuffer sb = new StringBuffer("WeakHashClockImpl({");
            
            boolean first = true;
            DoubleNode current = dot;
            if (current != null) {
                do {
                    K key = current.getWeakKey().get();
                    String keyString = (key == null) ? "null" : key.toString();
                
                    if (first) {
                        first = false;
                    
                        sb.append(keyString);
                    }
                    else {
                        sb.append("," + keyString);
                    }
                
                    current = current.getNext();
                    if (current == null) current = head;
                } while (current != dot);
            }
            
            sb.append("}," + System.identityHashCode(this) + ")");
                  
            return sb.toString();
        } finally {
            lock.unlock();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy