• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

grpc / grpc-java / #19970

29 Aug 2025 09:01PM UTC coverage: 88.532% (-0.06%) from 88.588%
#19970

push

github

web-flow
Upgrade guava version to 33.4.8 (#12219)

Guava seems to call a deprecated sun.misc.Unsafe::objectFieldOffset
method which might be removed in a future JDK release.
There are still a few things to do for -android versions, which are
tracked in https://github.com/google/guava/issues/7742,
see details at google/guava#7811

Fixes #12215

34678 of 39170 relevant lines covered (88.53%)

0.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.02
/../core/src/main/java/io/grpc/internal/Http2Ping.java
1
/*
2
 * Copyright 2015 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.internal;
18

19
import com.google.common.base.Stopwatch;
20
import com.google.errorprone.annotations.concurrent.GuardedBy;
21
import io.grpc.Status;
22
import io.grpc.internal.ClientTransport.PingCallback;
23
import java.util.LinkedHashMap;
24
import java.util.Map;
25
import java.util.concurrent.Executor;
26
import java.util.concurrent.TimeUnit;
27
import java.util.logging.Level;
28
import java.util.logging.Logger;
29

30
/**
31
 * Represents an outstanding PING operation on an HTTP/2 channel. This can be used by HTTP/2-based
32
 * transports to implement {@link ClientTransport#ping}.
33
 *
34
 * <p>A typical transport need only support one outstanding ping at a time. So, if a ping is
35
 * requested while an operation is already in progress, the given callback is notified when the
36
 * existing operation completes.
37
 */
38
public class Http2Ping {
39
  private static final Logger log = Logger.getLogger(Http2Ping.class.getName());
1✔
40

41
  /**
42
   * The PING frame includes 8 octets of payload data, e.g. 64 bits.
43
   */
44
  private final long data;
45

46
  /**
47
   * Used to measure elapsed time.
48
   */
49
  private final Stopwatch stopwatch;
50

51
  /**
52
   * The registered callbacks and the executor used to invoke them.
53
   */
54
  @GuardedBy("this") private Map<PingCallback, Executor> callbacks
1✔
55
      = new LinkedHashMap<>();
56

57
  /**
58
   * False until the operation completes, either successfully (other side sent acknowledgement) or
59
   * unsuccessfully.
60
   */
61
  @GuardedBy("this") private boolean completed;
62

63
  /**
64
   * If non-null, indicates the ping failed.
65
   */
66
  @GuardedBy("this") private Status failureCause;
67

68
  /**
69
   * The round-trip time for the ping, in nanoseconds. This value is only meaningful when
70
   * {@link #completed} is true and {@link #failureCause} is null.
71
   */
72
  @GuardedBy("this") private long roundTripTimeNanos;
73

74
  /**
75
   * Creates a new ping operation. The caller is responsible for sending a ping on an HTTP/2 channel
76
   * using the given payload. The caller is also responsible for starting the stopwatch when the
77
   * PING frame is sent.
78
   *
79
   * @param data the ping payload
80
   * @param stopwatch a stopwatch for measuring round-trip time
81
   */
82
  public Http2Ping(long data, Stopwatch stopwatch) {
1✔
83
    this.data = data;
1✔
84
    this.stopwatch = stopwatch;
1✔
85
  }
1✔
86

87
  /**
88
   * Registers a callback that is invoked when the ping operation completes. If this ping operation
89
   * is already completed, the callback is invoked immediately.
90
   *
91
   * @param callback the callback to invoke
92
   * @param executor the executor to use
93
   */
94
  public void addCallback(final ClientTransport.PingCallback callback, Executor executor) {
95
    Runnable runnable;
96
    synchronized (this) {
1✔
97
      if (!completed) {
1✔
98
        callbacks.put(callback, executor);
1✔
99
        return;
1✔
100
      }
101
      // otherwise, invoke callback immediately (but not while holding lock)
102
      runnable = this.failureCause != null ? asRunnable(callback, failureCause)
×
103
                                           : asRunnable(callback, roundTripTimeNanos);
×
104
    }
×
105
    doExecute(executor, runnable);
×
106
  }
×
107

108
  /**
109
   * Returns the expected ping payload for this outstanding operation.
110
   *
111
   * @return the expected payload for this outstanding ping
112
   */
113
  public long payload() {
114
    return data;
1✔
115
  }
116

117
  /**
118
   * Completes this operation successfully. The stopwatch given during construction is used to
119
   * measure the elapsed time. Registered callbacks are invoked and provided the measured elapsed
120
   * time.
121
   *
122
   * @return true if the operation is marked as complete; false if it was already complete
123
   */
124
  public boolean complete() {
125
    Map<ClientTransport.PingCallback, Executor> callbacks;
126
    long roundTripTimeNanos;
127
    synchronized (this) {
1✔
128
      if (completed) {
1✔
129
        return false;
×
130
      }
131
      completed = true;
1✔
132
      roundTripTimeNanos = this.roundTripTimeNanos = stopwatch.elapsed(TimeUnit.NANOSECONDS);
1✔
133
      callbacks = this.callbacks;
1✔
134
      this.callbacks = null;
1✔
135
    }
1✔
136
    for (Map.Entry<ClientTransport.PingCallback, Executor> entry : callbacks.entrySet()) {
1✔
137
      doExecute(entry.getValue(), asRunnable(entry.getKey(), roundTripTimeNanos));
1✔
138
    }
1✔
139
    return true;
1✔
140
  }
141

142
  /**
143
   * Completes this operation exceptionally. Registered callbacks are invoked and provided the
144
   * given throwable as the cause of failure.
145
   *
146
   * @param failureCause the cause of failure
147
   */
148
  public void failed(Status failureCause) {
149
    Map<ClientTransport.PingCallback, Executor> callbacks;
150
    synchronized (this) {
1✔
151
      if (completed) {
1✔
152
        return;
×
153
      }
154
      completed = true;
1✔
155
      this.failureCause = failureCause;
1✔
156
      callbacks = this.callbacks;
1✔
157
      this.callbacks = null;
1✔
158
    }
1✔
159
    for (Map.Entry<ClientTransport.PingCallback, Executor> entry : callbacks.entrySet()) {
1✔
160
      notifyFailed(entry.getKey(), entry.getValue(), failureCause);
1✔
161
    }
1✔
162
  }
1✔
163

164
  /**
165
   * Notifies the given callback that the ping operation failed.
166
   *
167
   * @param callback the callback
168
   * @param executor the executor used to invoke the callback
169
   * @param cause the cause of failure
170
   */
171
  public static void notifyFailed(PingCallback callback, Executor executor, Status cause) {
172
    doExecute(executor, asRunnable(callback, cause));
1✔
173
  }
1✔
174

175
  /**
176
   * Executes the given runnable. This prevents exceptions from propagating so that an exception
177
   * thrown by one callback won't prevent subsequent callbacks from being executed.
178
   */
179
  private static void doExecute(Executor executor, Runnable runnable) {
180
    try {
181
      executor.execute(runnable);
1✔
182
    } catch (Throwable th) {
×
183
      log.log(Level.SEVERE, "Failed to execute PingCallback", th);
×
184
    }
1✔
185
  }
1✔
186

187
  /**
188
   * Returns a runnable that, when run, invokes the given callback, providing the given round-trip
189
   * duration.
190
   */
191
  private static Runnable asRunnable(final ClientTransport.PingCallback callback,
192
                                     final long roundTripTimeNanos) {
193
    return new Runnable() {
1✔
194
      @Override
195
      public void run() {
196
        callback.onSuccess(roundTripTimeNanos);
1✔
197
      }
1✔
198
    };
199

200
  }
201

202
  /**
203
   * Returns a runnable that, when run, invokes the given callback, providing the given cause of
204
   * failure.
205
   */
206
  private static Runnable asRunnable(final ClientTransport.PingCallback callback,
207
                                     final Status failureCause) {
208
    return new Runnable() {
1✔
209
      @Override
210
      public void run() {
211
        callback.onFailure(failureCause);
1✔
212
      }
1✔
213
    };
214
  }
215
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc