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

jnr.ffi.provider.jffi.TransientNativeMemory Maven / Gradle / Ivy

package jnr.ffi.provider.jffi;

import com.kenai.jffi.PageManager;
import jnr.ffi.util.ref.FinalizablePhantomReference;
import jnr.ffi.util.ref.FinalizableReferenceQueue;

import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 *
 */
public class TransientNativeMemory extends DirectMemoryIO {
    /** Keeps strong references to the magazine until cleanup */
    @SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
    private static final Map referenceSet = new ConcurrentHashMap();

    private static final ThreadLocal currentMagazine = new ThreadLocal();
    private static final int PAGES_PER_MAGAZINE = 2;

    private final Sentinel sentinel;
    private final int size;
    
    public static DirectMemoryIO allocate(jnr.ffi.Runtime runtime, int size, int align, boolean clear) {
        if (size < 0) {
            throw new IllegalArgumentException("negative size: " + size);
        }

        if (size > 256) { /* Only use the transient allocator for small, short lived allocations */
            return new AllocatedDirectMemoryIO(runtime, size, clear);
        }

        Magazine magazine = currentMagazine.get();
        Sentinel sentinel = magazine != null ? magazine.sentinel() : null;
        long address;

        if (sentinel == null || (address = magazine.allocate(size, align)) == 0) {
            PageManager pm = PageManager.getInstance();
            long memory;
            do {
                memory = pm.allocatePages(PAGES_PER_MAGAZINE, PageManager.PROT_READ | PageManager.PROT_WRITE);
                if (memory != 0L && memory != -1L) {
                    break;
                }

                // No available pages; trigger a full GC to reclaim some memory
                System.gc();
                FinalizableReferenceQueue.cleanUpAll(); 
            } while (true);

            referenceSet.put(magazine = new Magazine(sentinel = new Sentinel(), pm, memory, PAGES_PER_MAGAZINE), Boolean.TRUE);
            currentMagazine.set(magazine);
            address = magazine.allocate(size, align);
        }

        return new TransientNativeMemory(runtime, sentinel, address, size);
    }


    TransientNativeMemory(jnr.ffi.Runtime runtime, Sentinel sentinel, long address, int size) {
        super(runtime, address);
        this.sentinel = sentinel;
        this.size = size;
    }

    private static long align(long offset, long align) {
        return (offset + align - 1L) & ~(align - 1L);
    }


    @Override
    public long size() {
        return this.size;
    }

    @Override
    public int hashCode() {
        return super.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof TransientNativeMemory) {
            TransientNativeMemory mem = (TransientNativeMemory) obj;
            return mem.size == size && mem.address() == address();
        }

        return super.equals(obj);
    }

    public final void dispose() { /* not-implemented */ }


    private static final class Sentinel {}
    
    /**
     * Holder for a group of memory allocations.
     */
    private static final class Magazine extends FinalizablePhantomReference {
        private final Reference sentinelReference;
        private final PageManager pm;
        private final long page;
        private final long end;
        private final int pageCount;
        private long memory;

        Magazine(Sentinel sentinel, PageManager pm, long page, int pageCount) {
            super(sentinel, NativeFinalizer.getInstance().getFinalizerQueue());
            this.sentinelReference = new WeakReference(sentinel);
            this.pm = pm;
            this.memory = this.page = page;
            this.pageCount = pageCount;
            this.end = memory + (pageCount * pm.pageSize());
        }
        
        Sentinel sentinel() {
            return sentinelReference.get();
        }

        long allocate(int size, int align) {
            long address = align(this.memory, align);
            if (address + size <= end) {
                memory = address + size;
                return address;
            }

            return 0L;
        }

        public final void finalizeReferent() {
            pm.freePages(page, pageCount);
            referenceSet.remove(this);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy