Package journal.io.api

Source Code of journal.io.api.DataFileAccessor$ResourceDisposer

/**
*    Licensed under the Apache License, Version 2.0 (the "License");
*    you may not use this file except in compliance with the License.
*    You may obtain a copy of the License at
*
*        http://www.apache.org/licenses/LICENSE-2.0
*
*    Unless required by applicable law or agreed to in writing, software
*    distributed under the License is distributed on an "AS IS" BASIS,
*    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*    See the License for the specific language governing permissions and
*    limitations under the License.
*/
package journal.io.api;

import java.util.concurrent.locks.ReentrantLock;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import journal.io.api.Journal.WriteCommand;
import journal.io.util.IOHelper;
import static journal.io.util.LogHelper.*;

/**
* File reader/updater to randomly access data files, supporting concurrent thread-isolated reads and writes.
*
* @author <a href="http://hiramchirino.com">Hiram Chirino</a>
* @author Sergio Bossa
*/
class DataFileAccessor {

    private final ConcurrentMap<Thread, ConcurrentMap<Integer, RandomAccessFile>> perThreadDataFileRafs = new ConcurrentHashMap<Thread, ConcurrentMap<Integer, RandomAccessFile>>();
    private final ConcurrentMap<Thread, ConcurrentMap<Integer, Lock>> perThreadDataFileLocks = new ConcurrentHashMap<Thread, ConcurrentMap<Integer, Lock>>();
    private final ReadWriteLock compactionLock = new ReentrantReadWriteLock();
    private final Lock accessorLock = compactionLock.readLock();
    private final Lock compactorMutex = compactionLock.writeLock();
    //
    private final Journal journal;
    //   
    private volatile ScheduledExecutorService disposer;

    public DataFileAccessor(Journal journal) {
        this.journal = journal;
    }

    void updateLocation(Location location, byte type, boolean sync) throws IOException {
        Lock threadLock = getOrCreateLock(Thread.currentThread(), location.getDataFileId());
        accessorLock.lock();
        threadLock.lock();
        try {
            journal.sync();
            //
            RandomAccessFile raf = getOrCreateRaf(Thread.currentThread(), location.getDataFileId());
            if (seekToLocation(raf, location)) {
                raf.skipBytes(Journal.RECORD_POINTER_SIZE + Journal.RECORD_LENGTH_SIZE);
                raf.write(type);
                location.setType(type);
                if (sync) {
                    IOHelper.sync(raf.getFD());
                }
            } else {
                throw new IOException("Cannot find location: " + location);
            }
        } finally {
            threadLock.unlock();
            accessorLock.unlock();
        }
    }

    byte[] readLocation(Location location, boolean sync) throws IOException {
        if (location.getData() != null && !sync) {
            return location.getData();
        } else {
            Location read = readLocationDetails(location.getDataFileId(), location.getPointer());
            if (read != null && !read.isDeletedRecord()) {
                return read.getData();
            } else {
                throw new IOException("Invalid location: " + location);
            }
        }
    }

    Location readLocationDetails(int file, int pointer) throws IOException {
        WriteCommand asyncWrite = journal.getInflightWrites().get(new Location(file, pointer));
        if (asyncWrite != null) {
            Location location = new Location(file, pointer);
            location.setPointer(asyncWrite.getLocation().getPointer());
            location.setSize(asyncWrite.getLocation().getSize());
            location.setType(asyncWrite.getLocation().getType());
            location.setData(asyncWrite.getData());
            return location;
        } else {
            Location location = new Location(file, pointer);
            Lock threadLock = getOrCreateLock(Thread.currentThread(), location.getDataFileId());
            accessorLock.lock();
            threadLock.lock();
            try {
                RandomAccessFile raf = getOrCreateRaf(Thread.currentThread(), location.getDataFileId());
                if (seekToLocation(raf, location)) {
                    long position = raf.getFilePointer();
                    location.setPointer(raf.readInt());
                    location.setSize(raf.readInt());
                    location.setType(raf.readByte());
                    if (location.getSize() > 0) {
                        location.setData(readLocationData(location, raf));
                        raf.seek(position);
                        return location;
                    } else {
                        raf.seek(position);
                        return null;
                    }
                } else {
                    return null;
                }
            } finally {
                threadLock.unlock();
                accessorLock.unlock();
            }
        }
    }

