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

de.schlichtherle.truezip.extension.pace.PaceManagerModel Maven / Gradle / Ivy

Go to download

This module constrains the number of mounted archive files in order to save some heap space. It provides a JMX interface for monitoring and management. Add the JAR artifact of this module to the run time class path to make its services available for service location in the client API modules.

There is a newer version: 7.7.10
Show newest version
/*
 * Copyright (C) 2005-2013 Schlichtherle IT Services.
 * All rights reserved. Use is subject to license terms.
 */
package de.schlichtherle.truezip.extension.pace;

import static de.schlichtherle.truezip.extension.pace.PaceManager.MAXIMUM_FILE_SYSTEMS_MOUNTED_DEFAULT_VALUE;
import static de.schlichtherle.truezip.extension.pace.PaceManager.MAXIMUM_FILE_SYSTEMS_MOUNTED_MINIMUM_VALUE;
import de.schlichtherle.truezip.fs.*;
import de.schlichtherle.truezip.util.BitField;
import de.schlichtherle.truezip.util.HashMaps;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;

/**
 * The pace manager model.
 * 
 * @author Christian Schlichtherle
 */
@ThreadSafe
final class PaceManagerModel {

    private static final Logger logger = Logger.getLogger(
            PaceManagerModel.class.getName(),
            PaceManagerModel.class.getName());
            
    private final Collection> evicted
            = new ConcurrentLinkedQueue>();
    private final MountedControllerSet mounted = new MountedControllerSet();

    private volatile int maxMounted = MAXIMUM_FILE_SYSTEMS_MOUNTED_DEFAULT_VALUE;
    private @Nullable volatile FsManager manager;

    void init(final FsManager manager) {
        assert !(manager instanceof PaceManagerController);
        if (null != this.manager) throw new IllegalStateException();
        this.manager = manager;
        mounted.sync();
    }

    int getFileSystemsMounted() {
        return mounted.sync();
    }

    int getMaximumFileSystemsMounted() {
        return maxMounted;
    }

    void setMaximumFileSystemsMounted(final int maxMounted) {
        if (maxMounted < MAXIMUM_FILE_SYSTEMS_MOUNTED_MINIMUM_VALUE)
            throw new IllegalArgumentException();
        this.maxMounted = maxMounted;
    }

    /**
     * If the number of mounted archive files exceeds {@link #getMaximumFileSystemsMounted()},
     * then this method sync()s the least recently used (LRU) archive files
     * which exceed this value.
     * 
     * @param  controller the controller for the file system to retain mounted for
     *         subsequent access.
     * @throws FsSyncException 
     */
    void retain(final FsController controller)
    throws FsSyncException {
        final FsMountPoint mp = controller.getModel().getMountPoint();
        iterating: for (final Iterator> i = evicted.iterator(); i.hasNext(); ) {
            final FsController ec = i.next();
            final FsMountPoint emp = ec.getModel().getMountPoint();
            final FsManager fm = new FsFilteringManager(manager, emp);
            for (final FsController fc : fm) {
                final FsMountPoint fmp = fc.getModel().getMountPoint();
                if (mp.equals(fmp) || mounted.contains(fmp)) {
                    if (emp.equals(fmp) || mounted.contains(emp)) i.remove();
                    continue iterating;
                }
            }
            try {
                fm.sync(FsSyncOptions.NONE);
                i.remove();
            } catch (final FsSyncException ex) {
                if (ex.getCause() instanceof FsResourceOpenException) {
                    logger.log(Level.FINER, "ignoring", ex);
                } else {
                    i.remove();
                    throw ex;
                }
            }
        }
    }

    /**
     * Registers the archive file system of the given controller as the most
     * recently used (MRU).
     * 
     * @param controller the controller for the most recently used file system.
     */
    void accessed(final FsController controller) {
        if (controller.getModel().isMounted()) mounted.add(controller);
    }

    void sync(final BitField options)
    throws FsSyncException {
        evicted.clear();
        try {
            manager.sync(options);
        } finally {
            mounted.sync();
        }
    }

    @SuppressWarnings("serial")
    private final class MountedControllerSet {
        private final LinkedHashMap> map
                = new LinkedHashMap>(
                    HashMaps.initialCapacity(getMaximumFileSystemsMounted() + 1),
                    0.75f,
                    true) {
            @Override
            public boolean removeEldestEntry(
                    final Map.Entry> entry) {
                final boolean evict = size() > getMaximumFileSystemsMounted();
                if (evict) {
                    final FsController c = entry.getValue();
                    final boolean added = evicted.add(c);
                    assert added;
                }
                return evict;
            }
        };
        private final ReadLock readLock;
        private final WriteLock writeLock;

        MountedControllerSet() {
            final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
            readLock = lock.readLock();
            writeLock = lock.writeLock();
        }

        boolean contains(FsMountPoint key) {
            readLock.lock();
            try {
                return map.containsKey(key);
            } finally {
                readLock.unlock();
            }
        }

        void add(final FsController controller) {
            final FsMountPoint mp = controller.getModel().getMountPoint();
            writeLock.lock();
            try {
                map.put(mp, controller);
            } finally {
                writeLock.unlock();
            }
        }

        int sync() {
            if (null == manager) return map.size();
            writeLock.lock();
            try {
                map.clear();
                for (final FsController c : manager)
                    if (c.getModel().isMounted())
                        map.put(c.getModel().getMountPoint(), c);
                return map.size();
            } finally {
                writeLock.unlock();
            }
        }
    } // MountedControllerSet
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy