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; ...@@ -6,9 +6,15 @@ import java.util.concurrent.atomic.AtomicReference;
import org.objectweb.asm.Type; import org.objectweb.asm.Type;
import ch.usi.dag.dislreserver.DiSLREServerFatalException; 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; private static final int INITIAL_TABLE_SIZE = 10000;
...@@ -109,6 +115,7 @@ public class ShadowClassTable { ...@@ -109,6 +115,7 @@ public class ShadowClassTable {
if (JAVA_LANG_CLASS.get () == null && __JAVA_LANG_CLASS_TYPE__.equals (type)) { if (JAVA_LANG_CLASS.get () == null && __JAVA_LANG_CLASS_TYPE__.equals (type)) {
JAVA_LANG_CLASS.compareAndSet (null, result); JAVA_LANG_CLASS.compareAndSet (null, result);
__log.trace ("initialized JAVA_LANG_CLASS");
} }
return result; return result;
......
...@@ -5,6 +5,8 @@ import java.util.Formatter; ...@@ -5,6 +5,8 @@ import java.util.Formatter;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier; import java.util.function.Supplier;
import ch.usi.dag.dislreserver.DiSLREServerFatalException;
public class ShadowObject implements Formattable { public class ShadowObject implements Formattable {
...@@ -122,6 +124,12 @@ public class ShadowObject implements Formattable { ...@@ -122,6 +124,12 @@ public class ShadowObject implements Formattable {
@Override @Override
public boolean equals (final Object object) { 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) { if (object instanceof ShadowObject) {
final ShadowObject that = (ShadowObject) object; final ShadowObject that = (ShadowObject) object;
return __netReference == that.__netReference; return __netReference == that.__netReference;
...@@ -130,6 +138,36 @@ public class ShadowObject implements Formattable { ...@@ -130,6 +138,36 @@ public class ShadowObject implements Formattable {
return false; 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; ...@@ -9,7 +9,7 @@ import ch.usi.dag.dislreserver.util.Logging;
import ch.usi.dag.util.logging.Logger; import ch.usi.dag.util.logging.Logger;
public class ShadowObjectTable { public final class ShadowObjectTable {
private static final Logger __log = Logging.getPackageInstance (); private static final Logger __log = Logging.getPackageInstance ();
...@@ -17,58 +17,27 @@ public class ShadowObjectTable { ...@@ -17,58 +17,27 @@ public class ShadowObjectTable {
private static final int INITIAL_TABLE_SIZE = 10_000_000; 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); shadowObjects = new ConcurrentHashMap <> (INITIAL_TABLE_SIZE);
// //
public static void register (final ShadowObject newObj) { public static void register (final ShadowObject object) {
if (newObj == null) { if (object == null) {
__log.warn ("attempting to register a null shadow object"); __log.warn ("attempting to register a null shadow object");
return; return;
} }
// //
final long objID = newObj.getId (); final long objectId = object.getId ();
final ShadowObject exist = shadowObjects.putIfAbsent (objID, newObj); final ShadowObject existing = shadowObjects.putIfAbsent (objectId, object);
if (existing != null) {
if (exist != null) { if (__log.traceIsLoggable ()) {
if (newObj.getId () == exist.getId ()) { __log.trace ("updating shadow object 0x%x", objectId);
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;
}
}
}
} }
throw new DiSLREServerFatalException ("Duplicated net reference"); existing.updateFrom (object);
} }
} }
...@@ -86,41 +55,63 @@ public class ShadowObjectTable { ...@@ -86,41 +55,63 @@ public class ShadowObjectTable {
} }
public static ShadowObject get (final long net_ref) { public static ShadowObject get (final long netReference) {
final long objID = NetReferenceHelper.getObjectId (net_ref); final long objectId = NetReferenceHelper.getObjectId (netReference);
if (objID == 0) { if (objectId == 0) {
// reserved ID for null // reserved for null
return null; return null;
} }
ShadowObject retVal = shadowObjects.get (objID); final ShadowObject retVal = shadowObjects.get (objectId);
if (retVal != null) { if (retVal != null) {
return retVal; 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"); 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 ())) { private static ShadowObject __createShadowObject (
tmp = new ShadowString (net_ref, klass); 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)) { return new ShadowString (netReference, shadowClass);
tmp = new ShadowThread (net_ref, klass);
} else { } else if (isAssignableFromThread (shadowClass)) {
tmp = new ShadowObject (net_ref, klass); 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) { return new ShadowThread (netReference, shadowClass);
retVal = tmp;
} 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 { ...@@ -34,44 +34,23 @@ public final class ShadowString extends ShadowObject {
return __value.get (); return __value.get ();
} }
void setValue (final String value) {
__value.updateAndGet (v -> value);
}
// //
@Override @Override
public boolean equals (final Object obj) { protected void _updateFrom (final ShadowObject object) {
// 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 () {
// //
// If two shadow strings are considered equal by the above equals() // If the value of this string has not yet been initialized,
// method, then they certainly have the same hash code, because it // update it from the other shadow string. The other string
// is derived from objectId (which in turn is derived from object tag, // is expected to have the same net reference.
// 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. if (__value.get () == null) {
// if (object instanceof ShadowString) {
return super.hashCode (); final ShadowString other = (ShadowString) object;
__value.updateAndGet (v -> other.__value.get ());
}
}
} }
// //
@Override @Override
...@@ -80,8 +59,10 @@ public final class ShadowString extends ShadowObject { ...@@ -80,8 +59,10 @@ public final class ShadowString extends ShadowObject {
final int flags, final int width, final int precision final int flags, final int width, final int precision
) { ) {
super.formatTo (formatter, flags, width, 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; ...@@ -2,6 +2,7 @@ package ch.usi.dag.dislreserver.shadow;
import java.util.Formattable; import java.util.Formattable;
import java.util.Formatter; 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. // TODO Make it clear that extra data have not yet been sent over the network.
...@@ -12,14 +13,29 @@ import java.util.Formatter; ...@@ -12,14 +13,29 @@ import java.util.Formatter;
// //
public final class ShadowThread extends ShadowObject implements Formattable { 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) { ShadowThread (final long netReference, final ShadowClass shadowClass) {
super (netReference, shadowClass); super (netReference, shadowClass);
__info = new AtomicReference <> ();
} }
ShadowThread ( ShadowThread (
...@@ -27,71 +43,42 @@ public final class ShadowThread extends ShadowObject implements Formattable { ...@@ -27,71 +43,42 @@ public final class ShadowThread extends ShadowObject implements Formattable {
final String name, final boolean isDaemon final String name, final boolean isDaemon
) { ) {
super (netReference, shadowClass); super (netReference, shadowClass);
__info = new AtomicReference <> (new Info (name, isDaemon));
__name = name;
__isDaemon = isDaemon;
} }
// TODO warn user that it will return null when the ShadowThread is not yet sent. // TODO warn user that it will return null when the ShadowThread is not yet sent.
// TODO LB: Consider switching to Optional
public String getName () { 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 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 () { public boolean isDaemon () {
return __isDaemon; final Info info = __info.get ();
} return (info != null) ? info.isDaemon : false;
void setName (final String name) {
__name = name;
}
void setDaemon (final boolean isDaemon) {
__isDaemon = isDaemon;
} }
// //
@Override @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? // Update the thread information from the other shadow thread.
// Comparing the (unique) net reference should be enough. // Both objects are expected to have the same net reference.
// //
if (super.equals (object)) { if (object instanceof ShadowThread) {
if (object instanceof ShadowThread) { final ShadowThread other = (ShadowThread) object;
final ShadowThread that = (ShadowThread) object; final Info otherInfo = other.__info.get ();
if (__name != null && __name.equals (that.__name)) { if (otherInfo != null) {
return __isDaemon == that.__isDaemon; 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 @Override
...@@ -100,7 +87,9 @@ public final class ShadowThread extends ShadowObject implements Formattable { ...@@ -100,7 +87,9 @@ public final class ShadowThread extends ShadowObject implements Formattable {
final int flags, final int width, final int precision final int flags, final int width, final int precision
) { ) {
super.formatTo (formatter, flags, width, 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