/** Block datagrams from given source if a memory to receive all datagrams. */
  void block(MembershipKeyImpl key, InetAddress source) throws IOException {
    assert key.channel() == this;
    assert key.sourceAddress() == null;

    synchronized (stateLock) {
      if (!key.isValid()) throw new IllegalStateException("key is no longer valid");
      if (source.isAnyLocalAddress())
        throw new IllegalArgumentException("Source address is a wildcard address");
      if (source.isMulticastAddress())
        throw new IllegalArgumentException("Source address is multicast address");
      if (source.getClass() != key.group().getClass())
        throw new IllegalArgumentException("Source address is different type to group");

      int n;
      if (key instanceof MembershipKeyImpl.Type6) {
        MembershipKeyImpl.Type6 key6 = (MembershipKeyImpl.Type6) key;
        n = Net.block6(fd, key6.groupAddress(), key6.index(), Net.inet6AsByteArray(source));
      } else {
        MembershipKeyImpl.Type4 key4 = (MembershipKeyImpl.Type4) key;
        n = Net.block4(fd, key4.groupAddress(), key4.interfaceAddress(), Net.inet4AsInt(source));
      }
      if (n == IOStatus.UNAVAILABLE) {
        // ancient kernel
        throw new UnsupportedOperationException();
      }
    }
  }
  /** Unblock given source. */
  void unblock(MembershipKeyImpl key, InetAddress source) {
    assert key.channel() == this;
    assert key.sourceAddress() == null;

    synchronized (stateLock) {
      if (!key.isValid()) throw new IllegalStateException("key is no longer valid");

      try {
        if (key instanceof MembershipKeyImpl.Type6) {
          MembershipKeyImpl.Type6 key6 = (MembershipKeyImpl.Type6) key;
          Net.unblock6(fd, key6.groupAddress(), key6.index(), Net.inet6AsByteArray(source));
        } else {
          MembershipKeyImpl.Type4 key4 = (MembershipKeyImpl.Type4) key;
          Net.unblock4(fd, key4.groupAddress(), key4.interfaceAddress(), Net.inet4AsInt(source));
        }
      } catch (IOException ioe) {
        // should not happen
        throw new AssertionError(ioe);
      }
    }
  }
  // package-private
  void drop(MembershipKeyImpl key) {
    assert key.channel() == this;

    synchronized (stateLock) {
      if (!key.isValid()) return;

      try {
        if (key instanceof MembershipKeyImpl.Type6) {
          MembershipKeyImpl.Type6 key6 = (MembershipKeyImpl.Type6) key;
          Net.drop6(fd, key6.groupAddress(), key6.index(), key6.source());
        } else {
          MembershipKeyImpl.Type4 key4 = (MembershipKeyImpl.Type4) key;
          Net.drop4(fd, key4.groupAddress(), key4.interfaceAddress(), key4.source());
        }
      } catch (IOException ioe) {
        // should not happen
        throw new AssertionError(ioe);
      }

      key.invalidate();
      registry.remove(key);
    }
  }