Repository: netty/netty
Issue: LocalAddress's can collide in a multi-client setup
Labels: defect

Using Netty 4.0.7, I'm randomly running into this exception when connecting many clients to the server over a LocalChannel:

```
io.netty.channel.ChannelException: address already in use by: [id: 0x65219647, local:E:65219647 => local:stardog]
    at io.netty.channel.local.LocalChannelRegistry.register(LocalChannelRegistry.java:46)
    at io.netty.channel.local.LocalChannel.doBind(LocalChannel.java:177)
    at io.netty.channel.local.LocalChannel$LocalUnsafe.connect(LocalChannel.java:338)
    at io.netty.channel.DefaultChannelPipeline$HeadHandler.connect(DefaultChannelPipeline.java:1008)
    at io.netty.channel.DefaultChannelHandlerContext.invokeConnect(DefaultChannelHandlerContext.java:491)
    at io.netty.channel.DefaultChannelHandlerContext.connect(DefaultChannelHandlerContext.java:476)
    at io.netty.channel.DefaultChannelHandlerContext.connect(DefaultChannelHandlerContext.java:461)
    at io.netty.channel.DefaultChannelPipeline.connect(DefaultChannelPipeline.java:847)
    at io.netty.channel.AbstractChannel.connect(AbstractChannel.java:198)
    at io.netty.bootstrap.Bootstrap$2.run(Bootstrap.java:165)
    at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:354)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:366)
    at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:101)
```

It looks like the ID of the LocalAddress is created from the hashCode of the LocalChannel its connected to.  It's entirely possible that two LocalChannels which are not == to get the same hashCode, which means their LocalAddresses will collide and you'll get the aforementioned stacktrace.  This greatly hampers a multi-client use case w/ LocalChannel/LocalAddress. 


Issue comments:
adding `buf.append(Long.toHexString(ThreadLocalRandom.current().nextLong() & 0xFFFFFFFFL | 0x100000000L));` in the LocalAddress constructor makes the collision less likely, although I don't think it removes it altogether.  Perhaps using something like `java.util.UUID` would be more appropriate, ala `buf.append(UUID.randomUUID().toString().replace('-', ':'));`

[This](https://gist.github.com/mhgrove/6295318) is a modified version of LocalEcho which creates and runs clients their own thread.  On my machine, this reproduces the name collision every single time.  If I apply either of the modifications to `LocalAddress` then it does not seem to occur.

It is interesting that this issue is always reproducible for you.  I can't reproduce it at all.  Looking from the source code of ThreadLocalRandom and Random, I think it is pretty difficult for two different channels to get the same hashCode() because each ThreadLocalRandom is supposed to have different seeds.  What JDK do you use?

Yeah, I also found it interesting that the only explanation seemed to be that two different Randoms produced the same nextLong, which I assume would really only happen if they had the same seed, and that also seemed unlikely.

In any case, I'm on OSX 10.8.3 w/ Java 1.6.0_45-b06-451-11M4406

Java 6 does a sloppy job in generating seeds, so I overrode it to make `ThreadLocalRandom` work better.  Please feel free to reopen if you find the fix does not work.


Pull request review comments:

Patch that resolved the issue:
diff --git a/transport/src/main/java/io/netty/channel/local/LocalAddress.java b/transport/src/main/java/io/netty/channel/local/LocalAddress.java
index 6098cca470e..511f012b65c 100644
--- a/transport/src/main/java/io/netty/channel/local/LocalAddress.java
+++ b/transport/src/main/java/io/netty/channel/local/LocalAddress.java
@@ -18,6 +18,7 @@
 import io.netty.channel.Channel;
 
 import java.net.SocketAddress;
+import java.util.UUID;
 
 /**
  * An endpoint in the local transport.  Each endpoint is identified by a unique
@@ -40,7 +41,7 @@ public final class LocalAddress extends SocketAddress implements Comparable<Loca
     LocalAddress(Channel channel) {
         StringBuilder buf = new StringBuilder(16);
         buf.append("local:E");
-        buf.append(Long.toHexString(channel.hashCode() & 0xFFFFFFFFL | 0x100000000L));
+        buf.append(UUID.randomUUID().toString().replace('-', ':'));
         buf.setCharAt(7, ':');
         id = buf.substring(6);
         strVal = buf.toString();


Test patch: