/** * Remove the channel from the channel map and free the number for re-use. This method must be * safe to call multiple times on the same channel. If it is not then things go badly wrong. */ public void releaseChannelNumber(ChannelN channel) { // Warning, here be dragons. Not great big ones, but little baby ones // which will nibble on your toes and occasionally trip you up when // you least expect it. (Pixies? HP2) // Basically, there's a race that can end us up here. It almost never // happens, but it's easier to repair it when it does than prevent it // from happening in the first place. // If we end up doing a Channel.close in one thread and a Channel.open // with the same channel number in another, the two can overlap in such // a way as to cause disconnectChannel on the old channel to try to // remove the new one. Ideally we would fix this race at the source, // but it's much easier to just catch it here. synchronized (this.monitor) { int channelNumber = channel.getChannelNumber(); ChannelN existing = _channelMap.remove(channelNumber); // Nothing to do here. Move along. if (existing == null) return; // Oops, we've gone and stomped on someone else's channel. Put it // back and pretend we didn't touch it. else if (existing != channel) { _channelMap.put(channelNumber, existing); return; } channelNumberAllocator.free(channelNumber); } }
public ChannelN createChannel(AMQConnection connection, int channelNumber) throws IOException { ChannelN ch; synchronized (this.monitor) { if (channelNumberAllocator.reserve(channelNumber)) { ch = addNewChannel(connection, channelNumber); } else { return null; } } ch.open(); // now that it's been safely added return ch; }