    Location readNextLocationDetails(Location start, int type) throws IOException {
        // Try with the most immediate subsequent location among inflight writes:
        Location asyncLocation = new Location(start.getDataFileId(), start.getPointer() + 1);
        WriteCommand asyncWrite = journal.getInflightWrites().get(asyncLocation);
        if (asyncWrite != null && asyncWrite.getLocation().isBatchControlRecord() && type != Location.BATCH_CONTROL_RECORD_TYPE) {
            asyncLocation = new Location(start.getDataFileId(), start.getPointer() + 2);
            asyncWrite = journal.getInflightWrites().get(asyncLocation);
        }
        if (asyncWrite != null) {
            asyncLocation.setPointer(asyncWrite.getLocation().getPointer());
            asyncLocation.setSize(asyncWrite.getLocation().getSize());
            asyncLocation.setType(asyncWrite.getLocation().getType());
            asyncLocation.setData(asyncWrite.getData());
            return asyncLocation;
        } else {
            // Else read from file:
            Lock threadLock = getOrCreateLock(Thread.currentThread(), start.getDataFileId());
            accessorLock.lock();
            threadLock.lock();
            try {
                RandomAccessFile raf = getOrCreateRaf(Thread.currentThread(), start.getDataFileId());
                if (seekToLocation(raf, start) && skipToNextLocation(raf)) {
                    Location next = new Location(start.getDataFileId());
                    long position = 0;
                    do {
                        position = raf.getFilePointer();
                        next.setPointer(raf.readInt());
                        next.setSize(raf.readInt());
                        next.setType(raf.readByte());
                        if (type != Location.ANY_RECORD_TYPE && next.getType() != type) {
                            raf.skipBytes(next.getSize() - Journal.HEADER_SIZE);
                        } else {
                            break;
                        }
                    } while (raf.length() - raf.getFilePointer() > Journal.HEADER_SIZE);
                    if (type == Location.ANY_RECORD_TYPE || next.getType() == type) {
                        next.setData(readLocationData(next, raf));
                        raf.seek(position);
                        return next;
                    } else {
                        raf.seek(position);
                        return null;
                    }
                } else {
                    return null;
                }
            } finally {
                threadLock.unlock();
                accessorLock.unlock();
            }
        }
    }

    void dispose(DataFile dataFile) {
        for (Entry<Thread, ConcurrentMap<Integer, RandomAccessFile>> threadRafs : perThreadDataFileRafs.entrySet()) {
            for (Entry<Integer, RandomAccessFile> raf : threadRafs.getValue().entrySet()) {
                if (raf.getKey().equals(dataFile.getDataFileId())) {
                    Lock lock = getOrCreateLock(threadRafs.getKey(), raf.getKey());
                    lock.lock();
                    try {
                        removeRaf(threadRafs.getKey(), raf.getKey());
                        return;
                    } catch (IOException ex) {
                        warn(ex, ex.getMessage());
                    } finally {
                        lock.unlock();
                    }
                }
            }
        }
    }

    void open() {
        disposer = journal.getDisposer();
        disposer.scheduleAtFixedRate(new ResourceDisposer(), journal.getDisposeInterval(), journal.getDisposeInterval(), TimeUnit.MILLISECONDS);
    }

    void close() {
        // No-op as of now...
    }

    void pause() {
        compactorMutex.lock();
    }

    void resume() {
        compactorMutex.unlock();
    }

