Skip to content

Commit 726ca90

Browse files
committed
add timed lock and PerScannerWaitSchedLockTime
1 parent 4f3457b commit 726ca90

File tree

5 files changed

+343
-3
lines changed

5 files changed

+343
-3
lines changed

be/src/util/timed_lock.h

Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
#pragma once
19+
20+
#include <chrono>
21+
#include <cstdint>
22+
#include <mutex>
23+
#include <shared_mutex>
24+
25+
namespace doris {
26+
27+
// A RAII-style lock wrapper that can optionally record the time spent waiting for the lock.
28+
// Similar to std::unique_lock but with timing capabilities.
29+
//
30+
// Usage example:
31+
// std::shared_mutex mutex;
32+
// int64_t wait_time_ns = 0;
33+
// {
34+
// TimedLock<std::shared_mutex> lock(mutex, &wait_time_ns);
35+
// // Critical section
36+
// }
37+
// // wait_time_ns now contains the time spent waiting for the lock in nanoseconds
38+
template <typename Mutex>
39+
class TimedLock {
40+
public:
41+
using mutex_type = Mutex;
42+
43+
// Constructor that acquires the lock and optionally records wait time
44+
// @param mutex: The mutex to lock
45+
// @param wait_time_ns: Optional pointer to store the wait time in nanoseconds
46+
explicit TimedLock(mutex_type& mutex, int64_t* wait_time_ns = nullptr)
47+
: _mutex(&mutex), _owns_lock(false), _wait_time_ns(wait_time_ns) {
48+
lock();
49+
}
50+
51+
// Constructor with defer_lock - does not acquire the lock
52+
TimedLock(mutex_type& mutex, std::defer_lock_t, int64_t* wait_time_ns = nullptr) noexcept
53+
: _mutex(&mutex), _owns_lock(false), _wait_time_ns(wait_time_ns) {}
54+
55+
// Constructor with adopt_lock - assumes the calling thread already owns the lock
56+
TimedLock(mutex_type& mutex, std::adopt_lock_t, int64_t* wait_time_ns = nullptr) noexcept
57+
: _mutex(&mutex), _owns_lock(true), _wait_time_ns(wait_time_ns) {}
58+
59+
// Destructor - releases the lock if owned
60+
~TimedLock() {
61+
if (_owns_lock) {
62+
unlock();
63+
}
64+
}
65+
66+
// Delete copy constructor and assignment operator
67+
TimedLock(const TimedLock&) = delete;
68+
TimedLock& operator=(const TimedLock&) = delete;
69+
70+
// Move constructor
71+
TimedLock(TimedLock&& other) noexcept
72+
: _mutex(other._mutex),
73+
_owns_lock(other._owns_lock),
74+
_wait_time_ns(other._wait_time_ns) {
75+
other._mutex = nullptr;
76+
other._owns_lock = false;
77+
other._wait_time_ns = nullptr;
78+
}
79+
80+
// Move assignment operator
81+
TimedLock& operator=(TimedLock&& other) noexcept {
82+
if (this != &other) {
83+
if (_owns_lock) {
84+
unlock();
85+
}
86+
_mutex = other._mutex;
87+
_owns_lock = other._owns_lock;
88+
_wait_time_ns = other._wait_time_ns;
89+
other._mutex = nullptr;
90+
other._owns_lock = false;
91+
other._wait_time_ns = nullptr;
92+
}
93+
return *this;
94+
}
95+
96+
// Acquires the lock and records wait time if pointer is provided
97+
void lock() {
98+
if (!_mutex) {
99+
throw std::system_error(std::make_error_code(std::errc::operation_not_permitted),
100+
"TimedLock: mutex is null");
101+
}
102+
if (_owns_lock) {
103+
throw std::system_error(std::make_error_code(std::errc::resource_deadlock_would_occur),
104+
"TimedLock: already owns lock");
105+
}
106+
107+
auto start = std::chrono::steady_clock::now();
108+
_mutex->lock();
109+
auto end = std::chrono::steady_clock::now();
110+
111+
_owns_lock = true;
112+
113+
if (_wait_time_ns) {
114+
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start);
115+
*_wait_time_ns = duration.count();
116+
}
117+
}
118+
119+
// Releases the lock
120+
void unlock() {
121+
if (!_owns_lock) {
122+
throw std::system_error(std::make_error_code(std::errc::operation_not_permitted),
123+
"TimedLock: does not own lock");
124+
}
125+
if (_mutex) {
126+
_mutex->unlock();
127+
_owns_lock = false;
128+
}
129+
}
130+
131+
// Checks whether this object owns the lock
132+
bool owns_lock() const noexcept { return _owns_lock; }
133+
134+
// Checks whether this object owns the lock (for use in boolean contexts)
135+
explicit operator bool() const noexcept { return owns_lock(); }
136+
137+
// Returns a pointer to the associated mutex
138+
mutex_type* mutex() const noexcept { return _mutex; }
139+
140+
// Disassociates the mutex without unlocking it
141+
mutex_type* release() noexcept {
142+
mutex_type* ret = _mutex;
143+
_mutex = nullptr;
144+
_owns_lock = false;
145+
return ret;
146+
}
147+
148+
// Transfers ownership to a std::unique_lock
149+
// This is useful when you need to pass the lock to a function that expects std::unique_lock
150+
// After calling this method, this TimedLock object no longer owns the lock
151+
std::unique_lock<mutex_type> transfer_to_unique_lock() noexcept {
152+
if (!_owns_lock || !_mutex) {
153+
return std::unique_lock<mutex_type>();
154+
}
155+
mutex_type* m = release();
156+
return std::unique_lock<mutex_type>(*m, std::adopt_lock);
157+
}
158+
159+
private:
160+
mutex_type* _mutex;
161+
bool _owns_lock;
162+
int64_t* _wait_time_ns;
163+
};
164+
165+
// Specialization for shared_mutex with shared (read) lock
166+
template <typename Mutex>
167+
class TimedSharedLock {
168+
public:
169+
using mutex_type = Mutex;
170+
171+
// Constructor that acquires the shared lock and optionally records wait time
172+
// @param mutex: The mutex to lock
173+
// @param wait_time_ns: Optional pointer to store the wait time in nanoseconds
174+
explicit TimedSharedLock(mutex_type& mutex, int64_t* wait_time_ns = nullptr)
175+
: _mutex(&mutex), _owns_lock(false), _wait_time_ns(wait_time_ns) {
176+
lock();
177+
}
178+
179+
// Constructor with defer_lock - does not acquire the lock
180+
TimedSharedLock(mutex_type& mutex, std::defer_lock_t,
181+
int64_t* wait_time_ns = nullptr) noexcept
182+
: _mutex(&mutex), _owns_lock(false), _wait_time_ns(wait_time_ns) {}
183+
184+
// Constructor with adopt_lock - assumes the calling thread already owns the lock
185+
TimedSharedLock(mutex_type& mutex, std::adopt_lock_t,
186+
int64_t* wait_time_ns = nullptr) noexcept
187+
: _mutex(&mutex), _owns_lock(true), _wait_time_ns(wait_time_ns) {}
188+
189+
// Destructor - releases the lock if owned
190+
~TimedSharedLock() {
191+
if (_owns_lock) {
192+
unlock();
193+
}
194+
}
195+
196+
// Delete copy constructor and assignment operator
197+
TimedSharedLock(const TimedSharedLock&) = delete;
198+
TimedSharedLock& operator=(const TimedSharedLock&) = delete;
199+
200+
// Move constructor
201+
TimedSharedLock(TimedSharedLock&& other) noexcept
202+
: _mutex(other._mutex),
203+
_owns_lock(other._owns_lock),
204+
_wait_time_ns(other._wait_time_ns) {
205+
other._mutex = nullptr;
206+
other._owns_lock = false;
207+
other._wait_time_ns = nullptr;
208+
}
209+
210+
// Move assignment operator
211+
TimedSharedLock& operator=(TimedSharedLock&& other) noexcept {
212+
if (this != &other) {
213+
if (_owns_lock) {
214+
unlock();
215+
}
216+
_mutex = other._mutex;
217+
_owns_lock = other._owns_lock;
218+
_wait_time_ns = other._wait_time_ns;
219+
other._mutex = nullptr;
220+
other._owns_lock = false;
221+
other._wait_time_ns = nullptr;
222+
}
223+
return *this;
224+
}
225+
226+
// Acquires the shared lock and records wait time if pointer is provided
227+
void lock() {
228+
if (!_mutex) {
229+
throw std::system_error(std::make_error_code(std::errc::operation_not_permitted),
230+
"TimedSharedLock: mutex is null");
231+
}
232+
if (_owns_lock) {
233+
throw std::system_error(std::make_error_code(std::errc::resource_deadlock_would_occur),
234+
"TimedSharedLock: already owns lock");
235+
}
236+
237+
auto start = std::chrono::steady_clock::now();
238+
_mutex->lock_shared();
239+
auto end = std::chrono::steady_clock::now();
240+
241+
_owns_lock = true;
242+
243+
if (_wait_time_ns) {
244+
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start);
245+
*_wait_time_ns = duration.count();
246+
}
247+
}
248+
249+
// Releases the shared lock
250+
void unlock() {
251+
if (!_owns_lock) {
252+
throw std::system_error(std::make_error_code(std::errc::operation_not_permitted),
253+
"TimedSharedLock: does not own lock");
254+
}
255+
if (_mutex) {
256+
_mutex->unlock_shared();
257+
_owns_lock = false;
258+
}
259+
}
260+
261+
// Checks whether this object owns the lock
262+
bool owns_lock() const noexcept { return _owns_lock; }
263+
264+
// Checks whether this object owns the lock (for use in boolean contexts)
265+
explicit operator bool() const noexcept { return owns_lock(); }
266+
267+
// Returns a pointer to the associated mutex
268+
mutex_type* mutex() const noexcept { return _mutex; }
269+
270+
// Disassociates the mutex without unlocking it
271+
mutex_type* release() noexcept {
272+
mutex_type* ret = _mutex;
273+
_mutex = nullptr;
274+
_owns_lock = false;
275+
return ret;
276+
}
277+
278+
// Transfers ownership to a std::shared_lock
279+
// This is useful when you need to pass the lock to a function that expects std::shared_lock
280+
// After calling this method, this TimedSharedLock object no longer owns the lock
281+
std::shared_lock<mutex_type> transfer_to_shared_lock() noexcept {
282+
if (!_owns_lock || !_mutex) {
283+
return std::shared_lock<mutex_type>();
284+
}
285+
mutex_type* m = release();
286+
return std::shared_lock<mutex_type>(*m, std::adopt_lock);
287+
}
288+
289+
private:
290+
mutex_type* _mutex;
291+
bool _owns_lock;
292+
int64_t* _wait_time_ns;
293+
};
294+
295+
} // namespace doris
296+
297+

