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

one.nio.mem.FixedSizeAllocator Maven / Gradle / Ivy

/*
 * Copyright 2015 Odnoklassniki Ltd, Mail.Ru Group
 *
 * 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 one.nio.mem;

import one.nio.util.JavaInternals;

import static one.nio.util.JavaInternals.unsafe;

// Fast lock-free allocator that manages entries of the fixed size in a linked list
public class FixedSizeAllocator implements Allocator {
    protected static final long headOffset = JavaInternals.fieldOffset(FixedSizeAllocator.class, "head");

    // These constants help to go around ABA problem of the lock-free linked list
    private static final long ADDR_MASK    = 0xffffffffffffL;
    private static final long COUNTER_MASK = ~ADDR_MASK;
    private static final long COUNTER_INC  = ADDR_MASK + 1;

    private volatile long p1, p2, p3, p4, p5, p6, p7 = 0;  // padding against false sharing
    protected volatile long head;
    private volatile long q1, q2, q3, q4, q5, q6, q7 = 0;  // padding against false sharing

    protected long entrySize;
    protected long chunkSize;
    protected long totalMemory;

    public FixedSizeAllocator(long entrySize, long chunkSize) {
        this.entrySize = entrySize;
        this.chunkSize = chunkSize;
        requestMoreMemory();
    }

    public FixedSizeAllocator(long startAddress, long totalMemory, long entrySize) {
        this.entrySize = entrySize;
        this.chunkSize = 0;
        this.totalMemory = totalMemory;
        this.head = startAddress;

        // Create linked list of free entries
        long lastEntry = startAddress + (totalMemory / entrySize - 1) * entrySize;
        for (long entry = startAddress; entry < lastEntry; ) {
            unsafe.putAddress(entry, entry += entrySize);
        }
    }
    
    public FixedSizeAllocator(long startAddress, long totalMemory, long entrySize, long head) {
        this.entrySize = entrySize;
        this.chunkSize = 0;
        this.totalMemory = totalMemory;
        this.head = head;
    }

    public static void relocate(long currentPtr, long delta) {
        for (long entry; (entry = unsafe.getAddress(currentPtr)) != 0; currentPtr = entry) {
            entry = (entry & ADDR_MASK) + delta;
            unsafe.putAddress(currentPtr, entry);
        }
    }

    public int countFreePages() {
        int count = 0;
        for (long entry = head & ADDR_MASK; entry != 0; entry = unsafe.getAddress(entry)) {
            count++;
        }
        return count;
    }

    public long entrySize() {
        return entrySize;
    }

    public long chunkSize() {
        return chunkSize;
    }

    public long totalMemory() {
        return totalMemory;
    }

    public long malloc() {
        for (;;) {
            long head = this.head;
            long entry = head & ADDR_MASK;
            if (entry == 0) {
                requestMoreMemory();
                continue;
            }

            long nextEntry = unsafe.getAddress(entry);
            if (unsafe.compareAndSwapLong(this, headOffset, head, nextEntry + (head & COUNTER_MASK) + COUNTER_INC)) {
                return entry;
            }
        }
    }

    @Override
    public long malloc(int size) {
        assert size == entrySize;
        return malloc();
    }

    @Override
    public long calloc(int size) {
        long address = malloc(size);
        DirectMemory.clearSmall(address, size);
        return address;
    }

    @Override
    public void free(long entry) {
        long head;
        do {
            head = this.head;
            unsafe.putAddress(entry, head & ADDR_MASK);
        } while (!unsafe.compareAndSwapLong(this, headOffset, head, entry + (head & COUNTER_MASK) + COUNTER_INC));
    }

    @Override
    public void verify() {
        // Do nothing
    }

    // Ask system for a large chunk of memory and divide it internally into small entries
    private synchronized void requestMoreMemory() {
        if ((head & ADDR_MASK) != 0) {
            return;
        }

        long newChunk = getMemoryFromSystem(chunkSize);
        totalMemory += chunkSize;

        // Create linked list of free entries
        long lastEntry = newChunk + (chunkSize / entrySize - 1) * entrySize;
        for (long entry = newChunk; entry < lastEntry; ) {
            unsafe.putAddress(entry, entry += entrySize);
        }

        // Update allocator head to point to new chunk
        long head;
        do {
            head = this.head;
            unsafe.putAddress(lastEntry, head & ADDR_MASK);
        } while (!unsafe.compareAndSwapLong(this, headOffset, head, newChunk));
    }

    // Override this with a custom way to get a large chunk of the off-heap memory
    // Note that this memory becomes the allocator's property and is never returned back
    protected long getMemoryFromSystem(long size) {
        if (size == 0) {
            throw new OutOfMemoryException("FixedSizeAllocator has reached its limit");
        }
        return unsafe.allocateMemory(size);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy