org.jruby.ext.ffi.jffi.TransientNativeMemoryIO Maven / Gradle / Ivy
/***** BEGIN LICENSE BLOCK *****
* Version: EPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Eclipse Public
* License Version 1.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.eclipse.org/legal/epl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Copyright (C) 2011 Wayne Meissner
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby.ext.ffi.jffi;
import com.kenai.jffi.PageManager;
import org.jruby.Ruby;
import org.jruby.ext.ffi.MemoryIO;
import org.jruby.util.PhantomReferenceReaper;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
final class TransientNativeMemoryIO extends BoundedNativeMemoryIO {
/** Keeps strong references to the memory bucket until cleanup */
private static final Map referenceSet = new ConcurrentHashMap();
private static final ThreadLocal> currentMagazine = new ThreadLocal>();
private static final int PAGES_PER_MAGAZINE = 1;
private final Object sentinel;
/**
* Allocates native memory, aligned to a minimum boundary.
*
* @param runtime The Ruby runtime
* @param size The number of bytes to allocate
* @param align The minimum alignment of the memory
* @param clear Whether the memory should be cleared (zeroed)
* @return A new {@link org.jruby.ext.ffi.AllocatedDirectMemoryIO}
*/
static MemoryIO allocateAligned(Ruby runtime, int size, int align, boolean clear) {
if (size > 1024) {
return AllocatedNativeMemoryIO.allocateAligned(runtime, size, align, clear);
}
Reference magazineReference = currentMagazine.get();
Magazine magazine = magazineReference != null ? magazineReference.get() : null;
Object sentinel = magazine != null ? magazine.sentinel() : null;
long address;
if (sentinel == null || (address = magazine.allocate(size, align)) == 0) {
PageManager pm = PageManager.getInstance();
long memory;
int pageCount = PAGES_PER_MAGAZINE;
do {
memory = pm.allocatePages(pageCount, 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();
} while (true);
referenceSet.put(magazine = new Magazine(sentinel = new Object(), pm, memory, pageCount), Boolean.TRUE);
currentMagazine.set(new SoftReference(magazine));
address = magazine.allocate(size, align);
}
return new TransientNativeMemoryIO(runtime, sentinel, address, size);
}
private TransientNativeMemoryIO(Ruby runtime, Object sentinel, long address, int size) {
super(runtime, address, size);
this.sentinel = sentinel;
}
private static long align(long offset, long align) {
return (offset + align - 1L) & ~(align - 1L);
}
/**
* Holder for a group of memory allocations.
*/
private static final class Magazine extends PhantomReferenceReaper
© 2015 - 2025 Weber Informatics LLC | Privacy Policy