Commit 565bb63b authored by Lubomir Bulej's avatar Lubomir Bulej

ShadowString, ShadowThread: get rid of overriden hashCode() and equals() and...

ShadowString, ShadowThread: get rid of overriden hashCode() and equals() and rely on the superclass implementation that only cares about net reference equality.
ShadowObject, ShadowString, ShadowThread: make a shadow object updatable from other object with the same net reference (required for partially-constructed special objects).
ShadowString, ShadowThread: get rid of setter methods in favor of updateFrom() method.
ShadowThread: make additional information about a thread atomically updateable.
ShadowClassTable: log when the JAVA_LANG_CLASS reference is updated.
ShadowClassTable, ShadowObjectTable: make the classes final.
ShadowObjectTable: use the updateFrom() method from shadow object instead of hand-coding updates in the registration method.
ShadowObjectTable: simplify the get() method by extracting code to create shadow objects in conjunction with the use of computeIfAbsent() method on the shadow object map.
parent 36a1c1e5
......@@ -6,9 +6,15 @@ import java.util.concurrent.atomic.AtomicReference;
import org.objectweb.asm.Type;
import ch.usi.dag.dislreserver.DiSLREServerFatalException;
import ch.usi.dag.dislreserver.util.Logging;
import ch.usi.dag.util.logging.Logger;
public class ShadowClassTable {
public final class ShadowClassTable {
private static final Logger __log = Logging.getPackageInstance ();
//
private static final int INITIAL_TABLE_SIZE = 10000;
......@@ -109,6 +115,7 @@ public class ShadowClassTable {
if (JAVA_LANG_CLASS.get () == null && __JAVA_LANG_CLASS_TYPE__.equals (type)) {
JAVA_LANG_CLASS.compareAndSet (null, result);
__log.trace ("initialized JAVA_LANG_CLASS");
}
return result;
......
......@@ -5,6 +5,8 @@ import java.util.Formatter;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import ch.usi.dag.dislreserver.DiSLREServerFatalException;
public class ShadowObject implements Formattable {
......@@ -122,6 +124,12 @@ public class ShadowObject implements Formattable {
@Override
public boolean equals (final Object object) {
//
// The equality of shadow objects should be based purely on
// the equality of the net reference. The value of some special
// shadow objects can be updated lazily, so we should not really
// compare the values.
//
if (object instanceof ShadowObject) {
final ShadowObject that = (ShadowObject) object;
return __netReference == that.__netReference;
......@@ -130,6 +138,36 @@ public class ShadowObject implements Formattable {
return false;
}
//
final void updateFrom (final ShadowObject other) {
//
// When updating value from another shadow object, the net reference
// of this and the other object must be the same.
//
if (__netReference != other.__netReference) {
throw new DiSLREServerFatalException (String.format (
"attempting to update object 0x%x using object 0x%x",
__netReference, other.__netReference
));
}
_updateFrom (other);
}
/**
* This method is intended to be override by the subclasses to implement
* updating of a shadow object's state. The caller of this method guarantees
* that the net reference of the object to update from will be the same
* as the net reference of this shadow object.
*
* @param object
* the {@link ShadowObject} instance to update from.
*/
protected void _updateFrom (final ShadowObject object) {
// do nothing
}
//
......
......@@ -9,7 +9,7 @@ import ch.usi.dag.dislreserver.util.Logging;
import ch.usi.dag.util.logging.Logger;
public class ShadowObjectTable {
public final class ShadowObjectTable {
private static final Logger __log = Logging.getPackageInstance ();
......@@ -17,58 +17,27 @@ public class ShadowObjectTable {
private static final int INITIAL_TABLE_SIZE = 10_000_000;
private static ConcurrentHashMap <Long, ShadowObject>
private static final ConcurrentHashMap <Long, ShadowObject>
shadowObjects = new ConcurrentHashMap <> (INITIAL_TABLE_SIZE);
//
public static void register (final ShadowObject newObj) {
if (newObj == null) {
public static void register (final ShadowObject object) {
if (object == null) {
__log.warn ("attempting to register a null shadow object");
return;
}
//
final long objID = newObj.getId ();
final ShadowObject exist = shadowObjects.putIfAbsent (objID, newObj);
if (exist != null) {
if (newObj.getId () == exist.getId ()) {
if (__log.traceIsLoggable ()) {
__log.trace ("re-registering shadow object %x", objID);
}
if (newObj.equals (exist)) {
return;
}
if (newObj instanceof ShadowString) {
if (exist instanceof ShadowString) {
final ShadowString existShadowString = (ShadowString) exist;
final ShadowString newShadowString = (ShadowString) newObj;
if (existShadowString.toString () == null) {
existShadowString.setValue (newShadowString.toString ());
return;
}
}
} else if (newObj instanceof ShadowThread) {
if (exist instanceof ShadowThread) {
final ShadowThread existShadowThread = (ShadowThread) exist;
final ShadowThread newShadowThread = (ShadowThread) newObj;
if (existShadowThread.getName () == null) {
existShadowThread.setName (newShadowThread.getName ());
existShadowThread.setDaemon (newShadowThread.isDaemon ());
return;
}
}
}
final long objectId = object.getId ();
final ShadowObject existing = shadowObjects.putIfAbsent (objectId, object);
if (existing != null) {
if (__log.traceIsLoggable ()) {
__log.trace ("updating shadow object 0x%x", objectId);
}
throw new DiSLREServerFatalException ("Duplicated net reference");
existing.updateFrom (object);
}
}
......@@ -86,41 +55,63 @@ public class ShadowObjectTable {
}
public static ShadowObject get (final long net_ref) {
final long objID = NetReferenceHelper.getObjectId (net_ref);
if (objID == 0) {
// reserved ID for null
public static ShadowObject get (final long netReference) {
final long objectId = NetReferenceHelper.getObjectId (netReference);
if (objectId == 0) {
// reserved for null
return null;
}
ShadowObject retVal = shadowObjects.get (objID);
final ShadowObject retVal = shadowObjects.get (objectId);
if (retVal != null) {
return retVal;
}
if (NetReferenceHelper.isClassInstance (objID)) {
//
// The corresponding shadow object was not found, so we create it.
// Only "normal" shadow objects will be generated here, not those
// representing instances of the Class class.
//
if (!NetReferenceHelper.isClassInstance (netReference)) {
return shadowObjects.computeIfAbsent (
objectId, key -> __createShadowObject (netReference)
);
} else {
throw new DiSLREServerFatalException ("Unknown class instance");
}
}
} else {
// Only common shadow object will be generated here
final ShadowClass klass = ShadowClassTable.get (NetReferenceHelper.getClassId (net_ref));
ShadowObject tmp = null;
if ("java.lang.String".equals (klass.getName ())) {
tmp = new ShadowString (net_ref, klass);
private static ShadowObject __createShadowObject (
final long netReference
) {
final ShadowClass shadowClass = ShadowClassTable.get (
NetReferenceHelper.getClassId (netReference)
);
if ("java.lang.String".equals (shadowClass.getName ())) {
if (__log.traceIsLoggable ()) {
final long objectId = NetReferenceHelper.getObjectId (netReference);
__log.trace ("creating uninitialized ShadowString for 0x%x", objectId);
}
} else if (isAssignableFromThread (klass)) {
tmp = new ShadowThread (net_ref, klass);
return new ShadowString (netReference, shadowClass);
} else {
tmp = new ShadowObject (net_ref, klass);
} else if (isAssignableFromThread (shadowClass)) {
if (__log.traceIsLoggable ()) {
final long objectId = NetReferenceHelper.getObjectId (netReference);
__log.trace ("creating uninitialized ShadowThread for 0x%x", objectId);
}
if ((retVal = shadowObjects.putIfAbsent (objID, tmp)) == null) {
retVal = tmp;
return new ShadowThread (netReference, shadowClass);
} else {
if (__log.traceIsLoggable ()) {
final long objectId = NetReferenceHelper.getObjectId (netReference);
__log.trace ("creating ShadowObject for 0x%x", objectId);
}
return retVal;
return new ShadowObject (netReference, shadowClass);
}
}
......
......@@ -34,44 +34,23 @@ public final class ShadowString extends ShadowObject {
return __value.get ();
}
void setValue (final String value) {
__value.updateAndGet (v -> value);
}
//
@Override
public boolean equals (final Object obj) {
// FIXME LB: This needs a comment!
if (super.equals (obj)) {
if (obj instanceof ShadowString) {
final ShadowString that = (ShadowString) obj;
if (__value != null) {
return __value.equals (that.__value);
}
}
}
return false;
}
@Override
public int hashCode () {
protected void _updateFrom (final ShadowObject object) {
//
// If two shadow strings are considered equal by the above equals()
// method, then they certainly have the same hash code, because it
// is derived from objectId (which in turn is derived from object tag,
// a.k.a. net reference) that is ensured to be equal by the call to
// super.equals().
// If the value of this string has not yet been initialized,
// update it from the other shadow string. The other string
// is expected to have the same net reference.
//
// If they are not equal, nobody cares about the hash code.
//
return super.hashCode ();
if (__value.get () == null) {
if (object instanceof ShadowString) {
final ShadowString other = (ShadowString) object;
__value.updateAndGet (v -> other.__value.get ());
}
}
}
//
@Override
......@@ -80,8 +59,10 @@ public final class ShadowString extends ShadowObject {
final int flags, final int width, final int precision
) {
super.formatTo (formatter, flags, width, precision);
if (__value != null) {
formatter.format (" <%s>", __value);
final String value = __value.get ();
if (value != null) {
formatter.format (" <%s>", value);
}
}
......
......@@ -2,6 +2,7 @@ package ch.usi.dag.dislreserver.shadow;
import java.util.Formattable;
import java.util.Formatter;
import java.util.concurrent.atomic.AtomicReference;
// TODO Make it clear that extra data have not yet been sent over the network.
......@@ -12,14 +13,29 @@ import java.util.Formatter;
//
public final class ShadowThread extends ShadowObject implements Formattable {
private String __name;
/**
* Data class for holding the additional information about a thread. Keeping
* the data in one class allows updating the thread information atomically.
*/
private static final class Info {
final String name;
final boolean isDaemon;
Info (final String name, final boolean isDaemon) {
this.name = name;
this.isDaemon = isDaemon;
}
}
//
private boolean __isDaemon;
private final AtomicReference <Info> __info;
//
ShadowThread (final long netReference, final ShadowClass shadowClass) {
super (netReference, shadowClass);
__info = new AtomicReference <> ();
}
ShadowThread (
......@@ -27,71 +43,42 @@ public final class ShadowThread extends ShadowObject implements Formattable {
final String name, final boolean isDaemon
) {
super (netReference, shadowClass);
__name = name;
__isDaemon = isDaemon;
__info = new AtomicReference <> (new Info (name, isDaemon));
}
// TODO warn user that it will return null when the ShadowThread is not yet sent.
// TODO LB: Consider switching to Optional
public String getName () {
return __name;
final Info info = __info.get ();
return (info != null) ? info.name : null;
}
// TODO warn user that it will return false when the ShadowThread is not yet sent.
// TODO LB: Switch to using Optional, or a Boolean reference
public boolean isDaemon () {
return __isDaemon;
}
void setName (final String name) {
__name = name;
}
void setDaemon (final boolean isDaemon) {
__isDaemon = isDaemon;
final Info info = __info.get ();
return (info != null) ? info.isDaemon : false;
}
//
@Override
public boolean equals (final Object object) {
protected void _updateFrom (final ShadowObject object) {
//
// TODO LB: Why do we need to check thread name or other fields?
// Comparing the (unique) net reference should be enough.
// Update the thread information from the other shadow thread.
// Both objects are expected to have the same net reference.
//
if (super.equals (object)) {
if (object instanceof ShadowThread) {
final ShadowThread that = (ShadowThread) object;
if (__name != null && __name.equals (that.__name)) {
return __isDaemon == that.__isDaemon;
}
if (object instanceof ShadowThread) {
final ShadowThread other = (ShadowThread) object;
final Info otherInfo = other.__info.get ();
if (otherInfo != null) {
this.__info.updateAndGet (v -> otherInfo);
}
}
return false;
}
@Override
public int hashCode () {
//
// TODO LB: Check ShadowThread.hashCode() -- it's needed.
//
// If two shadow threads are considered equal by the above equals()
// method, then they certainly have the same hash code, because it
// is derived from objectId (which in turn is derived from object tag,
// a.k.a. net reference) that is ensured to be equal by the call to
// super.equals().
//
// If they are not equal, nobody cares about the hash code.
//
return super.hashCode ();
}
//
@Override
......@@ -100,7 +87,9 @@ public final class ShadowThread extends ShadowObject implements Formattable {
final int flags, final int width, final int precision
) {
super.formatTo (formatter, flags, width, precision);
formatter.format (" <%s>", (__name != null) ? __name : "unknown");
final Info info = __info.get ();
formatter.format (" <%s>", (info != null) ? info.name : "unknown");
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment