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

com.maxifier.mxcache.clean.CleaningHelper Maven / Gradle / Ivy

Go to download

Constains all classes necessary for launching a MxCache-instrumentated application

There is a newer version: 2.6.9
Show newest version
/*
 * Copyright (c) 2008-2014 Maxifier Ltd. All Rights Reserved.
 */
package com.maxifier.mxcache.clean;

import com.maxifier.mxcache.caches.CleaningNode;
import com.maxifier.mxcache.impl.resource.DependencyNode;
import com.maxifier.mxcache.impl.resource.DependencyTracker;
import com.maxifier.mxcache.util.TIdentityHashSet;

import javax.annotation.Nonnull;

import java.util.*;
import java.util.concurrent.locks.Lock;

/**
 * CleaningHelper
 *
 * @author Andrey Yakoushin ([email protected])
 * @author Alexander Kochurov ([email protected])
 */
public final class CleaningHelper {

    private CleaningHelper() {
    }

    private static SuperLock getSuperLock(Collection caches) {
        List locks = new ArrayList(caches.size());
        for (CleaningNode cache : caches) {
            Lock lock = cache.getLock();
            if (lock != null) {
                locks.add(lock);
            }
        }
        return new SuperLock(locks);
    }

    private static int lockLists(List> lists) {
        int version = 0;
        for (WeakList list : lists) {
            version += list.lock();
        }
        return version;
    }

    private static void unlockLists(List> lists) {
        for (WeakList list : lists) {
            list.unlock();
        }
    }

    private static void clear(Collection caches) {
        for (CleaningNode cache : caches) {
            cache.clear();
        }
    }

    public static void clear(@Nonnull CleanableInstanceList list) {
        List> lists = new ArrayList>();
        List elements = new ArrayList();

        Iterable nodes = nodeMapping(elements);

        outer: while (true) {
            int subtreeVersion = list.deepLock();
            try {
                lists.clear();
                list.getLists(lists);
            } finally {
                list.deepUnlock();
            }

            int listsVersion = lockLists(lists);
            try {
                elements.clear();
                list.getCaches(elements);
            } finally {
                unlockLists(lists);
            }
            TIdentityHashSet elementsAndDependent = DependencyTracker.getAllDependentNodes(nodes, elements);
            // dependency modification check loop
            while (true) {
                SuperLock superLock = getSuperLock(elementsAndDependent);
                superLock.lock();
                try {
                    TIdentityHashSet newElements = DependencyTracker.getAllDependentNodes(nodes, elements);
                    if (!newElements.equals(elementsAndDependent)) {
                        // the set of dependent caches has been altered, lock everything again
                        elementsAndDependent = newElements;
                        continue;
                    }
                    int newSubtreeVersion = list.deepLock();
                    try {
                        if (newSubtreeVersion != subtreeVersion) {
                            continue outer;
                        }
                        int newListsVersion = lockLists(lists);
                        try {
                            if (newListsVersion != listsVersion) {
                                continue outer;
                            }
                            clear(elementsAndDependent);
                            return;
                        } finally {
                            unlockLists(lists);
                        }
                    } finally {
                        list.deepUnlock();
                    }
                } finally {
                    superLock.unlock();
                }
            }
        }
    }

    /**
     * Collects all dependent nodes of root node and locks them.
     * @param rootNode root node to lookup dependencies from
     * @return a list of dependent caches and a super lock for this list.
     *    Note: returned super lock is already locked unless there was an exception thrown.
     */
    public static RecursiveLock lockRecursive(DependencyNode rootNode) {
        TIdentityHashSet elements = DependencyTracker.getAllDependentNodes(Collections.singleton(rootNode));
        Iterable nodes = nodeMapping(elements);
        while (true) {
            SuperLock superLock = getSuperLock(elements);
            superLock.lock();
            try {
                TIdentityHashSet newElements = DependencyTracker.getAllDependentNodes(nodes, elements);
                if (!newElements.containsAll(elements)) {
                    // we have to unlock all locks and lock them again among with new ones as advanced locking algorithm
                    // locks guarantees the absence of deadlocks only if all locks are locked at once.
                    superLock.unlock();
                    elements.addAll(newElements);
                    continue;
                }
                return new RecursiveLock(elements, superLock);
            } catch (Error e) {
                // we have to unlock it on exception
                superLock.unlock();
                throw e;
            } catch (RuntimeException e) {
                // we have to unlock it on exception
                superLock.unlock();
                throw e;
            }
        }
    }

    public static void lockAndClear(Collection elements) {
        Iterable nodes = nodeMapping(elements);
        TIdentityHashSet elementsAndDependent = DependencyTracker.getAllDependentNodes(nodes, elements);
        while (true) {
            SuperLock superLock = getSuperLock(elementsAndDependent);
            superLock.lock();
            try {
                TIdentityHashSet newElements = DependencyTracker.getAllDependentNodes(nodes, elements);
                if (newElements.equals(elementsAndDependent)) {
                    for (CleaningNode element : elementsAndDependent) {
                        element.clear();
                    }
                    return;
                }
                // the set of dependent caches has been altered, lock everything again
                elementsAndDependent = newElements;
            } finally {
                superLock.unlock();
            }
        }
    }

    private static Iterable nodeMapping(final Collection elements) {
        return new MappingIterable(elements) {
            @Override
            public DependencyNode map(CleaningNode cleaningNode) {
                return cleaningNode.getDependencyNode();
            }
        };
    }

    public static class RecursiveLock {
        public final TIdentityHashSet elements;
        public final SuperLock lock;

        public RecursiveLock(TIdentityHashSet elements, SuperLock lock) {
            this.elements = elements;
            this.lock = lock;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy