Deserializing fibers that used `stderr` results in `java.lang.ClassNotFoundException: org.gradle.util.LinePerThreadBufferingOutputStream$1` when running under Gradle
Gradle seems to replace System.out.stderr with LinePerThreadBufferingOutputStream which inserts itself in a threadlocal that triggers the issue upon deserialization.
Workaround f.e. here.
Reproduce f.e. by inserting a System.err printout before this line and then gradle :quasar-core:jdk7Test.
There does not seem to be any easy workaround for this, actually.
-
By the time you notice that this object is in the process, it already registered itself as a thread local and can't be removed as that ThreadLocal is private (at least, not without reflection). Even if you reset stderr, it's too late.
-
You can't do Class.forName() on the child class that actually tries to get serialised, for some reason. Or at least I couldn't make it work yet. So you can't register a null serialiser for it either.
FWIW serialisation of ThreadLocals has only ever caused me pain so far. Frankly I'd rather it's not done by default.
And it doesn't insert itself into a threadlocal, the issue is that it subclasses ThreadLocal with an inner class that then points back to itself. PAIN!
Actually you can't even easily reflect your way out of this, because apparently Gradle runs your tests inside a class loader so you can't actually obtain the Class representing the errant subclass of ThreadLocal, so you can't register a custom serialiser for it.
This really is a hole.
@pron Any ideas here? This is one reason I wanted to disable TLS serialisation. It's kind of crazy how complicated working around this issue is.
@mikehearn I'm not sure I understand your case. In my (simple) case I switch from Gradle's re-assigned stderr / stdout to the OS' channels before any fiber getting serialized accesses the former. Isn't that feasible in your case?
@mikehearn I'm experimenting with new TL serialization. Can you give it a try 0.7.0/0.7.5-SNAPSHOT?
@mikehearn I realized that even that change is too simple to help. You may be right that the best approach is to give up on serializing TLs, but I'd like to think about it some more. In particular, we should at least consider trying to take into account the actual TL subclass rather than the boolean choice between ThreadLocal and InheritableThreadLocal we have now. It might prove to be futile, but it might be worth a shot.
OK, that sounds reasonable, thanks.
@circlespainter Unfortunately that doesn't fix it, because by the time my code gets control, the bad ThreadLocal has already been created and inserted itself into the global TLS list. No way to remove it then.
@pron thanks for the new code. Brute force but works (I just hit this again, this time with a sun.misc.Cleaner generated by the NIO FS code).
Could you maybe upload a 0.9.5-SNAPSHOT to Maven Central? That'd help, thanks.
Done.
As I said, there's much more that can (and will) be done here re handling of TL subclasses (we can use reflection to try and instantiate the proper subclasses) before we decide it's time to give up :)
Thanks!
For some things like file handles, sockets, threads, java internal classes etc, it doesn't make sense to serialise them anyway ... does Kryo know about these cases itself or will it need some sort of blacklist?
Serialization should simply fail for those, and the TL's value would be set to null. Once we support TL subclasses, we'll be able to support non-null defaults, too.