* Only synchronize internal getter for per-thread scope, don't synchronize public get() or put()

to avoid deadlocks.
* Do not synchronize PropertyRecorder methods, instead mark fields as volatile.
* Check for "global" reference before doing the default lookup in get().
This commit is contained in:
hns 2006-04-26 15:52:25 +00:00
parent ae6d3738d2
commit 7b622f8c54

View file

@ -42,8 +42,8 @@ public class GlobalObject extends ImporterTopLevel implements PropertyRecorder {
GlobalObject sharedGlobal = null; GlobalObject sharedGlobal = null;
// fields to implement PropertyRecorder // fields to implement PropertyRecorder
private boolean isRecording = false; private volatile boolean isRecording = false;
private HashSet changedProperties; private volatile HashSet changedProperties;
/** /**
* Creates a new GlobalObject object. * Creates a new GlobalObject object.
@ -106,7 +106,7 @@ public class GlobalObject extends ImporterTopLevel implements PropertyRecorder {
* @param start * @param start
* @param value * @param value
*/ */
public synchronized void put(String name, Scriptable start, Object value) { public void put(String name, Scriptable start, Object value) {
// register property for PropertyRecorder interface // register property for PropertyRecorder interface
if (isRecording) { if (isRecording) {
changedProperties.add(name); changedProperties.add(name);
@ -115,14 +115,14 @@ public class GlobalObject extends ImporterTopLevel implements PropertyRecorder {
} }
/** /**
* Override ScriptableObject.get() to synchronize it, use the per-thread scope if possible, * Override ScriptableObject.get() to use the per-thread scope if possible,
* and return the per-thread scope for "global". * and return the per-thread scope for "global".
* *
* @param name * @param name
* @param start * @param start
* @return the property for the given name * @return the property for the given name
*/ */
public synchronized Object get(String name, Scriptable start) { public Object get(String name, Scriptable start) {
// register property for PropertyRecorder interface // register property for PropertyRecorder interface
if (isRecording) { if (isRecording) {
changedProperties.add(name); changedProperties.add(name);
@ -130,29 +130,44 @@ public class GlobalObject extends ImporterTopLevel implements PropertyRecorder {
Context cx = Context.getCurrentContext(); Context cx = Context.getCurrentContext();
GlobalObject scope = (GlobalObject) cx.getThreadLocal("threadscope"); GlobalObject scope = (GlobalObject) cx.getThreadLocal("threadscope");
if (scope != null) { if (scope != null) {
Object obj = scope.get(name);
if (obj != null && obj != NOT_FOUND) {
return obj;
}
// make thread scope accessible as "global" // make thread scope accessible as "global"
if ("global".equals(name)) { if ("global".equals(name)) {
return scope; return scope;
} }
// use synchronized get on fast changing per-thread scopes just to be sure
Object obj = scope.getSynchronized(name);
if (obj != null && obj != NOT_FOUND) {
return obj;
}
} }
if (sharedGlobal != null) { if (sharedGlobal != null) {
return sharedGlobal.get(name); // we're a per-thread scope
return sharedGlobal.getInternal(name);
} else { } else {
// we are the shared scope
return super.get(name, start); return super.get(name, start);
} }
} }
/** /**
* Directly get a property, bypassing the extra stuff in get(String, Scriptable) * Directly get a property, bypassing the extra stuff in get(String, Scriptable).
* *
* @param name * @param name
* @return the property for the given name * @return the property for the given name
*/ */
protected synchronized Object get(String name) { protected Object getInternal(String name) {
return super.get(name, this);
}
/**
* Directly get a property, bypassing the extra stuff in get(String, Scriptable),
* and synchronizing in order to prevent cache read errors on multiprocessor systems.
* TODO: we need extensive testing in order to tell whether this is really necessary.
*
* @param name
* @return the property for the given name
*/
protected synchronized Object getSynchronized(String name) {
return super.get(name, this); return super.get(name, this);
} }
@ -707,7 +722,7 @@ public class GlobalObject extends ImporterTopLevel implements PropertyRecorder {
/** /**
* Tell this PropertyRecorder to start recording changes to properties * Tell this PropertyRecorder to start recording changes to properties
*/ */
public synchronized void startRecording() { public void startRecording() {
changedProperties = new HashSet(); changedProperties = new HashSet();
isRecording = true; isRecording = true;
} }
@ -715,7 +730,7 @@ public class GlobalObject extends ImporterTopLevel implements PropertyRecorder {
/** /**
* Tell this PropertyRecorder to stop recording changes to properties * Tell this PropertyRecorder to stop recording changes to properties
*/ */
public synchronized void stopRecording() { public void stopRecording() {
isRecording = false; isRecording = false;
} }
@ -725,14 +740,14 @@ public class GlobalObject extends ImporterTopLevel implements PropertyRecorder {
* *
* @return a Set containing the names of changed properties * @return a Set containing the names of changed properties
*/ */
public synchronized Set getChangeSet() { public Set getChangeSet() {
return changedProperties; return changedProperties;
} }
/** /**
* Clear the set of changed properties. * Clear the set of changed properties.
*/ */
public synchronized void clearChangeSet() { public void clearChangeSet() {
changedProperties = null; changedProperties = null;
} }
} }