Skip to content

Commit 873db32

Browse files
committed
Make the max send retries configurable
Reverts default to infinite as before
1 parent 17b5551 commit 873db32

2 files changed

Lines changed: 38 additions & 8 deletions

File tree

src/core/com/cosylab/epics/caj/CAJContext.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,12 @@ public class CAJContext extends Context implements CAContext, CAJConstants, Conf
176176
*/
177177
protected int maxArrayBytes = 16384;
178178

179+
/**
180+
* Maximum number of times to retry a partial TCP send before giving up.
181+
* A value of -1 means retry indefinitely (the default).
182+
*/
183+
protected int maxSendRetries = -1;
184+
179185
/**
180186
* Minimum interval in seconds between CA search broadcasts. Default is 0.1 seconds
181187
*/
@@ -465,6 +471,11 @@ protected void loadConfiguration()
465471
catch (Exception ex)
466472
{ logger.log(Level.WARNING, "Cannot parse EPICS_CA_MAX_ARRAY_BYTES='" + tmp + "'", ex); }
467473

474+
tmp = System.getenv("EPICS_CA_MAX_SEND_RETRIES");
475+
if (tmp != null)
476+
try { maxSendRetries = Integer.parseInt(tmp); }
477+
catch (Exception ex)
478+
{ logger.log(Level.WARNING, "Cannot parse EPICS_CA_MAX_SEND_RETRIES='" + tmp + "'", ex); }
468479

469480
tmp = System.getenv("EPICS_CA_MAX_SEARCH_PERIOD");
470481
if (tmp != null)
@@ -485,9 +496,10 @@ protected void loadConfiguration()
485496
repeaterPort = jcaLibrary.getPropertyAsInt(contextClassName + ".repeater_port", repeaterPort);
486497
serverPort = jcaLibrary.getPropertyAsInt(contextClassName + ".server_port", serverPort);
487498
maxArrayBytes = jcaLibrary.getPropertyAsInt(contextClassName + ".max_array_bytes", maxArrayBytes);
499+
maxSendRetries = jcaLibrary.getPropertyAsInt(contextClassName + ".max_send_retries", maxSendRetries);
488500
maxSearchInterval = jcaLibrary.getPropertyAsFloat(contextClassName + ".max_search_interval", maxSearchInterval);
489501
eventDispatcherClassName = jcaLibrary.getProperty(contextClassName + ".event_dispatcher");
490-
502+
491503
// load CAJ specific configuration (overrides default)
492504
addressList = jcaLibrary.getProperty(thisClassName + ".addr_list", addressList);
493505
autoAddressList = jcaLibrary.getPropertyAsBoolean(thisClassName + ".auto_addr_list", autoAddressList);
@@ -500,6 +512,7 @@ protected void loadConfiguration()
500512
repeaterPort = jcaLibrary.getPropertyAsInt(thisClassName + ".repeater_port", repeaterPort);
501513
serverPort = jcaLibrary.getPropertyAsInt(thisClassName + ".server_port", serverPort);
502514
maxArrayBytes = jcaLibrary.getPropertyAsInt(thisClassName + ".max_array_bytes", maxArrayBytes);
515+
maxSendRetries = jcaLibrary.getPropertyAsInt(thisClassName + ".max_send_retries", maxSendRetries);
503516
minSearchInterval = jcaLibrary.getPropertyAsFloat(thisClassName + ".min_search_interval", minSearchInterval);
504517
maxSearchInterval = jcaLibrary.getPropertyAsFloat(thisClassName + ".max_search_interval", maxSearchInterval);
505518
}
@@ -580,6 +593,13 @@ public void configure(Configuration configuration)
580593
maxArrayBytes = configuration.getAttributeAsInteger("max_array_bytes", maxArrayBytes);
581594
}
582595

