Coverage Report

Created: 2026-10-23 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/trafficserver/include/tscore/PendingAction.h
Line
Count
Source
1
/** @file
2
3
Container for a pending @c Action.
4
5
@section license License
6
7
Licensed to the Apache Software Foundation (ASF) under one
8
or more contributor license agreements.  See the NOTICE file
9
distributed with this work for additional information
10
regarding copyright ownership.  The ASF licenses this file
11
to you under the Apache License, Version 2.0 (the
12
"License"); you may not use this file except in compliance
13
with the License.  You may obtain a copy of the License at
14
15
    http://www.apache.org/licenses/LICENSE-2.0
16
17
Unless required by applicable law or agreed to in writing, software
18
distributed under the License is distributed on an "AS IS" BASIS,
19
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20
See the License for the specific language governing permissions and
21
limitations under the License.
22
*/
23
24
#pragma once
25
26
/** Hold a pending @c Action.
27
 *
28
 * This is modeled on smart pointer classes. This class wraps a pointer to
29
 * an @c Action (which is also the super type for @c Event). If cleared or
30
 * re-assigned the current @c Action, if any, is canceled before the pointer is
31
 * lost to avoid ghost actions triggering after the main continuation is gone.
32
 *
33
 * The class is aware of the special value @c ACTION_RESULT_DONE. If that is assigned
34
 * the pending action will not be canceled or cleared.
35
 */
36
class PendingAction
37
{
38
  using self_type = PendingAction;
39
40
public:
41
0
  PendingAction()                  = default;
42
  PendingAction(self_type const &) = delete;
43
  ~PendingAction();
44
  self_type &operator=(self_type const &) = delete;
45
46
  /// Check if there is an action.
47
  /// @return @c true if no action is present, @c false otherwise.
48
  bool empty() const;
49
50
  /** Assign a new @a action.
51
   *
52
   * @param action The instance to store.
53
   * @return @a this
54
   *
55
   * Any existing @c Action is canceled.
56
   * Assignment is thread
57
   */
58
  self_type &operator=(Action *action);
59
60
  /** Get the @c Continuation for the @c Action.
61
   *
62
   * @return A pointer to the continuation if there is an @c Action, @c nullptr if not.
63
   */
64
  Continuation *get_continuation() const;
65
66
  /** Get the @c Action.
67
   *
68
   * @return A pointer to the @c Action is present, @c nullptr if not.
69
   */
70
  Action *get() const;
71
72
  /** Clear the current @c Action if it is @a action.
73
   *
74
   * @param action @c Action to check.
75
   *
76
   * @return @c true if the action was cleared, @c false if not.
77
   *
78
   * This clears the internal pointer without any side effect. it is used when the @c Action
79
   * is handled and therefore should no longer be canceled.
80
   */
81
  bool clear_if_action_is(Action *action);
82
83
private:
84
  std::atomic<Action *> pending_action = nullptr;
85
};
86
87
inline bool
88
PendingAction::empty() const
89
0
{
90
0
  return pending_action == nullptr;
91
0
}
92
93
inline PendingAction &
94
PendingAction::operator=(Action *action)
95
0
{
96
  // Apparently @c HttpSM depends on not canceling the previous action if a new
97
  // one completes immediately. Canceling the contained action in that case
98
  // cause the @c HttpSM to permanently stall.
99
0
  if (ACTION_RESULT_DONE != action) {
100
0
    Action *expected; // Need for exchange, and to load @a pending_action only once.
101
    // Avoid race conditions - for each assigned action, ensure exactly one thread
102
    // cancels it. Assigning @a expected in the @c while expression avoids potential
103
    // races if two calls to this method have the same @a action.
104
0
    while ((expected = pending_action) != action) {
105
0
      if (pending_action.compare_exchange_strong(expected, action)) {
106
        // This thread did the swap and now no other thread can get @a expected which
107
        // must be canceled if it's not @c nullptr and then this thread is done.
108
0
        if (expected != nullptr) {
109
0
          expected->cancel();
110
0
        }
111
0
        break;
112
0
      }
113
0
    }
114
0
  }
115
0
  return *this;
116
0
}
117
118
inline Continuation *
119
PendingAction::get_continuation() const
120
0
{
121
0
  return pending_action ? pending_action.load()->continuation : nullptr;
122
0
}
123
124
inline Action *
125
PendingAction::get() const
126
0
{
127
0
  return pending_action;
128
0
}
129
130
inline PendingAction::~PendingAction()
131
0
{
132
0
  if (pending_action) {
133
0
    pending_action.load()->cancel();
134
0
  }
135
0
}
136
137
inline bool
138
PendingAction::clear_if_action_is(Action *action)
139
0
{
140
0
  if (action != nullptr) {
141
0
    if (pending_action.compare_exchange_strong(action, nullptr)) {
142
      // do NOT cancel - this is called when the event is handled.
143
0
      return true;
144
0
    }
145
0
  }
146
0
  return false;
147
0
}