protected Datagram doSendSyncDatagram(
      RegistrationReference registrationReference, Datagram datagram, long timeout)
      throws InterruptedException, IOException, TimeoutException {

    SendSyncDatagramCompletionHandler sendSyncDatagramCompletionHandler =
        new SendSyncDatagramCompletionHandler();

    datagram.completionHandler = sendSyncDatagramCompletionHandler;

    datagram.completionTypes = REPLIED_ENUM_SET;
    datagram.timeout = timeout;

    if (datagram.getSequenceId() == 0) {
      datagram.setSequenceId(generateSequenceId());
    }

    addResponseWaitingDatagram(datagram);

    doSendDatagram(registrationReference, datagram);

    return sendSyncDatagramCompletionHandler.waitResult(timeout);
  }
  @Override
  public <A> void sendDatagram(
      RegistrationReference registrationReference,
      Datagram datagram,
      A attachment,
      EnumSet<CompletionType> completionTypes,
      CompletionHandler<A> completionHandler,
      long timeout,
      TimeUnit timeUnit) {

    if (registrationReference == null) {
      throw new NullPointerException("Registration reference is null");
    }

    if (!registrationReference.isValid()) {
      throw new IllegalArgumentException("Registration reference is invalid");
    }

    if (datagram == null) {
      throw new NullPointerException("Datagram is null");
    }

    if (completionTypes == null) {
      throw new NullPointerException("Completion type set is null");
    }

    if (completionTypes.isEmpty()) {
      throw new IllegalArgumentException("Completion type set is empty");
    }

    if (completionHandler == null) {
      throw new NullPointerException("Complete handler is null");
    }

    if (timeUnit == null) {
      throw new NullPointerException("Time unit is null");
    }

    if (timeout <= 0) {
      timeout = defaultTimeout;
    } else {
      timeout = timeUnit.toMillis(timeout);
    }

    ensureOpen();

    datagram.attachment = attachment;
    datagram.completionHandler = (CompletionHandler<Object>) completionHandler;
    datagram.completionTypes = completionTypes;
    datagram.timeout = timeout;

    datagram.setAckRequest(completionTypes.contains(CompletionType.DELIVERED));

    if (datagram.getSequenceId() == 0) {
      datagram.setSequenceId(generateSequenceId());
    }

    if (completionTypes.contains(CompletionType.DELIVERED)
        || completionTypes.contains(CompletionType.REPLIED)) {

      addResponseWaitingDatagram(datagram);
    }

    doSendDatagram(registrationReference, datagram);
  }