New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ZlibEncoder fails when driven by multiple threads #546
Comments
Vaguely related discussion in #545 - proposed a similar fix, waiting for someone with godlike Netty knowledge to respond :) |
@jaens I also suggested this on #545 but just for the record. So this would be the idea: diff --git a/src/main/java/org/jboss/netty/handler/codec/oneone/OneToOneEncoder.java b/src/main/java/org/jboss/netty/handler/codec/oneone/OneToOneEncoder.java
index 29c9d38..9713b4b 100644
--- a/src/main/java/org/jboss/netty/handler/codec/oneone/OneToOneEncoder.java
+++ b/src/main/java/org/jboss/netty/handler/codec/oneone/OneToOneEncoder.java
@@ -50,17 +50,30 @@ public abstract class OneToOneEncoder implements ChannelDownstreamHandler {
}
public void handleDownstream(
- ChannelHandlerContext ctx, ChannelEvent evt) throws Exception {
+ final ChannelHandlerContext ctx, final ChannelEvent evt) throws Exception {
if (!(evt instanceof MessageEvent)) {
ctx.sendDownstream(evt);
return;
}
- MessageEvent e = (MessageEvent) evt;
- Object originalMessage = e.getMessage();
+ final MessageEvent e = (MessageEvent) evt;
+ ctx.getPipeline().execute(new Runnable() {
+
+ public void run() {
+ try {
+ encodeAndWrite(ctx, e);
+ } catch (Throwable cause) {
+ fireExceptionCaughtLater(e.getChannel(), cause);
+ }
+ }
+ });
+ }
+
+ private void encodeAndWrite(final ChannelHandlerContext ctx, final MessageEvent e) throws Exception {
+ final Object originalMessage = e.getMessage();
Object encodedMessage = encode(ctx, e.getChannel(), originalMessage);
if (originalMessage == encodedMessage) {
- ctx.sendDownstream(evt);
+ ctx.sendDownstream(e);
} else if (encodedMessage != null) {
write(ctx, e.getFuture(), encodedMessage, e.getRemoteAddress());
} |
@jaens if we really want to use synchronized then we should synchronize on the Channel and not one this as a OneToOneEncoder may be shared by different Channels. |
Something to note is that not all encoders require strict encode and write ordering. If the encoder and corresponding decoder relies on stream state, like ZIP, then we need to enforce strict encode and write ordering. Some encoders do not rely on stream state, but instead are message based and thus do not require strict encode and write ordering. Perhaps creating a new OneToOneStrictEncoder which enforces the encode / write order should be implemented. The default OneToOneEncoder would remain as is. The ZipEncoder would extend OneToOneStrictEncoder. Rather than enforcing the order of encode and write using synchronization, utilising the pipeline I/O thread to perform the encode and write is a good solution. |
Good point... Let me take care of this later. Sent from my iPhone. Excuse any typos.... Am 28.08.2012 um 11:35 schrieb Ravi Cheema notifications@github.com:
|
@normanmaurer I did not originally open this issue, but anyway, my two cents... :) Executing the rest of the downstream pipeline (which can contain arbitrary handlers) in the I/O worker thread is a pretty hefty change in semantics, with its own set of issues:
|
@jaens and I think you brought about some very good concerns. Thanks for that! So let us just use synchronized(channel) then... |
…ps to ensure strict ordering. This should be used if that is needed like in the case of ZIP. See ##546
@jaens just pushed the change.. let me know what you think about it.. |
@normanmaurer looks clear & should work I guess The SPDY codec fix pull req (which works for us in production) is pretty much the same, except it only synchronizes when necessary (since not all frame types actually access the shared compressor) While there are probably use cases for doing something more complicated, at least for re-usable components inside Netty itself I guess it's best to do "non-surprising" things ie. consistent with how Netty behaves in other places :) |
Yeah I will fix the SPDY stuff later .. One fix after the other ;) Sent from my iPhone. Excuse any typos.... Am 28.08.2012 um 13:54 schrieb Jaen notifications@github.com:
|
The ZlibEncoder is a downstream handler and extends OneToOneEncoder which in turn implements ChannelDownstreamHandler. All Netty downstream handlers can be driven by multiple threads.
In OneToOneEncoder.handleDownstream() (called from ZlibEncoder.handleDownstream()),
The OneToOneEncoder doesn't provide any protection between the encode(...) and subsequent channel write(...). This is fine for stateless encoding, but is a problem when using stateful encoders like Zip.
After encode(...) is called to compress a message, it is possible for another thread to also call encode(...) with another message, and the subsequent channel write()'s to get out of order. This causes errors when decoding the stream of bytes in the upstream handler in ZlibDecoder, since the stream is now corrupted.
The fix is to ensure the encode() and the subsequent channel write() is strictly performed in order, even if driven by multiple threads. This can be done by synchronizing the encode() and write() to ensure no other thread can jump in and interfere with the order of what is written to the channel. This functionality is essential for ZlibEncoder to work correctly, since the ZlibDecoder expects to be fed the same data as was encoded.
I wrote an alternative ZlibEncoder (below) works correctly in a multi-threaded environment. It ensures no thread can jump in between the encode() and the subsequent channel write()
public class MyZlibEncoder extends org.jboss.netty.handler.codec.compression.ZlibEncoder {
public MyZlibEncoder(ZlibWrapper wrapper) {
super(wrapper, 6);
}
}
The text was updated successfully, but these errors were encountered: