quasar icon indicating copy to clipboard operation
quasar copied to clipboard

Deserializing fibers that used `stderr` results in `java.lang.ClassNotFoundException: org.gradle.util.LinePerThreadBufferingOutputStream$1` when running under Gradle

Open circlespainter opened this issue 9 years ago • 12 comments

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.

circlespainter avatar Jan 12 '16 09:01 circlespainter

There does not seem to be any easy workaround for this, actually.

  1. 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.

  2. 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.

mikehearn avatar Feb 10 '16 15:02 mikehearn

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!

mikehearn avatar Feb 10 '16 15:02 mikehearn

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.

mikehearn avatar Feb 10 '16 16:02 mikehearn

@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 avatar Feb 22 '16 22:02 mikehearn

@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?

circlespainter avatar Feb 23 '16 08:02 circlespainter

@mikehearn I'm experimenting with new TL serialization. Can you give it a try 0.7.0/0.7.5-SNAPSHOT?

pron avatar Feb 23 '16 18:02 pron

@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.

pron avatar Feb 23 '16 19:02 pron

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.

mikehearn avatar Feb 24 '16 14:02 mikehearn

@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.

mikehearn avatar Mar 02 '16 13:03 mikehearn

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 :)

pron avatar Mar 04 '16 08:03 pron

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?

mikehearn avatar Mar 04 '16 10:03 mikehearn

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.

pron avatar Mar 04 '16 11:03 pron