No really, don't.
The test failure on arm that has been really driving me nuts for the
last month is finally solved, guess what was the cause of the problem?
That's right unsafe publication.
The "this" reference was published to JERI's ObjectTable prior to
construction completing, other threads could see the object in a
partially constructed state.
This caused problems with synchronization and a lost notifyAll() while
another thread was in a call wait(). notifyAll() was being called every
7ms so it was really being tested.
For more info, see River-420 on Jira.
This goes against a lot of books and literature on how to program Jini,
many examples (including our own) export during construction. This is
no longer how we should create proxy's, we need to get the message out
there. I'm a little short on time for the next few weeks, is anyone
able to publish something on our website?
The good news is I have fixes for all occurrences of exports duing
construction in the main src directory, the bad news is there are a
number of instances in the qa suite still to be fixed, although these
can be fixed relatively easily because they're not public api. The
really bad news is there's going to be a heap of client code out there
that has this issue.
The other good news is the qa refacting build is starting to look very
solid indeed. The last test failure on Arm was actually a segfault in
the JVM in an unrelated test, not our problem, unfortunately I didn't
find a log file or core dump.
Regards,
Peter.
This is what the code looks like now it's fixed, previously the proxy
was created during construction:
/**
* Listener that calls notifyAll on itself
*/
public class LeasedSpaceListener
implements RemoteEventListener, ServerProxyTrust, Serializable
{
private static Logger logger =
Logger.getLogger("com.sun.jini.qa.harness");
private boolean received = false;
private Object proxy;
private final Exporter exporter;
private final AccessControlContext context;
public LeasedSpaceListener(Configuration c) throws RemoteException {
try {
Exporter exporter = QAConfig.getDefaultExporter();
if (c instanceof com.sun.jini.qa.harness.QAConfiguration) {
exporter =
(Exporter) c.getEntry("test",
"outriggerListenerExporter",
Exporter.class);
}
this.exporter = exporter;
context = AccessController.getContext();
// Proxy was originally exported here, allowing "this" to escape.
} catch (ConfigurationException e) {
throw new RemoteException("Bad configuration", e);
}
}
private synchronized Object getProxy(){
if (proxy == null) {
proxy = AccessController.doPrivileged(new
PrivilegedAction<Object>(){
@Override
public Object run() {
try {
return exporter.export(LeasedSpaceListener.this);
} catch (ExportException ex) {
String message = "Proxy export failed for
LeaseListener";
logger.log(Level.WARNING, message , ex);
return null;
}
}
}, context);
}
return proxy;
}
public Object writeReplace() throws ObjectStreamException {
return getProxy();
}
public TrustVerifier getProxyVerifier() {
return new BasicProxyTrustVerifier(getProxy());
}
public void notify(RemoteEvent theEvent)
throws UnknownEventException, RemoteException {
// Perform logging outside the synchronized block so we don't
affect
// timing.
java.util.Date date = new java.util.Date();
synchronized (this){
received = true;
this.notifyAll();
}
logger.log(Level.INFO, "notify called at {0}", date.getTime());
}
/**
* @return the received
*/
public synchronized boolean isReceived() {
return received;
}
/**
* @param received the received to set
*/
public synchronized void setReceived(boolean received) {
this.received = received;
}
}