xref: /aosp_15_r20/external/grpc-grpc-java/binder/src/main/java/io/grpc/binder/internal/PingTracker.java (revision e07d83d3ffcef9ecfc9f7f475418ec639ff0e5fe)
1 /*
2  * Copyright 2020 The gRPC Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package io.grpc.binder.internal;
18 
19 import com.google.common.base.Ticker;
20 import io.grpc.Status;
21 import io.grpc.StatusException;
22 import io.grpc.internal.ClientTransport.PingCallback;
23 import java.util.concurrent.Executor;
24 import javax.annotation.Nullable;
25 import javax.annotation.concurrent.GuardedBy;
26 
27 /**
28  * Tracks an ongoing ping request for a client-side binder transport. We only handle a single active
29  * ping at a time, since that's all gRPC appears to need.
30  */
31 final class PingTracker {
32 
33   interface PingSender {
34     /**
35      * Send a ping to the remote endpoint. We expect a subsequent call to {@link #onPingResponse}
36      * with the same ID (assuming the ping succeeds).
37      */
sendPing(int id)38     void sendPing(int id) throws StatusException;
39   }
40 
41   private final Ticker ticker;
42   private final PingSender pingSender;
43 
44   @GuardedBy("this")
45   @Nullable
46   private Ping pendingPing;
47 
48   @GuardedBy("this")
49   private int nextPingId;
50 
PingTracker(Ticker ticker, PingSender pingSender)51   PingTracker(Ticker ticker, PingSender pingSender) {
52     this.ticker = ticker;
53     this.pingSender = pingSender;
54   }
55 
56   /**
57    * Start a ping.
58    *
59    * <p>See also {@link ClientTransport#ping}.
60    *
61    * @param callback The callback to report the ping result on.
62    * @param executor An executor to call callbacks on.
63    *     <p>Note that only one ping callback will be active at a time.
64    */
startPing(PingCallback callback, Executor executor)65   synchronized void startPing(PingCallback callback, Executor executor) {
66     pendingPing = new Ping(callback, executor, nextPingId++);
67     try {
68       pingSender.sendPing(pendingPing.id);
69     } catch (StatusException se) {
70       pendingPing.fail(se.getStatus());
71       pendingPing = null;
72     }
73   }
74 
75   /** Callback when a ping response with the given ID is received. */
onPingResponse(int id)76   synchronized void onPingResponse(int id) {
77     if (pendingPing != null && pendingPing.id == id) {
78       pendingPing.success();
79       pendingPing = null;
80     }
81   }
82 
83   private final class Ping {
84     private final PingCallback callback;
85     private final Executor executor;
86     private final int id;
87     private final long startTimeNanos;
88 
89     @GuardedBy("this")
90     private boolean done;
91 
Ping(PingCallback callback, Executor executor, int id)92     Ping(PingCallback callback, Executor executor, int id) {
93       this.callback = callback;
94       this.executor = executor;
95       this.id = id;
96       this.startTimeNanos = ticker.read();
97     }
98 
fail(Status status)99     private synchronized void fail(Status status) {
100       if (!done) {
101         done = true;
102         executor.execute(() -> callback.onFailure(status.asException()));
103       }
104     }
105 
success()106     private synchronized void success() {
107       if (!done) {
108         done = true;
109         executor.execute(
110             () -> callback.onSuccess(ticker.read() - startTimeNanos));
111       }
112     }
113   }
114 }
115