@@ -630,96 +630,76 @@ protected void enableFlowControl()
630630
631631 /**
632632 * Send a buffer through the transport.
633- * NOTE: TCP sent buffer/sending has to be synchronized.
633+ * NOTE: TCP sent buffer/sending has to be synchronized.
634634 * @param buffer buffer to be sent
635- * @throws IOException
635+ * @throws IOException
636636 */
637- public void send (ByteBuffer buffer , boolean asyncCloseOnError ) throws IOException
637+ public void send (ByteBuffer buffer ) throws IOException
638638 {
639639 sendLock .lock ();
640640 try
641641 {
642- noSyncSend (buffer , asyncCloseOnError );
642+ noSyncSend (buffer );
643643 }
644- finally
644+ finally
645645 {
646646 sendLock .unlock ();
647647 }
648648 }
649649
650650 /**
651- * Send a buffer through the transport.
652- * NOTE: TCP sent buffer/sending has to be synchronized.
653- * @param buffer buffer to be sent
654- * @throws IOException
651+ * Send a buffer through the transport without acquiring the send lock
652+ * (caller is responsible for holding it).
653+ *
654+ * <p>Flips the buffer, then loops until all bytes are written. If the
655+ * kernel TCP send buffer is full, waits briefly and retries. Throws
656+ * {@link IOException} (and closes the transport) if the send buffer
657+ * remains full after the configured max send retries (see
658+ * {@link CAJContext#getMaxSendRetries()}; -1 means retry indefinitely),
659+ * the channel is already closed, or the calling thread is interrupted.
660+ *
661+ * @param buffer fully-written buffer to send (will be flipped)
662+ * @throws IOException on write error, persistent backpressure, or interrupt
655663 */
656- // TODO optimize !!!
657- private void noSyncSend (ByteBuffer buffer , boolean asyncCloseOnError ) throws IOException
664+ private void noSyncSend (ByteBuffer buffer ) throws IOException
658665 {
659666 try
660667 {
661- // prepare buffer
662668 buffer .flip ();
663669
664- final int SEND_BUFFER_LIMIT = 16000 ;
665- int bufferLimit = buffer .limit ();
670+ context .getLogger ().finest ("Sending " + buffer .limit () + " bytes to " + socketAddress + "." );
666671
667- // TODO remove?!
668- context .getLogger ().finest ("Sending " + bufferLimit + " bytes to " + socketAddress + "." );
669-
670- // limit sending large buffers, split the into parts
671- int parts = (buffer .limit ()-1 ) / SEND_BUFFER_LIMIT + 1 ;
672- for (int part = 1 ; part <= parts ; part ++)
672+ int tries = 0 ;
673+ final int maxRetries = context .getMaxSendRetries ();
674+ while (buffer .hasRemaining ())
673675 {
674- if (parts > 1 )
675- {
676- buffer .limit (Math .min (part * SEND_BUFFER_LIMIT , bufferLimit ));
677- context .getLogger ().finest ("[Parted] Sending (part " + part + "/" + parts + ") " + (buffer .limit ()-buffer .position ()) + " bytes to " + socketAddress + "." );
678- }
679-
680- final int TRIES = 10 ;
681- for (int tries = 0 ; /* tries <= TRIES */ ; tries ++)
676+ int bytesSent = channel .write (buffer );
677+ if (bytesSent < 0 )
678+ throw new IOException ("channel closed" );
679+
680+ if (buffer .hasRemaining ())
682681 {
683-
684- // send
685- int bytesSent = channel .write (buffer );
686- if (bytesSent < 0 )
687- throw new IOException ("bytesSent < 0" );
688-
689- // bytesSend == buffer.position(), so there is no need for flip()
690- if (buffer .position () != buffer .limit ())
691- {
692- if (closed )
693- throw new IOException ("transport closed on the client side" );
694-
695- if (tries >= TRIES )
696- {
697- context .getLogger ().warning ("Failed to send message to " + socketAddress + " - buffer full, will retry." );
698-
699- //if (tries >= 2*TRIES)
700- // throw new IOException("TCP send buffer persistently full, disconnecting!");
701-
702- }
703-
704- // flush & wait for a while...
705- context .getLogger ().finest ("Send buffer full for " + socketAddress + ", waiting..." );
706- channel .socket ().getOutputStream ().flush ();
707- try {
708- Thread .sleep (Math .min (15000 ,10 +tries *100 ));
709- } catch (InterruptedException e ) {
710- // noop
711- }
712- continue ;
682+ if (closed )
683+ throw new IOException ("transport closed on the client side" );
684+
685+ tries ++;
686+ if (maxRetries >= 0 && tries > maxRetries )
687+ throw new IOException ("TCP send buffer persistently full, disconnecting " + socketAddress );
688+
689+ context .getLogger ().finest ("Send buffer full for " + socketAddress
690+ + ", waiting (attempt " + tries
691+ + (maxRetries < 0 ? "" : "/" + maxRetries ) + ")..." );
692+ try {
693+ Thread .sleep (Math .min (15000 , 10 + tries * 100 ));
694+ } catch (InterruptedException e ) {
695+ Thread .currentThread ().interrupt ();
696+ throw new IOException ("interrupted while sending to " + socketAddress );
713697 }
714- else
715- break ;
716698 }
717-
718699 }
719700 }
720- catch (IOException ioex )
701+ catch (IOException ioex )
721702 {
722- // close connection
723703 close (true );
724704 throw ioex ;
725705 }
@@ -832,7 +812,7 @@ public boolean flushInternal()
832812 }
833813
834814 try {
835- send (buf , false );
815+ send (buf );
836816 }
837817 finally {
838818 // return back to the cache
@@ -883,7 +863,7 @@ public void submit(Request requestMessage) throws IOException {
883863 {
884864 try
885865 {
886- noSyncSend (message , true );
866+ noSyncSend (message );
887867 return ;
888868 }
889869 finally
0 commit comments