be/src/vec/exec/scan/scanner.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,12 @@ class Scanner {
149149

150150
int64_t get_scanner_wait_worker_timer() const { return _scanner_wait_worker_timer; }
151151

152+
void update_sched_lock_timer(int64_t delta_ns) { _scanner_sched_lock_timer += delta_ns; }
153+
void update_submit_count() { ++_scanner_submit_count; }
154+
155+
int64_t get_scanner_sched_lock_timer() const { return _scanner_sched_lock_timer; }
156+
int64_t get_scanner_submit_count() const { return _scanner_submit_count; }
157+
152158
void update_scan_cpu_timer();
153159

154160
// Some counters need to be updated realtime, for example, workload group policy need
@@ -262,6 +268,9 @@ class Scanner {
262268
int64_t _projection_timer = 0;
263269

264270
bool _should_stop = false;
271+
272+
int64_t _scanner_sched_lock_timer = 0;
273+
int64_t _scanner_submit_count = 0;
265274
};
266275

267276
using ScannerSPtr = std::shared_ptr<Scanner>;

be/src/vec/exec/scan/scanner_context.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,10 +388,14 @@ void ScannerContext::stop_scanners(RuntimeState* state) {
388388
std::stringstream scanner_rows_read;
389389
std::stringstream scanner_wait_worker_time;
390390
std::stringstream scanner_projection;
391+
std::stringstream scanner_sched_lock_time;
392+
std::stringstream scanner_summit_count;
391393
scanner_statistics << "[";
392394
scanner_rows_read << "[";
393395
scanner_wait_worker_time << "[";
394396
scanner_projection << "[";
397+
scanner_sched_lock_time << "[";
398+
scanner_summit_count << "[";
395399
// Scanners can in 3 state
396400
// state 1: in scanner context, not scheduled
397401
// state 2: in scanner worker pool's queue, scheduled but not running
@@ -415,17 +419,24 @@ void ScannerContext::stop_scanners(RuntimeState* state) {
415419
<< PrettyPrinter::print(scanner->_scanner->get_scanner_wait_worker_timer(),
416420
TUnit::TIME_NS)
417421
<< ", ";
422+
423+
scanner_sched_lock_time << PrettyPrinter::print(scanner->_scanner->get_scanner_sched_lock_timer(), TUnit::TIME_NS) << ", ";
424+
scanner_summit_count << PrettyPrinter::print(scanner->_scanner->get_scanner_submit_count(), TUnit::UNIT) << ", ";
418425
// since there are all scanners, some scanners is running, so that could not call scanner
419426
// close here.
420427
}
421428
scanner_statistics << "]";
422429
scanner_rows_read << "]";
423430
scanner_wait_worker_time << "]";
424431
scanner_projection << "]";
432+
scanner_sched_lock_time << "]";
433+
scanner_summit_count << "]";
425434
_scanner_profile->add_info_string("PerScannerRunningTime", scanner_statistics.str());
426435
_scanner_profile->add_info_string("PerScannerRowsRead", scanner_rows_read.str());
427436
_scanner_profile->add_info_string("PerScannerWaitTime", scanner_wait_worker_time.str());
428437
_scanner_profile->add_info_string("PerScannerProjectionTime", scanner_projection.str());
438+
_scanner_profile->add_info_string("PerScannerWaitSchedLockTime", scanner_sched_lock_time.str());
439+
_scanner_profile->add_info_string("PerScannerSubmitCount", scanner_summit_count.str());
429440
}
430441
}
431442

@@ -576,6 +587,9 @@ Status ScannerContext::schedule_scan_task(std::shared_ptr<ScanTask> current_scan
576587
_set_scanner_done();
577588
return _process_status;
578589
}
590+
if (auto s = scan_task_iter->scanner.lock()) {
591+
s->_scanner->update_submit_count();
592+
}
579593
}
580594

581595
return Status::OK();

be/src/vec/exec/scan/scanner_scheduler.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ class ThreadPoolSimplifiedScanScheduler MOCK_REMOVE(final) : public ScannerSched
156156
#ifndef BE_TEST
157157
stop();
158158
#endif
159-
LOG(INFO) << "Scanner sche " << _sched_name << " shutdown";
159+
LOG(INFO) << "Scanner sched " << _sched_name << " shutdown";
160160
}
161161

162162
void stop() override {

0 commit comments

Comments
 (0)