package org.jruby.runtime;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReferenceArray;
import org.jruby.RubyModule;
import org.jruby.java.proxies.JavaProxy;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.WeakIdentityHashMap;
public class ObjectSpace {
private final ReferenceQueue<Object> deadReferences = new ReferenceQueue<>();
private final ReferenceQueue<ObjectGroup> objectGroupReferenceQueue = new ReferenceQueue<>();
private WeakReferenceListNode top;
private ReferenceQueue deadIdentityReferences = new ReferenceQueue();
private final Map<Long, IdReference> identities = new HashMap<>(64);
private final Map<IRubyObject, Long> identitiesByObject = new WeakIdentityHashMap(64);
private static final AtomicLong maxId = new AtomicLong(1000);
private final ThreadLocal<Reference<ObjectGroup>> currentObjectGroup = new ThreadLocal<>();
private Reference<GroupSweeper> groupSweeperReference;
public void registerObjectId(long id, IRubyObject object) {
synchronized (identities) {
cleanIdentities();
identities.put(id, new IdReference(object, id, deadIdentityReferences));
identitiesByObject.put(object, id);
}
}
public static long calculateObjectId(Object object) {
return maxId.getAndIncrement() * 2;
}
public long createAndRegisterObjectId(IRubyObject rubyObject) {
synchronized (identities) {
Long longId = identitiesByObject.get(rubyObject);
if (longId == null) {
longId = createId(rubyObject);
}
return longId.longValue();
}
}
private long createId(IRubyObject object) {
long id = calculateObjectId(object);
registerObjectId(id, object);
return id;
}
public IRubyObject id2ref(long id) {
synchronized (identities) {
cleanIdentities();
IdReference reference = identities.get(id);
if (reference == null) {
return null;
}
return reference.get();
}
}
private void cleanIdentities() {
IdReference ref;
while ((ref = (IdReference) deadIdentityReferences.poll()) != null) {
identities.remove(ref.id);
}
}
@Deprecated
public long idOf(IRubyObject rubyObject) {
return createAndRegisterObjectId(rubyObject);
}
public void addFinalizer(IRubyObject object, IRubyObject proc) {
object.addFinalizer(proc);
}
public void removeFinalizers(long id) {
IRubyObject object = id2ref(id);
if (object != null) {
object.removeFinalizers();
}
}
public void add(IRubyObject object) {
if (true && object.getMetaClass() != null && !(object instanceof JavaProxy)) {
if (object.isFrozen()) return;
getObjectGroup().add(object);
} else {
addIndividualWeakReference(object);
}
}
private synchronized void addIndividualWeakReference(IRubyObject object) {
cleanup(deadReferences);
top = new WeakReferenceListNode<Object>(object, deadReferences, top);
}
private synchronized void registerGroupSweeper() {
if (groupSweeperReference == null || groupSweeperReference.get() == null) {
groupSweeperReference = new WeakReference<GroupSweeper>(new GroupSweeper());
}
}
private synchronized void splitObjectGroups() {
cleanup(objectGroupReferenceQueue);
WeakReferenceListNode node = top;
while (node != null) {
Object obj = node.get();
if (obj instanceof ObjectGroup) {
ObjectGroup objectGroup = (ObjectGroup) obj;
for (int i = 0; i < objectGroup.size(); i++) {
IRubyObject rubyObject = objectGroup.set(i, null);
if (rubyObject != null) {
top = new WeakReferenceListNode<Object>(rubyObject, deadReferences, top);
}
}
}
node = node.nextNode;
}
}
public synchronized Iterator iterator(RubyModule rubyClass) {
final ArrayList<Reference<Object>> objList = new ArrayList<>(64);
WeakReferenceListNode current = top;
while (current != null) {
Object obj = current.get();
if (obj instanceof IRubyObject) {
IRubyObject rubyObject = (IRubyObject) obj;
if (rubyClass.isInstance(rubyObject)) {
objList.add(current);
}
} else if (obj instanceof ObjectGroup) {
for (IRubyObject rubyObject : (ObjectGroup) obj) {
if (rubyObject != null && rubyClass.isInstance(rubyObject)) {
objList.add(new WeakReference<Object>(rubyObject));
}
}
}
current = current.nextNode;
}
return new Iterator() {
final Iterator<Reference<Object>> iter = objList.iterator();
public boolean hasNext() {
throw new UnsupportedOperationException();
}
public Object next() {
Object obj = null;
while (iter.hasNext()) {
obj = iter.next().get();
if (obj != null) break;
}
return obj;
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
private void cleanup(ReferenceQueue<?> referenceQueue) {
WeakReferenceListNode reference;
while ((reference = (WeakReferenceListNode) referenceQueue.poll()) != null) {
reference.remove();
}
}
private class WeakReferenceListNode<T> extends WeakReference<T> {
private WeakReferenceListNode prevNode;
private WeakReferenceListNode nextNode;
public WeakReferenceListNode(T referent, ReferenceQueue<T> queue, WeakReferenceListNode<?> next) {
super(referent, queue);
this.nextNode = next;
if (next != null) {
next.prevNode = this;
}
}
private void remove() {
if (prevNode != null) {
prevNode.nextNode = nextNode;
} else {
top = nextNode;
}
if (nextNode != null) {
nextNode.prevNode = prevNode;
}
}
}
private static class IdReference extends WeakReference<IRubyObject> {
final long id;
IdReference(IRubyObject object, long id, ReferenceQueue queue) {
super(object, queue);
this.id = id;
}
}
private ObjectGroup getObjectGroup() {
Reference<ObjectGroup> ref = currentObjectGroup.get();
ObjectGroup objectGroup = ref != null ? ref.get() : null;
return objectGroup != null && !objectGroup.isFull() ? objectGroup : addObjectGroup();
}
private synchronized ObjectGroup addObjectGroup() {
cleanup(objectGroupReferenceQueue);
ObjectGroup objectGroup;
WeakReferenceListNode<ObjectGroup> ref = new WeakReferenceListNode<ObjectGroup>(objectGroup = new ObjectGroup(),
objectGroupReferenceQueue, top);
currentObjectGroup.set(ref);
top = ref;
if (groupSweeperReference == null) registerGroupSweeper();
return objectGroup;
}
private static final class ObjectGroup extends AbstractList<IRubyObject> {
private static final int MAX_OBJECTS_PER_GROUP = 64;
private final AtomicReferenceArray<IRubyObject> objects = new AtomicReferenceArray<IRubyObject>(MAX_OBJECTS_PER_GROUP);
private int nextIndex = 0;
public boolean add(IRubyObject obj) {
obj.getMetaClass().getObjectGroupAccessorForWrite().set(obj, this);
objects.set(nextIndex, obj);
++nextIndex;
return true;
}
@Override
public IRubyObject get(int index) {
return objects.get(index);
}
@Override
public IRubyObject set(int index, IRubyObject element) {
return objects.getAndSet(index, element);
}
private boolean isFull() {
return nextIndex >= objects.length();
}
public int size() {
return objects.length();
}
}
private final class GroupSweeper {
@Override
protected void finalize() throws Throwable {
try {
splitObjectGroups();
} finally {
registerGroupSweeper();
super.finalize();
}
}
}
}