2222
2323import java .time .Duration ;
2424import java .util .concurrent .TimeUnit ;
25+ import java .util .concurrent .atomic .AtomicBoolean ;
2526import java .util .concurrent .atomic .AtomicLong ;
27+ import java .util .concurrent .atomic .AtomicReference ;
2628import java .util .concurrent .locks .ReentrantLock ;
2729
2830abstract class MessageManager {
@@ -42,11 +44,10 @@ public enum ManageResult {MESSAGE, STATUS_HANDLED, STATUS_TERMINUS, STATUS_ERROR
4244 protected AtomicLong lastMsgReceivedNanoTime ;
4345
4446 // heartbeat stuff
45- protected boolean hb ;
46- protected long idleHeartbeatSetting ;
47- protected long alarmPeriodSettingNanos ;
48- protected ScheduledTask heartbeatTask ;
49- protected final AtomicLong currentAlarmPeriodNanos ;
47+ protected final AtomicBoolean hb ;
48+ protected final AtomicLong idleHeartbeatSettingMillis ;
49+ protected final AtomicLong alarmPeriodSettingNanos ;
50+ protected final AtomicReference <ScheduledTask > heartbeatTask ;
5051
5152 protected MessageManager (NatsConnection conn , SubscribeOptions so , boolean syncMode ) {
5253 stateChangeLock = new ReentrantLock ();
@@ -57,20 +58,20 @@ protected MessageManager(NatsConnection conn, SubscribeOptions so, boolean syncM
5758 lastStreamSeq = 0 ;
5859 lastConsumerSeq = 0 ;
5960
60- hb = false ;
61- idleHeartbeatSetting = 0 ;
62- alarmPeriodSettingNanos = 0 ;
61+ hb = new AtomicBoolean ( false ) ;
62+ idleHeartbeatSettingMillis = new AtomicLong () ;
63+ alarmPeriodSettingNanos = new AtomicLong () ;
6364 lastMsgReceivedNanoTime = new AtomicLong (NatsSystemClock .nanoTime ());
64- currentAlarmPeriodNanos = new AtomicLong ();
65+ heartbeatTask = new AtomicReference <> ();
6566 }
6667
6768 protected boolean isSyncMode () { return syncMode ; }
6869 protected long getLastStreamSequence () { return lastStreamSeq ; }
6970 protected long getLastConsumerSequence () { return lastConsumerSeq ; }
7071 protected long getLastMsgReceivedNanoTime () { return lastMsgReceivedNanoTime .get (); }
71- protected boolean isHb () { return hb ; }
72- protected long getIdleHeartbeatSetting () { return idleHeartbeatSetting ; }
73- protected long getAlarmPeriodSettingNanos () { return alarmPeriodSettingNanos ; }
72+ protected boolean isHb () { return hb . get () ; }
73+ protected long getIdleHeartbeatSetting () { return idleHeartbeatSettingMillis . get () ; }
74+ protected long getAlarmPeriodSettingNanos () { return alarmPeriodSettingNanos . get () ; }
7475
7576 protected void startup (NatsJetStreamSubscription sub ) {
7677 this .sub = sub ;
@@ -108,21 +109,22 @@ protected void handleHeartbeatError() {
108109 protected void configureIdleHeartbeat (Duration configIdleHeartbeat , long configMessageAlarmTime ) {
109110 stateChangeLock .lock ();
110111 try {
111- idleHeartbeatSetting = configIdleHeartbeat == null ? 0 : configIdleHeartbeat .toMillis ();
112- if (idleHeartbeatSetting <= 0 ) {
113- alarmPeriodSettingNanos = 0 ;
114- hb = false ;
112+ long idleSettingMillis = configIdleHeartbeat == null ? 0 : configIdleHeartbeat .toMillis ();
113+ idleHeartbeatSettingMillis .set (idleSettingMillis );
114+ if (idleSettingMillis <= 0 ) {
115+ alarmPeriodSettingNanos .set (0 );
116+ hb .set (false );
115117 }
116118 else {
117- long alarmPeriodSetting ;
118- if (configMessageAlarmTime < idleHeartbeatSetting ) {
119- alarmPeriodSetting = idleHeartbeatSetting * THRESHOLD ;
119+ long alarmPeriodSettingMillis ;
120+ if (configMessageAlarmTime < idleSettingMillis ) {
121+ alarmPeriodSettingMillis = idleSettingMillis * THRESHOLD ;
120122 }
121123 else {
122- alarmPeriodSetting = configMessageAlarmTime ;
124+ alarmPeriodSettingMillis = configMessageAlarmTime ;
123125 }
124- alarmPeriodSettingNanos = alarmPeriodSetting * NatsConstants .NANOS_PER_MILLI ;
125- hb = true ;
126+ alarmPeriodSettingNanos . set ( alarmPeriodSettingMillis * NatsConstants .NANOS_PER_MILLI ) ;
127+ hb . set ( true ) ;
126128 }
127129 }
128130 finally {
@@ -137,26 +139,20 @@ protected void updateLastMessageReceived() {
137139 protected void initOrResetHeartbeatTimer () {
138140 stateChangeLock .lock ();
139141 try {
140- if (heartbeatTask != null ) {
141- // Same settings, just reuse the existing timer
142- if (currentAlarmPeriodNanos .get () == alarmPeriodSettingNanos ) {
143- updateLastMessageReceived ();
144- return ;
145- }
146-
147- // Replace timer since settings have changed
148- shutdownHeartbeatTimer ();
142+ ScheduledTask hbTask = heartbeatTask .get ();
143+ if (hbTask != null ) {
144+ hbTask .shutdown ();
149145 }
150146
151147 // replacement or new comes here
152- this .currentAlarmPeriodNanos .set (alarmPeriodSettingNanos );
153- heartbeatTask = new ScheduledTask (conn .getScheduledExecutor (), alarmPeriodSettingNanos , TimeUnit .NANOSECONDS ,
148+ heartbeatTask .set (new ScheduledTask (conn .getScheduledExecutor (), alarmPeriodSettingNanos .get (), TimeUnit .NANOSECONDS ,
154149 () -> {
155150 long sinceLast = NatsSystemClock .nanoTime () - lastMsgReceivedNanoTime .get ();
156- if (sinceLast > currentAlarmPeriodNanos .get ()) {
151+ if (sinceLast > alarmPeriodSettingNanos .get ()) {
152+ shutdownHeartbeatTimer (); // a new one will get started when needed.
157153 handleHeartbeatError ();
158154 }
159- });
155+ })) ;
160156 updateLastMessageReceived ();
161157 }
162158 finally {
@@ -167,11 +163,11 @@ protected void initOrResetHeartbeatTimer() {
167163 protected void shutdownHeartbeatTimer () {
168164 stateChangeLock .lock ();
169165 try {
170- if (heartbeatTask != null ) {
171- heartbeatTask .shutdown ();
172- heartbeatTask = null ;
166+ ScheduledTask hbTask = heartbeatTask .get ();
167+ if (hbTask != null ) {
168+ hbTask .shutdown ();
169+ heartbeatTask .set (null );
173170 }
174- currentAlarmPeriodNanos .set (0 );
175171 }
176172 finally {
177173 stateChangeLock .unlock ();
0 commit comments