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

grpc / grpc-java / #19314

26 Jun 2024 09:30PM CUT coverage: 88.23%. Remained the same
#19314

push

github

web-flow
Revert "Start 1.63.3 development cycle (#11318)" (#11333)

This reverts commit 7ece6555b.

31206 of 35369 relevant lines covered (88.23%)

0.88 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 io.grpc.internal.ClientTransport.PingCallback;
21
import java.util.LinkedHashMap;
22
import java.util.Map;
23
import java.util.concurrent.Executor;
24
import java.util.concurrent.TimeUnit;
25
import java.util.logging.Level;
26
import java.util.logging.Logger;
27
import javax.annotation.concurrent.GuardedBy;
28

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

199
  }
200

201
  /**
202
   * Returns a runnable that, when run, invokes the given callback, providing the given cause of
203
   * failure.
204
   */
205
  private static Runnable asRunnable(final ClientTransport.PingCallback callback,
206
                                     final Throwable failureCause) {
207
    return new Runnable() {
1✔
208
      @Override
209
      public void run() {
210
        callback.onFailure(failureCause);
1✔
211
      }
1✔
212
    };
213
  }
214
}
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