private void sendPendingHaves() {
    if (destroyed) {

      return;
    }

    try {
      pending_haves_mon.enter();

      int num_haves = pending_haves.size();

      if (num_haves == 0) {

        return;
      }

      // single have -> use BT

      if (num_haves == 1 || az_have_version < BTMessageFactory.MESSAGE_VERSION_SUPPORTS_PADDING) {

        for (int i = 0; i < num_haves; i++) {

          Integer piece_num = (Integer) pending_haves.get(i);

          outgoing_message_q.addMessage(new BTHave(piece_num.intValue(), bt_have_version), true);
        }
      } else {

        int[] piece_numbers = new int[num_haves];

        for (int i = 0; i < num_haves; i++) {

          piece_numbers[i] = ((Integer) pending_haves.get(i)).intValue();
        }

        outgoing_message_q.addMessage(new AZHave(piece_numbers, az_have_version), true);
      }

      outgoing_message_q.doListenerNotifications();

      pending_haves.clear();

    } finally {

      pending_haves_mon.exit();
    }
  }
  public ByteBuffer destroy() {
    if (destroyed) {
      Debug.out("Trying to redestroy message decoder, stack trace follows: " + this);
      Debug.outStackTrace();
    }

    is_paused = true;
    destroyed = true;

    // there's a concurrency issue with the decoder whereby it can be destroyed while will being
    // messed with. Don't
    // have the energy to look into it properly atm so just try to ensure that it doesn't bork too
    // badly (parg: 29/04/2012)
    // only occasional but does have potential to generate direct buffer mem leak ;(

    int lbuff_read = 0;
    int pbuff_read = 0;
    length_buffer.limit(SS, 4);

    DirectByteBuffer plb = payload_buffer;

    if (reading_length_mode) {
      lbuff_read = length_buffer.position(SS);
    } else { // reading payload
      length_buffer.position(SS, 4);
      lbuff_read = 4;
      pbuff_read = plb == null ? 0 : plb.position(SS);
    }

    ByteBuffer unused = ByteBuffer.allocate(lbuff_read + pbuff_read); // TODO convert to direct?

    length_buffer.flip(SS);
    unused.put(length_buffer.getBuffer(SS));

    try {
      if (plb != null) {
        plb.flip(SS);
        unused.put(
            plb.getBuffer(
                SS)); // Got a buffer overflow exception here in the past - related to PEX?
      }
    } catch (RuntimeException e) {
      Debug.out("hit known threading issue");
    }

    unused.flip();

    length_buffer.returnToPool();

    if (plb != null) {
      plb.returnToPool();
      payload_buffer = null;
    }

    try {
      for (int i = 0; i < messages_last_read.size(); i++) {
        Message msg = (Message) messages_last_read.get(i);
        msg.destroy();
      }
    } catch (RuntimeException e) {
      // happens if messages modified by alt thread...
      Debug.out("hit known threading issue");
    }
    messages_last_read.clear();

    return unused;
  }