@@ -37,6 +37,7 @@ class NatsConnectionWriter implements Runnable {
3737
3838 private final NatsConnection connection ;
3939
40+ private final ReentrantLock writerLock ;
4041 private Future <Boolean > stopped ;
4142 private Future <DataPort > dataPortFuture ;
4243 private DataPort dataPort ;
@@ -50,10 +51,10 @@ class NatsConnectionWriter implements Runnable {
5051 private final MessageQueue outgoing ;
5152 private final MessageQueue reconnectOutgoing ;
5253 private final long reconnectBufferSize ;
53- private final AtomicBoolean flushBuffer ;
5454
5555 NatsConnectionWriter (NatsConnection connection , NatsConnectionWriter sourceWriter ) {
5656 this .connection = connection ;
57+ writerLock = new ReentrantLock ();
5758
5859 this .running = new AtomicBoolean (false );
5960 this .reconnectMode = new AtomicBoolean (sourceWriter != null );
@@ -76,8 +77,6 @@ class NatsConnectionWriter implements Runnable {
7677 reconnectOutgoing = new MessageQueue (true , options .getRequestCleanupInterval (),
7778 sourceWriter == null ? null : sourceWriter .reconnectOutgoing );
7879 reconnectBufferSize = options .getReconnectBufferSize ();
79-
80- flushBuffer = new AtomicBoolean (false );
8180 }
8281
8382 // Should only be called if the current thread has exited.
@@ -123,86 +122,88 @@ boolean isRunning() {
123122 }
124123
125124 void sendMessageBatch (NatsMessage msg , DataPort dataPort , StatisticsCollector stats ) throws IOException {
126- int sendPosition = 0 ;
127- int sbl = sendBufferLength .get ();
128-
129- while (msg != null ) {
130- long size = msg .getSizeInBytes ();
131-
132- if (sendPosition + size > sbl ) {
133- if (sendPosition > 0 ) {
134- dataPort .write (sendBuffer , sendPosition );
135- connection .getNatsStatistics ().registerWrite (sendPosition );
136- sendPosition = 0 ;
137- }
138- if (size > sbl ) { // have to resize b/c can't fit 1 message
139- sbl = bufferAllocSize ((int ) size , BUFFER_BLOCK_SIZE );
140- sendBufferLength .set (sbl );
141- sendBuffer = new byte [sbl ];
125+ writerLock .lock ();
126+ try {
127+ int sendPosition = 0 ;
128+ int sbl = sendBufferLength .get ();
129+
130+ while (msg != null ) {
131+ long size = msg .getSizeInBytes ();
132+
133+ if (sendPosition + size > sbl ) {
134+ if (sendPosition > 0 ) {
135+ dataPort .write (sendBuffer , sendPosition );
136+ connection .getNatsStatistics ().registerWrite (sendPosition );
137+ sendPosition = 0 ;
138+ }
139+ if (size > sbl ) { // have to resize b/c can't fit 1 message
140+ sbl = bufferAllocSize ((int ) size , BUFFER_BLOCK_SIZE );
141+ sendBufferLength .set (sbl );
142+ sendBuffer = new byte [sbl ];
143+ }
142144 }
143- }
144145
145- ByteArrayBuilder bab = msg .getProtocolBab ();
146- int babLen = bab .length ();
147- System .arraycopy (bab .internalArray (), 0 , sendBuffer , sendPosition , babLen );
148- sendPosition += babLen ;
146+ ByteArrayBuilder bab = msg .getProtocolBab ();
147+ int babLen = bab .length ();
148+ System .arraycopy (bab .internalArray (), 0 , sendBuffer , sendPosition , babLen );
149+ sendPosition += babLen ;
150+
151+ sendBuffer [sendPosition ++] = CR ;
152+ sendBuffer [sendPosition ++] = LF ;
149153
150- sendBuffer [ sendPosition ++] = CR ;
151- sendBuffer [ sendPosition ++] = LF ;
154+ if (! msg . isProtocol ()) {
155+ sendPosition += msg . copyNotEmptyHeaders ( sendPosition , sendBuffer ) ;
152156
153- if (!msg .isProtocol ()) {
154- sendPosition += msg .copyNotEmptyHeaders (sendPosition , sendBuffer );
157+ byte [] bytes = msg .getData (); // guaranteed to not be null
158+ if (bytes .length > 0 ) {
159+ System .arraycopy (bytes , 0 , sendBuffer , sendPosition , bytes .length );
160+ sendPosition += bytes .length ;
161+ }
155162
156- byte [] bytes = msg .getData (); // guaranteed to not be null
157- if (bytes .length > 0 ) {
158- System .arraycopy (bytes , 0 , sendBuffer , sendPosition , bytes .length );
159- sendPosition += bytes .length ;
163+ sendBuffer [sendPosition ++] = CR ;
164+ sendBuffer [sendPosition ++] = LF ;
160165 }
161166
162- sendBuffer [sendPosition ++] = CR ;
163- sendBuffer [sendPosition ++] = LF ;
164- }
167+ stats .incrementOutMsgs ();
168+ stats .incrementOutBytes (size );
165169
166- stats .incrementOutMsgs ();
167- stats .incrementOutBytes (size );
170+ if (msg .flushImmediatelyAfterPublish ) {
171+ dataPort .flush ();
172+ }
173+ msg = msg .next ;
174+ }
168175
169- msg = msg .next ;
176+ // no need to write if there are no bytes
177+ if (sendPosition > 0 ) {
178+ dataPort .write (sendBuffer , sendPosition );
179+ connection .getNatsStatistics ().registerWrite (sendPosition );
180+ }
170181 }
171-
172- // no need to write if there are no bytes
173- if (sendPosition > 0 ) {
174- dataPort .write (sendBuffer , sendPosition );
182+ finally {
183+ writerLock .unlock ();
175184 }
176-
177- connection .getNatsStatistics ().registerWrite (sendPosition );
178185 }
179186
180187 @ Override
181188 public void run () {
182- Duration waitForMessage = Duration .ofMinutes (2 ); // This can be long since no one is sending
183- Duration reconnectWait = Duration .ofMillis (1 ); // This should be short, since we are trying to get the reconnect through
189+ Duration outgoingTimeout = Duration .ofMinutes (2 ); // This can be long since no one is sending
190+ Duration reconnectTimeout = Duration .ofMillis (1 ); // This should be short, since we are trying to get the reconnect through
184191
185192 try {
186193 dataPort = this .dataPortFuture .get (); // Will wait for the future to complete
187194 StatisticsCollector stats = this .connection .getNatsStatistics ();
188- int maxAccumulate = Options .MAX_MESSAGES_IN_NETWORK_BUFFER ;
189195
190196 while (this .running .get ()) {
191- NatsMessage msg = null ;
192-
197+ NatsMessage msg ;
193198 if (this .reconnectMode .get ()) {
194- msg = this .reconnectOutgoing .accumulate (sendBufferLength .get (), maxAccumulate , reconnectWait );
195- } else {
196- msg = this .outgoing .accumulate (sendBufferLength .get (), maxAccumulate , waitForMessage );
199+ msg = this .reconnectOutgoing .accumulate (sendBufferLength .get (), Options .MAX_MESSAGES_IN_NETWORK_BUFFER , reconnectTimeout );
200+ }
201+ else {
202+ msg = this .outgoing .accumulate (sendBufferLength .get (), Options .MAX_MESSAGES_IN_NETWORK_BUFFER , outgoingTimeout );
197203 }
198-
199204 if (msg != null ) {
200205 sendMessageBatch (msg , dataPort , stats );
201206 }
202-
203- if (flushBuffer .getAndSet (false )) {
204- dataPort .flush ();
205- }
206207 }
207208 } catch (IOException | BufferOverflowException io ) {
208209 // if already not running, an IOE is not unreasonable in a transition state
@@ -241,8 +242,18 @@ void queueInternalMessage(NatsMessage msg) {
241242 }
242243
243244 void flushBuffer () {
244- if (running .get ()) {
245- flushBuffer .set (true );
245+ // Since there is no connection level locking, we rely on synchronization
246+ // of the APIs here.
247+ writerLock .lock ();
248+ try {
249+ if (this .running .get ()) {
250+ dataPort .flush ();
251+ }
252+ } catch (Exception e ) {
253+ // NOOP;
254+ }
255+ finally {
256+ writerLock .unlock ();
246257 }
247258 }
248259}
0 commit comments