I'm using AdoptOpenJDK jdk81212-b04 on Ubuntu Linux, running on Eclipse 4.13. I have a method in Swing that creates a lambda inside a lambda; both probably get called on separate threads. It looks like this (pseudocode):
private SwingAction createAction(final Data payload) {
System.out.println(System.identityHashCode(payload));
return new SwingAction(() -> {
System.out.println(System.identityHashCode(payload));
//do stuff
//show an "are you sure?" modal dialog and get a response
//show a file selection dialog
//when the dialog completes, create a worker and show a status:
showStatusDialogWithWorker(() -> new SwingWorker() {
protected String doInBackground() {
save(payload);
}
});
You can see that the lambdas are several layers deep, and that eventually the "payload" which was captured gets saved to a file, more or less.
But before considering the layers and the threads, let's go directly to the problem:
The first time I call createAction(), the two System.out.println() methods print the exact same hash code, indicating that the captured payload inside the lambda is the same I passed to createAction().
If I later call createAction() with a different payload, the two System.out.println() values printed are different! In particular, the second line printed always indicates the same value that was printed in step 1!!
I can repeat this over and over; the actual payload passed will keep getting a different identity hash code, while the second line printed (inside the lambda) will stay the same! Eventually something will click and suddenly the numbers will be the same again, but then they will diverge with the same behavior.
Is Java somehow caching the lambda, as well as the argument that is going to the lambda? But how is this possible? The payload argument is marked final, and besides, lambda captures have to be effectively final anyway!
Is there a Java 8 bug that doesn't recognize a lambda should not be cached if the captured variable is several lambdas deep?
Is there a Java 8 bug that is caching lambdas and lambda arguments across threads?
Or is there something that I don't understand about lambda capture method arguments versus method local variables?
First Failed Workaround Attempt
Yesterday I thought I could prevent this behavior simply by capturing the method parameter locally on the method stack:
private SwingAction createAction(final Data payload) {
final Data theRealPayload = payload;
System.out.println(System.identityHashCode(theRealPayload));
return new SwingAction(() -> {
System.out.println(System.identityHashCode(theRealPayload));
//do stuff
//show an "are you sure?" modal dialog and get a response
//show a file selection dialog
//when the dialog completes, create a worker and show a status:
showStatusDialogWithWorker(() -> new SwingWorker() {
protected String doInBackground() {
save(theRealPayload);
}
});
With that single line Data theRealPayload = payload, if I henceforth used theRealPayload instead of payload suddenly the bug no longer appeared, and every single time I called createAction(), the two printed lines indicate exactly the same instance of the captured variable.
However today this workaround has stopped working.
Separate Bug Fix Addresses Problem; But Why?
I found a separate bug that was throwing an exception inside showStatusDialogWithWorker(). Basically showStatusDialogWithWorker() is supposed to create the worker (in the passed lambda) and show a status dialog until the worker is finished. There was a bug that would create the worker correctly, but fail to create the dialog, throwing an exception that would bubble up and never get caught. I fixed this bug so that the showStatusDialogWithWorker() successfully shows the dialog when the worker is running and then closes it after the worker finishes. I can now no longer reproduce the lambda capture issue.
But why does something inside showStatusDialogWithWorker() relate to the problem at all? When I was printing out System.identityHashCode() outside and inside the lambda, and the values were differing, this was happening before showStatusDialogWithWorker() was being called, and before the exception was being thrown. Why should a later exception make a difference?
Besides, the fundamental question remains: how is it even possible that a final parameter passed by a method and captured by a lambda could ever change?
how is it even possible that a final parameter passed by a method and captured by a lambda could ever change?
It is not. As you have pointed out, unless there is a bug in the JVM, this cannot happen.
This is very difficult to pin down without a minimal reproducible example. Here are the observations you have made:
The first time I call createAction(), the two System.out.println() methods print the exact same hash code, indicating that the captured payload inside the lambda is the same I passed to createAction().
If I later call createAction() with a different payload, the two System.out.println() values printed are different! In particular, the second line printed always indicates the same value that was printed in step 1!!
One possible explanation that fits the evidence is that the lambda being called the second time is in fact the lambda from the first run, and the lambda that was created by the second run has been discarded. That would give the above observations, and would place the bug inside the code that you have not shown here.
Perhaps you could add some extra logging to record:
a) the ids of any lambdas created inside createAction at creation time (I think you would need to change the lambdas into anon classes that implement the callback interfaces with logging in their constructors)
b) the ids of the lambdas at the time of their invocation
I think that the above logging would be sufficient to prove or disprove my theory.
GL!
I don't find anything wrong with the captured lambdas in your code.
Your workaround doesn't change the local capturing as you just declared a new variable an assigned it to to the same reference.
The problem is most likely on your handling of the SwingAction created object.
I wouldn't be surprised if you find that printing the IdentityHashCode of that returned object where you are using it yields consistent values with your payloads. Or in other words, you may be using a previous reference to SwingAction.
Besides, the fundamental question remains: how is it even possible
that a final parameter passed by a method and captured by a lambda
could ever change?
This shouldn't be possible at the reference level, the variable can't be reassigned. The reference passed may be mutable itself though, but that doesn't apply to this case.
Related
I Use Java 15 openjdk and tried on Java 14
Include details about your goal
I'm making a RMI system in order to make instances of any object synchronisable between computers and make multiple engines works on the same object. With my system, when i want to synchronise an object, i generate a class that will extends the object class and then override every methods of the class in order to control if the method call must be delegated to the object or to perform a RMI request instead.
The class generation is divided in two part :
I generate the source code in which every non-final methods are overriden in order to add my delegating system. the code is generated in scala language, and this class yet does not extends from the class of the object to synchronise because scala don't let me override some methods, even if they are not final (it's a thing with scala's setters and getters), then i compile the code using the Scala Compiler.
I use javassist to modify the generated class and make it extends the expected class + i add some methods and modify anonfun methods in order to perform super calls.
What is happening when i see the exception ?
I have a module Server and a module Client, they both run the same code except that they have different implementation of the Engine module, which is where i define all features of my framework, my RMI system is a feature of the framework for example, and for this RMI system, absolutely no code is runned into the implementations modules.
In the Engine module, I've made a player command in my program that creates a synchronised list (of type scala.collections.mutable.ListBuffer), thus, with this command i can add some player objects to the list. for example, if i add a player to the list, it will be added in the local list of the program that executes the command, and a RMI request will be done to the other computers that hosts the list in order to make them add the same object in their list.
Now, if i enter something like player add id=7 name=testPlayer x=78 y=23, it will start to get completely weird :
First of all, this exception occurs only when the server program handles the RMI request, which is completely nonsensical because as i said, nothing is run in the implementation.
For example, if i enter the command on the server, the player will be added in it's local list, and a RMI request for the add method will be sent from the server to the client, but on the client, as it will handle the request, will not crash at all (i can spam the command, nothing breaks). So, if the server handles the RMI request, it throws me this error :
java.lang.NegativeArraySizeException: -531627648
at java.base/java.lang.Class.copyFields(Class.java:3538)
at java.base/java.lang.Class.getDeclaredFields(Class.java:2341)
at fr.linkit.engine.connection.packet.serialization.tree.ClassDescription.listAllSerialFields$1(ClassDescription.scala:45)
at fr.linkit.engine.connection.packet.serialization.tree.ClassDescription.listSerializableFields(ClassDescription.scala:52)
at fr.linkit.engine.connection.packet.serialization.tree.ClassDescription.<init>(ClassDescription.scala:23)
at fr.linkit.engine.connection.packet.serialization.tree.DefaultClassProfile.<init>(DefaultClassProfile.scala:23)
at fr.linkit.engine.connection.packet.serialization.tree.DefaultSerialContext.$anonfun$getClassProfile$1(DefaultSerialContext.scala:61)
at scala.collection.mutable.HashMap.getOrElseUpdate(HashMap.scala:454)
at fr.linkit.engine.connection.packet.serialization.tree.DefaultSerialContext.getClassProfile(DefaultSerialContext.scala:61)
at fr.linkit.engine.connection.packet.serialization.tree.DefaultNodeFinder.getClassProfile(DefaultNodeFinder.scala:49)
at fr.linkit.engine.connection.packet.serialization.tree.DefaultNodeFinder.getSerialNodeForType(DefaultNodeFinder.scala:36)
at fr.linkit.engine.connection.packet.serialization.tree.DefaultNodeFinder.getSerialNodeForRef(DefaultNodeFinder.scala:44)
at fr.linkit.engine.connection.packet.serialization.tree.DefaultNodeFinder.$anonfun$listNodes$1(DefaultNodeFinder.scala:55)
at scala.collection.immutable.List.map(List.scala:246)
at fr.linkit.engine.connection.packet.serialization.tree.DefaultNodeFinder.listNodes(DefaultNodeFinder.scala:53)
at fr.linkit.engine.connection.packet.serialization.tree.nodes.ObjectNode$ObjectSerialNode.serialize(ObjectNode.scala:58)
at fr.linkit.engine.connection.packet.serialization.tree.nodes.ArrayNode$ArraySerialNode.serializeItem$1(ArrayNode.scala:84)
at fr.linkit.engine.connection.packet.serialization.tree.nodes.ArrayNode$ArraySerialNode.$anonfun$serialize$1(ArrayNode.scala:68)
at fr.linkit.engine.connection.packet.serialization.tree.nodes.ArrayNode$ArraySerialNode.$anonfun$serialize$1$adapted(ArrayNode.scala:67)
at scala.collection.ArrayOps$.foreach$extension(ArrayOps.scala:1323)
at fr.linkit.engine.connection.packet.serialization.tree.nodes.ArrayNode$ArraySerialNode.serialize(ArrayNode.scala:67)
at fr.linkit.engine.connection.packet.serialization.tree.LengthSign$.$anonfun$of$2(LengthSign.scala:62)
at fr.linkit.engine.connection.packet.serialization.tree.LengthSign$.$anonfun$of$2$adapted(LengthSign.scala:54)
at scala.collection.immutable.List.foreach(List.scala:333)
at fr.linkit.engine.connection.packet.serialization.tree.LengthSign$.of(LengthSign.scala:54)
at fr.linkit.engine.connection.packet.serialization.tree.nodes.ObjectNode$ObjectSerialNode.serialize(ObjectNode.scala:63)
at fr.linkit.engine.connection.packet.serialization.tree.nodes.ArrayNode$ArraySerialNode.serializeItem$1(ArrayNode.scala:84)
at fr.linkit.engine.connection.packet.serialization.tree.nodes.ArrayNode$ArraySerialNode.$anonfun$serialize$1(ArrayNode.scala:68)
at fr.linkit.engine.connection.packet.serialization.tree.nodes.ArrayNode$ArraySerialNode.$anonfun$serialize$1$adapted(ArrayNode.scala:67)
at scala.collection.ArrayOps$.foreach$extension(ArrayOps.scala:1323)
at fr.linkit.engine.connection.packet.serialization.tree.nodes.ArrayNode$ArraySerialNode.serialize(ArrayNode.scala:67)
at fr.linkit.engine.connection.packet.serialization.DefaultSerializer.serialize(DefaultSerializer.scala:34)
at fr.linkit.engine.connection.packet.serialization.SimpleTransferInfo.makeSerial(SimpleTransferInfo.scala:38)
at fr.linkit.engine.connection.packet.serialization.LazyPacketSerializationResult.bytes$lzycompute(LazyPacketSerializationResult.scala:27)
at fr.linkit.engine.connection.packet.serialization.LazyPacketSerializationResult.bytes(LazyPacketSerializationResult.scala:27)
at fr.linkit.engine.connection.packet.serialization.LazyPacketSerializationResult.writableBytes$lzycompute(LazyPacketSerializationResult.scala:30)
at fr.linkit.engine.connection.packet.serialization.LazyPacketSerializationResult.writableBytes(LazyPacketSerializationResult.scala:29)
at fr.linkit.server.connection.ExternalConnectionSession.send(ExternalConnectionSession.scala:53)
at fr.linkit.server.connection.ServerExternalConnection.$anonfun$sendPacket$1(ServerExternalConnection.scala:100)
at fr.linkit.engine.local.concurrency.pool.BusyWorkerPool.$anonfun$runLater$1(BusyWorkerPool.scala:351)
at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18)
at scala.util.Try$.apply(Try.scala:210)
at fr.linkit.engine.local.concurrency.pool.BusyWorkerPool.$anonfun$runLaterControl$1(BusyWorkerPool.scala:122)
at fr.linkit.engine.local.concurrency.SimpleAsyncTask.runTask(SimpleAsyncTask.scala:75)
at fr.linkit.engine.local.concurrency.pool.BusyWorkerThread.runTask(BusyWorkerThread.scala:67)
at fr.linkit.engine.local.concurrency.pool.BusyWorkerPool.$anonfun$runLaterControl$2(BusyWorkerPool.scala:132)
The exception occurs during the serialization of the response packet, (as we are performing a Remote Method Invocation, we have to send the return value of the method). In this case, the add method returns the instance of the list, so the packet will contains the instance of the list as a result value (sounds useless but i have to deal with this kind of situations). When the list gets serialized, it crashes here :
def listAllSerialFields(cl: Class[_]): Seq[Field] = {
if (cl == null)
return Seq.empty
val fields = cl.getDeclaredFields //Line 45, Here, the cl value is the generated class
fields
.filterNot(p => Modifier.isTransient(p.getModifiers) || Modifier.isStatic(p.getModifiers))
.tapEach(_.setAccessible(true))
.toList ++ listAllSerialFields(cl.getSuperclass)
}
Then, further in the method it crashes here :
private static Field[] copyFields(Field[] arg) {
Field[] out = new Field[arg.length]; //arg.length is -500 millions !
ReflectionFactory fact = getReflectionFactory();
for (int i = 0; i < arg.length; i++) {
out[i] = fact.copyField(arg[i]);
}
return out;
}
I suspect that it's the reflection data that causes that because, when i used the debugger to follow the thread execution, the jvm crashed when the debugger saw the referent field of the SoftReference<ReflectionData> Class.reflectionData field. But i repeat, on the client it does not crash, and my debugger can inspect the reflection data successfully.
EDIT -
If i call getDeclaredFields directly once the class get loaded (here for example):
var loader = puppetClass.getClassLoader
if (loader == null)
loader = getClass.getClassLoader //Use the Application's classloader
val classLoader = new GeneratedClassLoader(folderPath, loader, Seq(classOf[LinkitApplication].getClassLoader))
val clazz = Class.forName(wrapperClassName, false, classLoader).asInstanceOf[Class[_ <: PuppetWrapper[AnyRef]]]
println(s"CREATED CLASS ${clazz} IN THREAD = " + Thread.currentThread())
clazz.getDeclaredFields //Invoking a method in order to make the class load its reflectionData (causes fatal error if not made directly)
ClassMappings.putClass(clazz)
clazz
It will never crash or throw me the an exception, however, it still weird that i have to do that because only the server would oftenly crash, and when it crashes, it can does it in the exact same thread that have loaded the class...
This isn't really an answer to your original question, but re-iterates what was said in the comments. What you're doing is "unsafe", and the behavior is undefined. The fact that the workaround is working at all is somewhat incidental and cannot be relied upon. Maybe it works now, but in a future Java version it might fail.
The Unsafe class is going away once safe replacements exist for all of its useful capabilities. This will likely occur after the completion of the Panama project, which provides access to native memory. The VarHandle class is the replacement for direct field access, but it doesn't permit modifying final fields, and it likely never will. Such a backdoor prevents certain optimizations, and new Java features like "records" and "hidden classes" trust that final fields are really final. This behavior might apply to all classes at some point.
There's no planned safe alternative for allocating classes without a constructor, and so that's a problem too. The built-in Java serialization mechanism will have to continue using a backdoor until it's rewritten to use a different technique.
The safe technique is to generate a hidden constructor which performs deserialization and sets the final fields. It might also need dummy parameters to avoid conflicts with any other constructors. The constructor is added with an instrumentation agent which modifies serializable classes as they're loaded.
Ideally the hidden constructor should be private, but then accessing it becomes tricky. The agent should also define (or augment) a static class intializer which looks up the MethodHandle for the hidden constructor and passes it to some serialization framework layer. The private constructor will still be visible by any code which calls getDeclaredConstructors, but that's a relatively minor problem.
As for serializing the fields out in the first place, a VarHandle for each field can be passed along from the static initializer, or a private method is added which does the serialization. I think that the private method approach is better, and it just needs one MethodHandle for accessing it.
Thanks a lot for ones who tried to help me, and thanks to boneil for his answer because he made me become aware that Unsafe wasn't a great solution, but i still have to deal with it. However, i decided to answer because i just made a "discovery" with the Unsafe.allocateInstance method : When Unsafe allocates an instance, every fields of the objects are null but still neverless non detected as null by the JVM. I just had a case where i didn't knew why i got an exception in the init method of one of my allocated instances, i first started to think that it was called twice, but it turned out that i could'nt even debug the method execution with a breakpoint because it was making my JVM crash as soon as the debugger stops in the instance's method. Thus, as i could not use the debugger, i decided to use the plain old nooby printf statement, but still unable to debug as i got a NPE here :
(in java.lang.String.java)
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString(); //obj is null.
}
as you can see, the obj was null as it threw a NPE, but it still has been considerated as a normal instance. Therefore, by seeing this, i decided to use reflection to set the field that caused this issue as null (field.set(instance, null)), and i was able to print the entire object, and i was able to use the debugger ! So this means that it's an Unsafe thing that it does not even set fields' value to null, which is very anoying but, it's ok...
Now i think that my jvm crashed because as the debugger tried to introspect my allocated object, it received an NPE, even if it certainly check if the object was not null, thus, this made an internal error in the debugger, and then the JVM crashed.
EDIT: it seems like it's not just when memory is not initialised by Unsafe, "cursed null" fields looks to appear when Unsafe touches an object (putInt, putObject...). If an Unsafe method is invoked to put something in an object's memory, any null field of the object have a (high) chance to become a weird null field
I am new to using optionals in Java 8. I know the method orElseGet() takes a supplier and orElseThrow() also takes a supplier that throws an exception. orElseThrow() might be a good one to use if I can construct my own exception and do something when that exception is triggered.
My main goal is to use a method that will either get the unwrapped value, or if the optional wraps null, then to actually execute an entirely different function.
Looking for the closest thing to:
class DoInsteadClass {
public void doInstead() {
// Do this instead
}
}
Optional<String> myString = "Hello";
num.orElse(DoInsteadClass::doInstead);
If the only way to do this is orElseThrow(), and as long as I can handle the exception with the 'do this instead' code, that should be fine too. It just makes my codebase larger because I have to create a few different custom utility suppliers for the 2 or 3 cases where some of my optional values would return null.
The problem is, .ifPresent() invokes the specified consumer with the value, otherwise does nothing. I need it to DO SOMETHING if the value is null. My current code utilizes a custom workaround where I first check if the value is null, and if it is, execute a chosen function. Then the next statement is the one that checks for ifPresent(). So this is doing the same thing, I am just looking for a more sugar-coated, one statement version.
In JDK 9 there will be an ifPresentOrElse method that will allow it.
For the time being, however, you may opt for the if statement, with if(num.isPresent()), or write your own reusable ifPresentOrElse method.
I am trying to build a static code analysis, that allows to collect all Strings passed to a function without running the code. I am using Eclipse JDT (3.10.0) to parse the code.
Assumptions/Precodinditions:
Every passed argument can be resolved to a String literal
The value is not saved to any (non-static) fields and then passed on in another call
All callers can be identified and visited by the parser
The MethodInvocation of the examined method is already identified
What i have:
At the moment i am able to identify all MethodInvocations on that particular Method and therefore am able to collect all arguments passed as StringLiterals.
I am able to see all argument Types but, of course, cannot determine the value of parameters, fields, objects etc. as the value binding would only be available at runtime.
The Problem
Under the assumption, that every single passed Argument (no matter the Type) is at some time resolvable to a StringLiteral or a Concatenation of StringLiterals, i should be able to determine the distinct set of all Values which are passed to this method by the program.
Is there a way, to recursively determine the String value of all method calls without following every stacktrace and manually implementing the logic for every occurence?
Imagine the following examples:
public class IAmAnalysed{
public void analysedMethod(String argument){
//do something useful
}
}
//Values in a map
hashMap.put("test", "TestString");
hashMap.put("test2", "TestString2");
for (Map.Entry<String, String> e : hashMap.entrySet()) {
iAmAnalysed.analysedMethod(e.getValue);
}
//util method
public void util(String argument){
iAmAnalysed.analysedMethod(argument + "utilCalled");
}
util("TestString3")
This should give me the following set of values:
TestString
TestString2
TestString3utilCalled
The only appraoch i can think of (using Eclipse JDT) is to add every argument that is no StringLiteral to a working Set and start one more iteration with the ASTParser to determine where the passed value is set, or where it comes from. Then i add this location to the workingSet and iterate once more. This at the end should lead me to all possible arguments.
Unfortunately with this approach i would have to implement logic for every single possible way the value could be passed (imagine all the other possibilities next to the two above)
Examining all possible data flows into a given method is not generally feasible by static analysis. The approach you outline can work for a small group of programs, until you hit, e.g., recursion, at what point the thing will blow up.
Maybe a comprehensive test suite will give better results than static analysis on this one.
We hit an extremely surprising exception today. Inside of a synchronized block, we call wait() and it throws IllegalMonitorStateException. What can cause this?
This is happening in well-tested open source code:
http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/Mux.java?view=markup#l222
We eliminated the obvious causes:
are we synchronized on the right variable? Yes, it's muxLock
is it a mutable variable? No, muxLock is final
are we using any weird "-XX:" JVM flags that might affect monitor behavior? No, but we are launching the JVM embedded inside a C++ app via JNI.
is this a strange JVM? No, it's Sun's 1.6.0_25 win/x64 JRE
is this a known JVM bug? Can't find anything relevant at http://bugs.sun.com/bugdatabase
So, I'm trying to think of more far-fetched explanations.
could an uncaught out-of-memory error cause the monitor state to be screwed up? We're looking at this, but we're seeing no evidence of memory errors yet.
UPDATE: (based on comment)
I've also verified from the stacktrace and breakpoint that the thread is indeed inside the synchronized block when the exception is thrown. It's not the case that some other unrelated code is emitting the exception (unless something is REALLY confusing Eclipse!)
The only suspicious thing I see that you are passing a reference to 'this' to some other object in your constructor. Is it possible (in fact, not unlikely) that, through weird re-ordering of things, if some other thread gets that reference to 'this' and calls the method that uses the muxlock, things can go extremely wrong.
The Java Language Specification is pretty specific about this:
An object is considered to be completely initialized when its constructor finishes. A thread that can only see a reference to an object after that object has been completely initialized is guaranteed to see the correctly initialized values for that object's final fields.
In other words, if another thread gets hold of the 'this' reference before the constructor is finished, the final field 'muxlock' might not be correctly initialized yet. In general, publishing a reference to 'this' before the constructor has finished can be pretty dangerous, especially in threaded situations.
Some potentially useful discussion about such things:
http://madpropellerhead.com/random/20100328-java-final-fields-are-not-as-final-as-you-may-think
For some older, but still useful general discussion of why publishing 'this' in a constructor is a very bad idea in general, see for instance:
http://www.ibm.com/developerworks/java/library/j-jtp0618/index.html
http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/Mux.java?r1=1069292&r2=1135026&diff_format=h
here i can see that timeout was added lately
make sure that startTimeout is > than 0 otherwise you will wait(0) or wait(-n) this probably cause IllegalMonitorStateException
EDIT: Ok above is a disaster But lets try this :
we are in Mux constructor : http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/Mux.java?view=markup
line 176 we create SocketChannelConnectionIO andd pass this after that we break and and different thread takes over .
in constructor of SocketChannelConnectionIO defined here : http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/SocketChannelConnectionIO.java?view=markup
line 112 we register to channel with the new handler().
handler recieaves something on chanel and function let say function handleReadReady is executed we synchronize on muxLock .
now we are still in constructor so object in final is still mutable !!!
let assume it changes , now we have something waiting on different muxLock
One in a million scenario
EDIT
http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/Mux.java?revision=1135026&view=co
Mux(SocketChannel channel,
int role, int initialInboundRation, int maxFragmentSize)
throws IOException
{
this.role = role;
if ((initialInboundRation & ~0x00FFFF00) != 0) {
throw new IllegalArgumentException(
"illegal initial inbound ration: " +
toHexString(initialInboundRation));
}
this.initialInboundRation = initialInboundRation;
this.maxFragmentSize = maxFragmentSize;
//LINE BELOW IS CAUSING PROBLEM it passes this to SocketChannelConnectionIO
this.connectionIO = new SocketChannelConnectionIO(this, channel);
//Lets assume it stops here we are still in constructor
//and we are not in synchronized block
directBuffersUseful = true;
}
now in constructor of SocketChannelConnectionIO
http://svn.apache.org/viewvc/river/jtsk/trunk/src/com/sun/jini/jeri/internal/mux/SocketChannelConnectionIO.java?revision=1069292&view=co
SocketChannelConnectionIO(Mux mux, SocketChannel channel)
throws IOException
{
super(mux);
channel.configureBlocking(false);
this.channel = channel;
//Line below we are registering to the channel with mux that is still mutable
//this is the line that actually is causing the problem move that to
// start() and it should work
key = selectionManager.register(channel, new Handler());
}
move this code to start() should work key = selectionManager.register(channel, new Handler()); (i am assuming start is executet when we want to start prosessing)
/**
* Starts processing connection data.
*/
void start() throws IOException {
key = selectionManager.register(channel, new Handler());
key.renewInterestMask(SelectionKey.OP_READ);
}
But it would be much better not to create SocketChannelConnectionIO in the constructor of mux but maybe somewhere after that the same for second constructor creating StreamConnectionIO with this
The answer is in my opinion that its either a bug, or someone changed the object behind the reference despite its being final. If you can reproduce it, I recommend to set a read/write breakpoint on muxlock field to see if it is touched or not. You could check the identityhashcode of the muxlock in the first line of the synchronized block, and before waits and notifies with appropiate log entries or breakpoints. With reflection you can change final references. Quote from http://download.oracle.com/javase/6/docs/api/java/lang/reflect/Field.html:
"If the underlying field is final, the method throws an IllegalAccessException unless setAccessible(true) has succeeded for this field and this field is non-static. Setting a final field in this way is meaningful only during deserialization or reconstruction of instances of classes with blank final fields, before they are made available for access by other parts of a program. Use in any other context may have unpredictable effects, including cases in which other parts of a program continue to use the original value of this field."
Maybe its a bug in eclispe, and during debugging it somehow changes the field. Is it reproducable outside eclispe as well? Put a printstractrace in catch and see what happens.
Member variables are not as final as one would hope to. You should put the synchronized object into a final local variable, first. This does not explain why the member variable is altered, but if it fixes the problem you at least know that the member variable is really modified.
Is there a way to view stack elements like un-assigned return values or exceptions that not assigned to a local variable? (e.g. throw new ...)
For example, suppose I have code along the lines of:
public String foo(InputStream in) throws IOException {
NastyObj obj = null;
try {
obj = new NastyObj(in);
return (obj.read());
} finally {
if (obj != null) obj.close();
}
}
Is there any way to view the return or exception value without stepping to a higher level frame where it is assigned? This is particularly relevant with exceptions because you often have to step back up through a number of frames to find an actual handler.
I usually use the Eclipse debugging environment, but any answer is appreciated. Also, if this cannot be done, can you explain why? (JVM, JPDA limitation?)
The answer seems to be that both JPDA/JDI and Eclipse are deficient.
I’ve verified the following, but I’m not going to bother posting the code unless someone really needs it.
For the most part, JDI mirrors the structure of the code. Named locals for a given scope can be obtained through the debuggee thread’s current StackFrame. Unscoped locals and method arguments can be obtained through the debuggee thread’s current Method. However, in general where the documentation refers to a LocalVariable, it is a named local.
It IS possible to get the return value of a function, if the function is returning normally and you are using a 1.6 debugging setup (RFE). If the debugger listens for MethodExitEvent, then event.returnValue() gets the value returned after all the method has finished executing. According to the documentation, this event is NOT generated when a method terminates via a thrown exception.
The conclusion is that if you use JDI directly under Java 1.6, you can get the return value of a function before it's assigned to a local, if it returns normally. The Eclipse and Netbeans UIs don't support this. You can't get information about Exceptions that have been thrown but not caught yet.
Couldn't you just catch the IOException (and name it) and then rethrow it? (Don't know Java, but that's what I'd do in C++ and Python.) Of course, this answer is invalid if you can't edit the given code or are inspecting the state right now and need to know what it looks like... But if you can edit the code, that seems like the way to go.