596+
// max. send retries (-1 = infinite)
597+
try {
598+
maxSendRetries = configuration.getChild("max_send_retries", false).getValueAsInteger();
599+
} catch(Exception ex) {
600+
maxSendRetries = configuration.getAttributeAsInteger("max_send_retries", maxSendRetries);
601+
}
602+
583603
// max. search interval
584604
try {
585605
maxSearchInterval = configuration.getChild("max_search_interval", false).getValueAsFloat();
@@ -1322,6 +1342,7 @@ public void printInfo(PrintStream out) throws IllegalStateException {
13221342
out.println("REPEATER_PORT : " + repeaterPort);
13231343
out.println("SERVER_PORT : " + serverPort);
13241344
out.println("MAX_ARRAY_BYTES : " + maxArrayBytes);
1345+
out.println("MAX_SEND_RETRIES : " + (maxSendRetries < 0 ? "infinite" : maxSendRetries));
13251346
out.println("MIN_SEARCH_INTERVAL : " + minSearchInterval);
13261347
out.println("MAX_SEARCH_INTERVAL : " + maxSearchInterval);
13271348
out.println("EVENT_DISPATCHER: " + eventDispatcher);
@@ -1438,6 +1459,14 @@ public int getMaxArrayBytes() {
14381459
return maxArrayBytes;
14391460
}
14401461

1462+
/**
1463+
* Get maximum number of TCP send retries (-1 means infinite).
1464+
* @return max send retries, or -1 for unlimited.
1465+
*/
1466+
public int getMaxSendRetries() {
1467+
return maxSendRetries;
1468+
}
1469+
14411470
/**
14421471
* Get repeater port.
14431472
* @return repeater port.

src/core/com/cosylab/epics/caj/impl/CATransport.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -628,9 +628,6 @@ protected void enableFlowControl()
628628
}
629629
}
630630

631-
/** Maximum number of times to retry a partial write before giving up. */
632-
private static final int MAX_SEND_RETRIES = 10;
633-
634631
/**
635632
* Send a buffer through the transport.
636633
* NOTE: TCP sent buffer/sending has to be synchronized.
@@ -657,8 +654,9 @@ public void send(ByteBuffer buffer) throws IOException
657654
* <p>Flips the buffer, then loops until all bytes are written. If the
658655
* kernel TCP send buffer is full, waits briefly and retries. Throws
659656
* {@link IOException} (and closes the transport) if the send buffer
660-
* remains full after {@value #MAX_SEND_RETRIES} retries, the channel is
661-
* already closed, or the calling thread is interrupted.
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.
662660
*
663661
* @param buffer fully-written buffer to send (will be flipped)
664662
* @throws IOException on write error, persistent backpressure, or interrupt
@@ -672,6 +670,7 @@ private void noSyncSend(ByteBuffer buffer) throws IOException
672670
context.getLogger().finest("Sending " + buffer.limit() + " bytes to " + socketAddress + ".");
673671

674672
int tries = 0;
673+
final int maxRetries = context.getMaxSendRetries();
675674
while (buffer.hasRemaining())
676675
{
677676
int bytesSent = channel.write(buffer);
@@ -683,11 +682,13 @@ private void noSyncSend(ByteBuffer buffer) throws IOException
683682
if (closed)
684683
throw new IOException("transport closed on the client side");
685684

686-
if (++tries > MAX_SEND_RETRIES)
685+
tries++;
686+
if (maxRetries >= 0 && tries > maxRetries)
687687
throw new IOException("TCP send buffer persistently full, disconnecting " + socketAddress);
688688

689689
context.getLogger().finest("Send buffer full for " + socketAddress
690-
+ ", waiting (attempt " + tries + "/" + MAX_SEND_RETRIES + ")...");
690+
+ ", waiting (attempt " + tries
691+
+ (maxRetries < 0 ? "" : "/" + maxRetries) + ")...");
691692
try {
692693
Thread.sleep(Math.min(15000, 10 + tries * 100));
693694
} catch (InterruptedException e) {

0 commit comments

Comments
 (0)