2727import java .util .concurrent .TimeoutException ;
2828import java .util .concurrent .atomic .AtomicBoolean ;
2929import java .util .concurrent .atomic .AtomicReference ;
30+ import java .util .concurrent .locks .Lock ;
31+ import java .util .concurrent .locks .ReentrantLock ;
3032import java .util .function .Supplier ;
3133import javax .annotation .Nonnull ;
3234import javax .annotation .Nullable ;
@@ -43,7 +45,8 @@ public final class HttpRequestService implements RequestService {
4345 private final AtomicBoolean isRunning = new AtomicBoolean (false );
4446 private final AtomicBoolean hasStopped = new AtomicBoolean (false );
4547 private final AtomicReference <PeriodicDelay > currentDelay ;
46- private final AtomicReference <ScheduledFuture <?>> currentTask = new AtomicReference <>();
48+ private final AtomicReference <ScheduledFuture <?>> scheduledTask = new AtomicReference <>();
49+ private final Lock sendLock = new ReentrantLock ();
4750 private final RetryAfterParser retryAfterParser ;
4851 @ Nullable private Callback callback ;
4952 @ Nullable private Supplier <Request > requestSupplier ;
@@ -100,26 +103,12 @@ public void start(Callback callback, Supplier<Request> requestSupplier) {
100103 if (isRunning .compareAndSet (false , true )) {
101104 this .callback = callback ;
102105 this .requestSupplier = requestSupplier ;
103- currentTask .set (
104- executorService .schedule (
105- this ::periodicSend , getNextDelay ().toNanos (), TimeUnit .NANOSECONDS ));
106+ scheduleNextExecution ();
106107 } else {
107108 throw new IllegalStateException ("HttpRequestService is already running" );
108109 }
109110 }
110111
111- private void periodicSend () {
112- doSendRequest ();
113- // schedule the next execution
114- currentTask .set (
115- executorService .schedule (
116- this ::periodicSend , getNextDelay ().toNanos (), TimeUnit .NANOSECONDS ));
117- }
118-
119- private void sendOnce () {
120- executorService .execute (this ::doSendRequest );
121- }
122-
123112 private Duration getNextDelay () {
124113 return Objects .requireNonNull (currentDelay .get ()).getNextDelay ();
125114 }
@@ -138,7 +127,43 @@ public void sendRequest() {
138127 throw new IllegalStateException ("HttpRequestService is not running" );
139128 }
140129
141- sendOnce ();
130+ executorService .execute (this ::requestOnDemand );
131+ }
132+
133+ private void requestOnDemand () {
134+ if (sendLock .tryLock ()) {
135+ try {
136+ ScheduledFuture <?> scheduledFuture = scheduledTask .get ();
137+ if (scheduledFuture != null ) {
138+ // Cancel future task
139+ scheduledFuture .cancel (false );
140+ }
141+ sendAndScheduleNext ();
142+ } finally {
143+ sendLock .unlock ();
144+ }
145+ }
146+ }
147+
148+ private void periodicRequest () {
149+ if (sendLock .tryLock ()) {
150+ try {
151+ sendAndScheduleNext ();
152+ } finally {
153+ sendLock .unlock ();
154+ }
155+ }
156+ }
157+
158+ private void sendAndScheduleNext () {
159+ doSendRequest ();
160+ scheduleNextExecution ();
161+ }
162+
163+ private void scheduleNextExecution () {
164+ scheduledTask .set (
165+ executorService .schedule (
166+ this ::periodicRequest , getNextDelay ().toNanos (), TimeUnit .NANOSECONDS ));
142167 }
143168
144169 private void doSendRequest () {
@@ -150,7 +175,7 @@ private void doSendRequest() {
150175 try (HttpSender .Response response = future .get (30 , TimeUnit .SECONDS )) {
151176 getCallback ().onConnectionSuccess ();
152177 if (isSuccessful (response )) {
153- handleSuccessResponse (
178+ handleHttpSuccess (
154179 Response .create (ServerToAgent .ADAPTER .decode (response .bodyInputStream ())));
155180 } else {
156181 handleHttpError (response );
@@ -187,7 +212,7 @@ private static boolean isSuccessful(HttpSender.Response response) {
187212 return response .statusCode () >= 200 && response .statusCode () < 300 ;
188213 }
189214
190- private void handleSuccessResponse (Response response ) {
215+ private void handleHttpSuccess (Response response ) {
191216 useRegularDelay ();
192217 ServerToAgent serverToAgent = response .getServerToAgent ();
193218
@@ -211,28 +236,19 @@ private void handleErrorResponse(ServerErrorResponse errorResponse) {
211236
212237 private void useRegularDelay () {
213238 if (currentDelay .compareAndSet (periodicRetryDelay , periodicRequestDelay )) {
214- cancelCurrentTask ();
215239 periodicRequestDelay .reset ();
216240 }
217241 }
218242
219243 private void useRetryDelay (@ Nullable Duration retryAfter ) {
220244 if (currentDelay .compareAndSet (periodicRequestDelay , periodicRetryDelay )) {
221- cancelCurrentTask ();
222245 periodicRetryDelay .reset ();
223246 if (retryAfter != null && periodicRetryDelay instanceof AcceptsDelaySuggestion ) {
224247 ((AcceptsDelaySuggestion ) periodicRetryDelay ).suggestDelay (retryAfter );
225248 }
226249 }
227250 }
228251
229- private void cancelCurrentTask () {
230- ScheduledFuture <?> future = currentTask .get ();
231- if (future != null ) {
232- future .cancel (false );
233- }
234- }
235-
236252 private Callback getCallback () {
237253 return Objects .requireNonNull (callback );
238254 }
0 commit comments