]> git.ipfire.org Git - thirdparty/squid.git/blob - src/base/CodeContext.h
Maintenance: automate header guards 2/3 (#1655)
[thirdparty/squid.git] / src / base / CodeContext.h
1 /*
2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS code_contexts for details.
7 */
8
9 #ifndef SQUID_SRC_BASE_CODECONTEXT_H
10 #define SQUID_SRC_BASE_CODECONTEXT_H
11
12 #include "base/InstanceId.h"
13 #include "base/RefCount.h"
14 #include "base/Stopwatch.h"
15
16 #include <iosfwd>
17
18 /** \file
19 *
20 * Most error-reporting code cannot know what transaction or task Squid was
21 * working on when the error occurred. For example, when Squid HTTP request
22 * parser discovers a malformed header field, the parser can report the field
23 * contents, but that information is often useless for the admin without
24 * processing context details like which client sent the request or what the
25 * requested URL was. Moreover, even when the error reporting code does have
26 * access to some context details, it cannot separate important facts from noise
27 * because such classification is usually deployment-specific (i.e. cannot be
28 * hard-coded) and requires human expertise. The situation is aggravated by a
29 * busy Squid instance constantly switching from one processing context to
30 * another.
31 *
32 * To solve these problems, Squid assigns a CodeContext object to a processing
33 * context. When Squid switches to another processing context, it switches the
34 * current CodeContext object as well. When Squid prints a level-0 or level-1
35 * message to cache.log, it asks the current CodeContext object (if any) to
36 * report context details, allowing the admin to correlate the cache.log message
37 * with an access.log record.
38 *
39 * Squid also reports processing context changes to cache.log when Squid
40 * level-5+ debugging is enabled.
41 *
42 * CodeContext is being retrofitted into existing code with lots of places that
43 * switch processing context. Identifying and adjusting all those places takes
44 * time. Until then, there will be incorrect and missing context attributions.
45 *
46 * @{
47 **/
48
49 /// Interface for reporting what Squid code is working on.
50 /// Such reports are usually requested outside creator's call stack.
51 /// They are especially useful for attributing low-level errors to transactions.
52 class CodeContext: public RefCountable
53 {
54 public:
55 typedef RefCount<CodeContext> Pointer;
56
57 /// \returns the known global context or, to indicate unknown context, nil
58 static const Pointer &Current();
59
60 /// forgets the current context, setting it to nil/unknown
61 static void Reset();
62
63 /// changes the current context; nil argument sets it to nil/unknown
64 static void Reset(const Pointer);
65
66 ~CodeContext() override {}
67
68 /// \returns a small, permanent ID of the current context
69 /// gists persist forever and are suitable for passing to other SMP workers
70 virtual ScopedId codeContextGist() const = 0;
71
72 /// appends human-friendly context description line(s) to a cache.log record
73 virtual std::ostream &detailCodeContext(std::ostream &os) const = 0;
74
75 /// time spent in this context (see also: %busy_time)
76 Stopwatch busyTime;
77
78 private:
79 static void ForgetCurrent();
80 static void Entering(const Pointer &codeCtx);
81 static void Leaving();
82 };
83
84 /// by default, only small context gist is printed
85 inline
86 std::ostream &operator <<(std::ostream &os, const CodeContext &ctx)
87 {
88 return os << ctx.codeContextGist();
89 }
90
91 /* convenience context-reporting wrappers that also reduce linking problems */
92 std::ostream &CurrentCodeContextBrief(std::ostream &os);
93 std::ostream &CurrentCodeContextDetail(std::ostream &os);
94
95 /// Convenience class that automatically restores the current/outer CodeContext
96 /// when leaving the scope of the new-context following/inner code. \see Run().
97 class CodeContextGuard
98 {
99 public:
100 CodeContextGuard(const CodeContext::Pointer &newContext): savedCodeContext(CodeContext::Current()) { CodeContext::Reset(newContext); }
101 ~CodeContextGuard() { CodeContext::Reset(savedCodeContext); }
102
103 // no copying of any kind (for simplicity and to prevent accidental copies)
104 CodeContextGuard(CodeContextGuard &&) = delete;
105
106 CodeContext::Pointer savedCodeContext;
107 };
108
109 /// A helper that calls the given function in the given call context. If the
110 /// function throws, the call context is preserved, so that the exception is
111 /// associated with the context that triggered it.
112 template <typename Fun>
113 inline void
114 CallAndRestore_(const CodeContext::Pointer &context, Fun &&fun)
115 {
116 const auto savedCodeContext(CodeContext::Current());
117 CodeContext::Reset(context);
118 fun();
119 CodeContext::Reset(savedCodeContext);
120 }
121
122 /// Service code running in its own service context should use this function.
123 /// \sa CallAndRestore_()
124 template <typename Fun>
125 inline void
126 CallBack(const CodeContext::Pointer &callbackContext, Fun &&callback)
127 {
128 // TODO: Consider catching exceptions and letting CodeContext handle them.
129 CallAndRestore_(callbackContext, callback);
130 }
131
132 /// To supply error-reporting code with parsing context X (where the error
133 /// occurred), parsing code should use this function when initiating parsing
134 /// inside that context X.
135 /// \sa CallAndRestore_()
136 template <typename Fun>
137 inline void
138 CallParser(const CodeContext::Pointer &parsingContext, Fun &&parse)
139 {
140 CallAndRestore_(parsingContext, parse);
141 }
142
143 /// Executes `service` in `serviceContext` but due to automatic caller context
144 /// restoration, service exceptions are associated with the caller that suffered
145 /// from (and/or caused) them (rather than with the service itself).
146 ///
147 /// Service code running in caller's context should use this function to escape
148 /// into service context (e.g., for submitting caller-agnostic requests).
149 template <typename Fun>
150 inline void
151 CallService(const CodeContext::Pointer &serviceContext, Fun &&service)
152 {
153 // TODO: Consider catching exceptions and letting CodeContext handle them.
154 CodeContextGuard guard(serviceContext);
155 service();
156 }
157
158 /// Executes context `creator` in the service context. If an exception occurs,
159 /// the creator context is preserved, so that the exception is associated with
160 /// the creator that triggered them (rather than with the service).
161 ///
162 /// Service code running in its own context should use this function to create
163 /// new code contexts. TODO: Use or, if this pattern is not repeated, remove.
164 template <typename Fun>
165 inline void
166 CallContextCreator(Fun &&creator)
167 {
168 const auto savedCodeContext(CodeContext::Current());
169 creator();
170 CodeContext::Reset(savedCodeContext);
171 }
172
173 /// @}
174
175 #endif /* SQUID_SRC_BASE_CODECONTEXT_H */
176