* 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:
parent
ae6d3738d2
commit
7b622f8c54
1 changed files with 31 additions and 16 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue