Coverage Report

Created: 2026-07-30 06:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/grpc-swift/Sources/GRPC/ClientCalls/LazyEventLoopPromise.swift
Line
Count
Source
1
/*
2
 * Copyright 2020, gRPC Authors All rights reserved.
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
import NIOConcurrencyHelpers
17
import NIOCore
18
19
extension EventLoop {
20
0
  internal func makeLazyPromise<Value>(of: Value.Type = Value.self) -> LazyEventLoopPromise<Value> {
21
0
    return LazyEventLoopPromise(on: self)
22
0
  }
23
}
24
25
/// A `LazyEventLoopPromise` is similar to an `EventLoopPromise` except that the underlying
26
/// `EventLoopPromise` promise is only created if it is required. That is, when the future result
27
/// has been requested and the promise has not yet been completed.
28
///
29
/// Note that all methods **must** be called from its `eventLoop`.
30
internal struct LazyEventLoopPromise<Value> {
31
  private enum State {
32
    // No future has been requested, no result has been delivered.
33
    case idle
34
35
    // No future has been requested, but this result have been delivered.
36
    case resolvedResult(Result<Value, Error>)
37
38
    // A future has been request; the promise may or may not contain a result.
39
    case unresolvedPromise(EventLoopPromise<Value>)
40
41
    // A future was requested, it's also been resolved.
42
    case resolvedFuture(EventLoopFuture<Value>)
43
  }
44
45
  private var state: State
46
  private let eventLoop: EventLoop
47
48
0
  fileprivate init(on eventLoop: EventLoop) {
49
0
    self.state = .idle
50
0
    self.eventLoop = eventLoop
51
0
  }
52
53
  /// Get the future result of this promise.
54
0
  internal mutating func getFutureResult() -> EventLoopFuture<Value> {
55
0
    self.eventLoop.preconditionInEventLoop()
56
0
57
0
    switch self.state {
58
0
    case .idle:
59
0
      let promise = self.eventLoop.makePromise(of: Value.self)
60
0
      self.state = .unresolvedPromise(promise)
61
0
      return promise.futureResult
62
0
63
0
    case let .resolvedResult(result):
64
0
      let future: EventLoopFuture<Value>
65
0
      switch result {
66
0
      case let .success(value):
67
0
        future = self.eventLoop.makeSucceededFuture(value)
68
0
      case let .failure(error):
69
0
        future = self.eventLoop.makeFailedFuture(error)
70
0
      }
71
0
      self.state = .resolvedFuture(future)
72
0
      return future
73
0
74
0
    case let .unresolvedPromise(promise):
75
0
      return promise.futureResult
76
0
77
0
    case let .resolvedFuture(future):
78
0
      return future
79
0
    }
80
0
  }
81
82
  /// Succeed the promise with the given value.
83
0
  internal mutating func succeed(_ value: Value) {
84
0
    self.completeWith(.success(value))
85
0
  }
86
87
  /// Fail the promise with the given error.
88
0
  internal mutating func fail(_ error: Error) {
89
0
    self.completeWith(.failure(error))
90
0
  }
91
92
  /// Complete the promise with the given result.
93
0
  internal mutating func completeWith(_ result: Result<Value, Error>) {
94
0
    self.eventLoop.preconditionInEventLoop()
95
0
96
0
    switch self.state {
97
0
    case .idle:
98
0
      self.state = .resolvedResult(result)
99
0
100
0
    case let .unresolvedPromise(promise):
101
0
      promise.completeWith(result)
102
0
      self.state = .resolvedFuture(promise.futureResult)
103
0
104
0
    case .resolvedResult, .resolvedFuture:
105
0
      ()
106
0
    }
107
0
  }
108
}