    private boolean seekToLocation(RandomAccessFile raf, Location destination) throws IOException {
        // First try the next file position:
        long position = raf.getFilePointer();
        int pointer = -1;
        int length = -1;
        int type = -1;
        if (raf.length() - position > Journal.HEADER_SIZE) {
            pointer = raf.readInt();
            length = raf.readInt();
            type = raf.readByte();
        }
        // Else seek from beginning:
        if (pointer != destination.getPointer() || type != destination.getType()) {
            raf.seek(0);
            position = raf.getFilePointer();
            if (raf.length() - position > Journal.HEADER_SIZE) {
                pointer = raf.readInt();
                while (pointer != destination.getPointer()) {
                    length = raf.readInt();
                    raf.skipBytes(length - Journal.RECORD_POINTER_SIZE - Journal.RECORD_LENGTH_SIZE);
                    position = raf.getFilePointer();
                    if (raf.length() - position > Journal.HEADER_SIZE) {
                        pointer = raf.readInt();
                    } else {
                        return false;
                    }
                }
            } else {
                return false;
            }
        }
        raf.seek(position);
        return true;
    }

    private boolean skipToNextLocation(RandomAccessFile raf) throws IOException {
        if (raf.length() - raf.getFilePointer() > Journal.HEADER_SIZE) {
            raf.skipBytes(Journal.RECORD_POINTER_SIZE);
            raf.skipBytes(raf.readInt() - Journal.RECORD_POINTER_SIZE - Journal.RECORD_LENGTH_SIZE);
            if (raf.length() - raf.getFilePointer() > Journal.HEADER_SIZE) {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

    private byte[] readLocationData(Location location, RandomAccessFile raf) throws IOException {
        if (location.isBatchControlRecord()) {
            int batchSize = raf.readInt();
            byte[] data = new byte[Journal.CHECKSUM_SIZE + batchSize];
            raf.readFully(data);
            return data;
        } else {
            byte[] data = new byte[location.getSize() - Journal.HEADER_SIZE];
            raf.readFully(data);
            return data;
        }
    }

    private RandomAccessFile getOrCreateRaf(Thread thread, Integer file) throws IOException {
        ConcurrentMap<Integer, RandomAccessFile> rafs = perThreadDataFileRafs.get(thread);
        if (rafs == null) {
            rafs = new ConcurrentHashMap<Integer, RandomAccessFile>();
            perThreadDataFileRafs.put(thread, rafs);
        }
        RandomAccessFile raf = rafs.get(file);
        if (raf == null) {
            raf = journal.getDataFiles().get(file).openRandomAccessFile();
            rafs.put(file, raf);
        }
        return raf;
    }

    private void removeRaf(Thread thread, Integer file) throws IOException {
        RandomAccessFile raf = perThreadDataFileRafs.get(thread).remove(file);
        raf.close();
    }

    private Lock getOrCreateLock(Thread thread, Integer file) {
        ConcurrentMap<Integer, Lock> locks = perThreadDataFileLocks.get(thread);
        if (locks == null) {
            locks = new ConcurrentHashMap<Integer, Lock>();
            perThreadDataFileLocks.put(thread, locks);
        }
        Lock lock = locks.get(file);
        if (lock == null) {
            lock = new ReentrantLock();
            locks.put(file, lock);
        }
        return lock;
    }

    private class ResourceDisposer implements Runnable {

        public void run() {
            Set<Thread> deadThreads = new HashSet<Thread>();
            for (Entry<Thread, ConcurrentMap<Integer, RandomAccessFile>> threadRafs : perThreadDataFileRafs.entrySet()) {
                for (Entry<Integer, RandomAccessFile> raf : threadRafs.getValue().entrySet()) {
                    Lock lock = getOrCreateLock(threadRafs.getKey(), raf.getKey());
                    if (lock.tryLock()) {
                        try {
                            removeRaf(threadRafs.getKey(), raf.getKey());
                            if (!threadRafs.getKey().isAlive()) {
                                deadThreads.add(threadRafs.getKey());
                            }
                        } catch (IOException ex) {
                            warn(ex, ex.getMessage());
                        } finally {
                            lock.unlock();
                        }
                    }
                }
            }
            for (Thread deadThread : deadThreads) {
                perThreadDataFileRafs.remove(deadThread);
                perThreadDataFileLocks.remove(deadThread);
            }
        }

    }
}
TOP

Related Classes of journal.io.api.DataFileAccessor$ResourceDisposer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.