@@ -190,13 +190,13 @@ public abstract class BrowserPage implements Serializable {
190190
191191 private static final int INITIAL_WS_DEFAULT_HEARTBEAT_INTERVAL = 25_000 ;
192192
193- private static final int INITIAL_WS_DEFAULT_HEARTBEAT_TIMEOUT = INITIAL_WS_DEFAULT_HEARTBEAT_INTERVAL ;
193+ private static final int INITIAL_WS_DEFAULT_HEARTBEAT_TIMEOUT = 10_000 ;
194194
195195 private static volatile int wsDefaultHeartbeatInterval = INITIAL_WS_DEFAULT_HEARTBEAT_INTERVAL ;
196196
197197 private static volatile int wsDefaultReconnectInterval = 2_000 ;
198198
199- private static volatile int wsDefaultHeartbeatTimeout = 10_000 ;
199+ private static volatile int wsDefaultHeartbeatTimeout = INITIAL_WS_DEFAULT_HEARTBEAT_TIMEOUT ;
200200
201201 private final LongAdder pushQueueSize = new LongAdder ();
202202
@@ -247,6 +247,8 @@ public abstract class BrowserPage implements Serializable {
247247
248248 private final AtomicInteger clientSidePayloadIdGenerator = new AtomicInteger ();
249249
250+ private final AtomicBoolean hasHitBufferOutOfMemory = new AtomicBoolean ();
251+
250252 private static final long DEFAULT_IO_BUFFER_TIMEOUT = TimeUnit .MILLISECONDS
251253 .toNanos (INITIAL_WS_DEFAULT_HEARTBEAT_INTERVAL );
252254
@@ -606,9 +608,9 @@ public final WebSocketPushListener getWsListener() {
606608 return wsListenerHolder != null ? wsListenerHolder .webSocketPushListener : null ;
607609 }
608610
609- final void push (final NameValue ... nameValues ) {
611+ final boolean push (final NameValue ... nameValues ) {
610612 final ByteBuffer payload = buildPayload (WffBinaryMessageUtil .VERSION_1 .getWffBinaryMessageBytes (nameValues ));
611- push (new ClientTasksWrapper (payload ));
613+ return push (new ClientTasksWrapper (payload ));
612614 }
613615
614616 /**
@@ -631,8 +633,10 @@ final ClientTasksWrapper pushAndGetWrapper(final Queue<Collection<NameValue>> mu
631633 }
632634
633635 final ClientTasksWrapper clientTasks = new ClientTasksWrapper (tasks );
634- push (clientTasks );
635- return clientTasks ;
636+ if (push (clientTasks )) {
637+ return clientTasks ;
638+ }
639+ return null ;
636640 }
637641
638642 private void pushLockless (final ClientTasksWrapper clientTasks ) {
@@ -652,23 +656,40 @@ private void pushLockless(final ClientTasksWrapper clientTasks) {
652656 }
653657 }
654658
655- private void push (final ClientTasksWrapper clientTasks ) {
659+ private boolean push (final ClientTasksWrapper clientTasks ) {
656660 if (outputBufferLimitLock != null ) {
657661 final WebSocketPushListenerHolder wsListenerCurrent = wsListener ;
658662 if (wsListenerCurrent == null || (!wsListenerCurrent .serverSideActionPerformed .get ()
659663 && !wsListenerCurrent .clientSideJSExecuted .get ())) {
660664 try {
665+ // if wsListenerCurrent is null there is no advantage of waiting
666+ final boolean acquired = isQuickTryAcquireApplicableWithNoBOMCheck (wsListenerCurrent )
667+ ? outputBufferLimitLock .tryAcquire (clientTasks .getCurrentSize ())
668+ : outputBufferLimitLock .tryAcquire (clientTasks .getCurrentSize (),
669+ settings .outputBufferTimeout , TimeUnit .NANOSECONDS );
661670 // onPayloadLoss check should be second
662- if (outputBufferLimitLock .tryAcquire (clientTasks .getCurrentSize (), settings .outputBufferTimeout ,
663- TimeUnit .NANOSECONDS ) || onPayloadLoss == null ) {
671+ if (acquired || onPayloadLoss == null ) {
664672 pushLockless (clientTasks );
673+ return true ;
665674 } else {
666- if (LOGGER .isLoggable (Level .SEVERE )) {
667- LOGGER .severe (
668- """
669- Buffer timeout reached while preparing server event for client so further changes will not be pushed to client.
670- Increase Settings.outputBufferLimit or Settings.outputBufferTimeout to solve this issue.
671- NB: Settings.outputBufferTimeout should be <= maxIdleTimeout by BrowserPageContent.enableAutoClean method.""" );
675+ if (LOGGER .isLoggable (Level .SEVERE ) || LOGGER .isLoggable (Level .FINEST )) {
676+ if (wsListenerCurrent != null ) {
677+ if (LOGGER .isLoggable (Level .SEVERE )) {
678+ LOGGER .severe (
679+ """
680+ Buffer timeout reached while preparing server event for client so further changes will not be pushed to client.
681+ Increase Settings.outputBufferLimit or Settings.outputBufferTimeout to solve this issue.
682+ NB: Settings.outputBufferTimeout should be <= maxIdleTimeout by BrowserPageContent.enableAutoClean method.""" );
683+ }
684+ } else {
685+ if (LOGGER .isLoggable (Level .FINEST )) {
686+ LOGGER .finest (
687+ """
688+ Buffer timeout reached while preparing server event for client so further changes will not be pushed to client and there is no active websocket connection available at the moment.
689+ Increase Settings.outputBufferLimit or Settings.outputBufferTimeout to solve this issue.
690+ NB: Settings.outputBufferTimeout should be <= maxIdleTimeout by BrowserPageContent.enableAutoClean method.""" );
691+ }
692+ }
672693 }
673694 if (wsListenerCurrent != null && !wsListenerCurrent .clientSideJSExecuted .get ()
674695 && onPayloadLoss .javaScript != null && !onPayloadLoss .javaScript .isBlank ()) {
@@ -691,6 +712,7 @@ private void push(final ClientTasksWrapper clientTasks) {
691712 wffBMBytesQueue .offerFirst (new ClientTasksWrapper (clientAction ));
692713 }
693714 wsListenerCurrent .clientSideJSExecuted .set (true );
715+ hasHitBufferOutOfMemory .set (true );
694716 }
695717
696718 }
@@ -702,7 +724,9 @@ private void push(final ClientTasksWrapper clientTasks) {
702724 }
703725 } else {
704726 pushLockless (clientTasks );
727+ return true ;
705728 }
729+ return false ;
706730 }
707731
708732 private void pushWffBMBytesQueue () {
@@ -783,7 +807,7 @@ private void pushWffBMBytesQueue() {
783807
784808 if (pushWffBMBytesQueueLock .hasQueuedThreads ()) {
785809 final Thread waitingThread = waitingThreadRef .get ();
786- if (waitingThread != null && ! waitingThread . equals ( taskThread )
810+ if (waitingThread != null && waitingThread != taskThread
787811 && waitingThread .getPriority () >= taskThread .getPriority ()) {
788812 break ;
789813 }
@@ -911,22 +935,34 @@ final void webSocketMessagedWithoutLosslessCheck(final byte[] message) {
911935
912936 if (inputBufferLimitLock != null ) {
913937 try {
938+ final WebSocketPushListenerHolder wsListenerCurrent = wsListener ;
939+ final boolean acquired = isQuickTryAcquireApplicable (wsListenerCurrent )
940+ ? inputBufferLimitLock .tryAcquire (message .length )
941+ : inputBufferLimitLock .tryAcquire (message .length , settings .inputBufferTimeout ,
942+ TimeUnit .NANOSECONDS );
914943 // onPayloadLoss check should be second
915- if (inputBufferLimitLock .tryAcquire (message .length , settings .inputBufferTimeout , TimeUnit .NANOSECONDS )
916- || onPayloadLoss == null ) {
944+ if (acquired || onPayloadLoss == null ) {
917945 taskFromClientQ .offer (message );
918946 } else {
919- if (LOGGER .isLoggable (Level .SEVERE )) {
920- LOGGER .severe (
921- """
922- Buffer timeout reached while processing event from client so further client events will not be received at server side.
923- Increase Settings.inputBufferLimit or Settings.inputBufferTimeout to solve this issue.
924- NB: Settings.inputBufferTimeout should be <= maxIdleTimeout by BrowserPageContent.enableAutoClean method.""" );
947+ if (LOGGER .isLoggable (Level .SEVERE ) || LOGGER .isLoggable (Level .FINEST )) {
948+ final String msg = """
949+ Buffer timeout reached while processing event from client so further client events will not be received at server side.
950+ Increase Settings.inputBufferLimit or Settings.inputBufferTimeout to solve this issue.
951+ NB: Settings.inputBufferTimeout should be <= maxIdleTimeout by BrowserPageContent.enableAutoClean method.""" ;
952+ if (wsListenerCurrent != null ) {
953+ if (LOGGER .isLoggable (Level .SEVERE )) {
954+ LOGGER .severe (msg );
955+ }
956+ } else {
957+ if (LOGGER .isLoggable (Level .FINEST )) {
958+ LOGGER .finest (msg );
959+ }
960+ }
925961 }
926- final WebSocketPushListenerHolder wsListenerCurrent = wsListener ;
927962 if (wsListenerCurrent != null && onPayloadLoss .serverSideAction != null ) {
928963 onPayloadLoss .serverSideAction .perform ();
929964 wsListenerCurrent .serverSideActionPerformed .set (true );
965+ hasHitBufferOutOfMemory .set (true );
930966 }
931967 }
932968 } catch (final InterruptedException e ) {
@@ -968,6 +1004,7 @@ final boolean checkLosslessCommunication(final byte[] message) {
9681004 }
9691005 onPayloadLoss .serverSideAction .perform ();
9701006 wsListenerCurrent .serverSideActionPerformed .set (true );
1007+ hasHitBufferOutOfMemory .set (true );
9711008 return false ;
9721009 }
9731010 }
@@ -3481,4 +3518,23 @@ private int getClientSidePayloadId() {
34813518 return id ;
34823519 }
34833520
3521+ private boolean isQuickTryAcquireApplicable (final WebSocketPushListenerHolder wsListenerCurrent ) {
3522+ return wsListenerCurrent == null || hasHitBufferOutOfMemory .get () || (wsHeartbeatInterval > 0
3523+ && wsHeartbeatTimeout > 0
3524+ && (System .currentTimeMillis () - lastClientAccessedTime ) > (wsHeartbeatInterval + wsHeartbeatTimeout ));
3525+ }
3526+
3527+ private boolean isQuickTryAcquireApplicableWithNoBOMCheck (final WebSocketPushListenerHolder wsListenerCurrent ) {
3528+ return wsListenerCurrent == null || (wsHeartbeatInterval > 0 && wsHeartbeatTimeout > 0
3529+ && (System .currentTimeMillis () - lastClientAccessedTime ) > (wsHeartbeatInterval + wsHeartbeatTimeout ));
3530+ }
3531+
3532+ /**
3533+ * @return true if the browser page has hit out of memory.
3534+ * @since 12.0.10
3535+ */
3536+ public final boolean hasHitBufferOutOfMemory () {
3537+ return hasHitBufferOutOfMemory .get ();
3538+ }
3539+
34843540}
0 commit comments