master
h 81 lines 3.09 KB
Raw
1 /*++
2
3 Copyright (c) Microsoft. All rights reserved.
4
5 Module Name:
6
7 SlowOperationWatcher.h
8
9 Abstract:
10
11 RAII guard that watches a scoped operation. A threadpool timer is armed in the
12 constructor for `SlowThreshold` (10 s default). On the fast path (scope exits
13 before the threshold) the watcher's destructor cancels and drains the timer and
14 nothing is emitted. If the threshold is reached first -- including while the
15 scope is still running or hung indefinitely -- the timer callback fires once,
16 emitting a single `SlowOperation` telemetry event carrying the phase name and
17 the call site captured via std::source_location, so the backend can attribute
18 where time is spent.
19
20 Usage:
21
22 SlowOperationWatcher slow{"WaitForMiniInitConnect"};
23 m_miniInitChannel = wsl::shared::SocketChannel{AcceptConnection(timeout), ...};
24
25 If the scope needs to outlive the watched operation (for example to keep a
26 pointer into an internal receive buffer alive without a nested block), call
27 Reset() to disarm the watcher early:
28
29 SlowOperationWatcher slow{"WaitForCreateInstanceResult"};
30 const auto& result = channel.ReceiveMessage<...>(...);
31 slow.Reset();
32 // result remains valid and usable here
33
34 --*/
35
36 #pragma once
37
38 #include <windows.h>
39 #include <wil/resource.h>
40 #include <chrono>
41 #include <source_location>
42
43 class SlowOperationWatcher
44 {
45 public:
46 // Name is restricted to a string-literal reference (const char (&)[N]) to guarantee
47 // static storage duration: the raw pointer is dereferenced later from a threadpool
48 // callback, so accepting a `const char*` would make UAF via a temporary (e.g.
49 // std::string::c_str()) easy. Keep Name a short CamelCase phase identifier that the
50 // backend query can switch on (e.g. "WaitForMiniInitConnect").
51 template <size_t N>
52 explicit SlowOperationWatcher(
53 const char (&Name)[N],
54 std::chrono::milliseconds SlowThreshold = std::chrono::seconds{10},
55 std::source_location Location = std::source_location::current()) :
56 SlowOperationWatcher(static_cast<const char*>(Name), SlowThreshold, Location)
57 {
58 }
59
60 ~SlowOperationWatcher() noexcept = default;
61
62 // Disarm the watcher early. After Reset() returns, the threshold callback is
63 // guaranteed not to fire. Relies on wil::unique_threadpool_timer's destroyer to
64 // cancel pending callbacks and drain any in-flight one.
65 void Reset() noexcept;
66
67 SlowOperationWatcher(const SlowOperationWatcher&) = delete;
68 SlowOperationWatcher& operator=(const SlowOperationWatcher&) = delete;
69 SlowOperationWatcher(SlowOperationWatcher&&) = delete;
70 SlowOperationWatcher& operator=(SlowOperationWatcher&&) = delete;
71
72 private:
73 explicit SlowOperationWatcher(_In_z_ const char* Name, std::chrono::milliseconds SlowThreshold, std::source_location Location);
74
75 static void CALLBACK OnTimerFired(PTP_CALLBACK_INSTANCE, PVOID Context, PTP_TIMER) noexcept;
76
77 const char* const m_name;
78 const std::chrono::milliseconds m_slowThreshold;
79 const std::source_location m_location;
80 wil::unique_threadpool_timer m_timer;
81 };