1
#pragma once
2

            
3
#include <cstddef>
4

            
5
#include "envoy/common/pure.h"
6
#include "envoy/common/scope_tracker.h"
7
#include "envoy/stream_info/stream_info.h"
8

            
9
#include "source/common/common/cleanup.h"
10
#include "source/common/common/macros.h"
11
#include "source/common/common/non_copyable.h"
12

            
13
namespace Envoy {
14

            
15
#ifdef ENVOY_ENABLE_EXECUTION_CONTEXT
16

            
17
static constexpr absl::string_view kConnectionExecutionContextFilterStateName =
18
    "envoy.network.connection_execution_context";
19

            
20
namespace Http {
21
struct FilterContext;
22
}
23

            
24
namespace Tracing {
25
class Span;
26
}
27

            
28
class ScopedExecutionContext;
29

            
30
// ExecutionContext can be inherited by subclasses to represent arbitrary information associated
31
// with the execution of a piece of code. activate/deactivate are called when the said execution
32
// starts/ends. For an example usage, please see
33
// https://github.com/envoyproxy/envoy/issues/32012.
34
class ExecutionContext : public StreamInfo::FilterState::Object, NonCopyable {
35
public:
36
1
  static void setEnabled(bool value) { enabled().store(value, std::memory_order_relaxed); }
37

            
38
27
  static bool isEnabled() { return enabled().load(std::memory_order_relaxed); }
39

            
40
17
  static ExecutionContext* fromStreamInfo(OptRef<const StreamInfo::StreamInfo> info) {
41
17
    if (!isEnabled() || !info.has_value()) {
42
4
      return nullptr;
43
4
    }
44
13
    const auto* const_context = info->filterState().getDataReadOnly<ExecutionContext>(
45
13
        kConnectionExecutionContextFilterStateName);
46
13
    return const_cast<ExecutionContext*>(const_context);
47
17
  }
48

            
49
  // Called when enters a scope in which |*span| is active.
50
  // Returns an object that can do some cleanup when exits the scope.
51
  virtual Envoy::Cleanup onScopeEnter(Envoy::Tracing::Span* span) PURE;
52
  // Called when enters a scope in which |*filter_context| is active.
53
  // Returns an object that can do some cleanup when exits the scope.
54
  virtual Envoy::Cleanup onScopeEnter(const Http::FilterContext* filter_context) PURE;
55

            
56
protected:
57
  // Called when the current thread starts to run code on behalf of the owner of this object.
58
  // protected because it should only be called by ScopedExecutionContext.
59
  virtual void activate() PURE;
60
  // Called when the current thread stops running code on behalf of the owner of this object.
61
  // protected because it should only be called by ScopedExecutionContext.
62
  virtual void deactivate() PURE;
63

            
64
private:
65
28
  static std::atomic<bool>& enabled() { MUTABLE_CONSTRUCT_ON_FIRST_USE(std::atomic<bool>); }
66

            
67
  friend class ScopedExecutionContext;
68
};
69

            
70
// ScopedExecutionContext is a stack-only RAII object to call ExecutionContext::activate on
71
// construction and ExecutionContext::deactivate on destruction.
72
//
73
// ScopedExecutionContext is intened to be used in a simple c++ scope:
74
//   {
75
//     ExecutionContext context;
76
//     // context.activate() called here.
77
//     ScopedExecutionContext scoped_execution_context(&context);
78
//     // context.deactivate() called when scoped_execution_context destructs.
79
//   }
80
class ScopedExecutionContext : NonCopyable {
81
public:
82
1
  ScopedExecutionContext() : ScopedExecutionContext(nullptr) {}
83
  ScopedExecutionContext(const ScopeTrackedObject* object)
84
11
      : context_((object != nullptr && ExecutionContext::isEnabled())
85
11
                     ? ExecutionContext::fromStreamInfo(object->trackedStream())
86
11
                     : nullptr) {
87
11
    if (context_ != nullptr) {
88
7
      context_->activate();
89
7
    }
90
11
  }
91

            
92
11
  ~ScopedExecutionContext() {
93
11
    if (context_ != nullptr) {
94
7
      context_->deactivate();
95
7
    }
96
11
  }
97

            
98
  // This object is stack-only, it is part of ScopeTrackerScopeState which is
99
  // also stack-only.
100
  void* operator new(std::size_t) = delete;
101

            
102
3
  bool isNull() const { return context_ == nullptr; }
103

            
104
private:
105
  ExecutionContext* context_;
106
};
107

            
108
#define ENVOY_EXECUTION_SCOPE_CAT_(a, b) a##b
109
#define ENVOY_EXECUTION_SCOPE_CAT(a, b) ENVOY_EXECUTION_SCOPE_CAT_(a, b)
110
// Invoked when |scopedObject| is active from the current line to the end of the current C++ scope.
111
// |trackedStream| is an OptRef<const StreamInfo> from which an ExecutionContext is extracted.
112
// |scopedObject| is a pointer to an Envoy::Tracing::Span or an Http::FilterContext.
113
#define ENVOY_EXECUTION_SCOPE(trackedStream, scopedObject)                                         \
114
8
  Envoy::Cleanup ENVOY_EXECUTION_SCOPE_CAT(on_scope_exit_, __LINE__) =                             \
115
8
      [execution_context = ExecutionContext::fromStreamInfo(trackedStream),                        \
116
8
       scoped_object = (scopedObject)] {                                                           \
117
8
        if (execution_context == nullptr) {                                                        \
118
4
          return Envoy::Cleanup::noop();                                                           \
119
4
        }                                                                                          \
120
8
        return execution_context->onScopeEnter(scoped_object);                                     \
121
8
      }()
122
#else
123
#define ENVOY_EXECUTION_SCOPE(trackedStream, scopedObject)
124
#endif
125

            
126
} // namespace Envoy