Line | Count | Source |
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 | | // This file is copied from |
18 | | // https://github.com/apache/impala/blob/branch-2.9.0/be/src/util/blocking-queue.hpp |
19 | | // and modified by Doris |
20 | | |
21 | | #pragma once |
22 | | |
23 | | #include <unistd.h> |
24 | | |
25 | | #include <atomic> |
26 | | #include <condition_variable> |
27 | | #include <list> |
28 | | #include <mutex> |
29 | | |
30 | | #include "common/logging.h" |
31 | | #include "util/stopwatch.hpp" |
32 | | |
33 | | #ifdef BE_TEST |
34 | | #include "cpp/sync_point.h" |
35 | | #endif |
36 | | |
37 | | namespace doris { |
38 | | // Fixed capacity FIFO queue, where both BlockingGet and BlockingPut operations block |
39 | | // if the queue is empty or full, respectively. |
40 | | template <typename T> |
41 | | class BlockingQueue { |
42 | | public: |
43 | | BlockingQueue(uint32_t max_elements) |
44 | 138 | : _shutdown(false), |
45 | 138 | _max_elements(max_elements), |
46 | 138 | _total_get_wait_time(0), |
47 | 138 | _total_put_wait_time(0), |
48 | 138 | _get_waiting(0), |
49 | 138 | _put_waiting(0) {}_ZN5doris13BlockingQueueISt10shared_ptrIN5arrow11RecordBatchEEEC2Ej Line | Count | Source | 44 | 9 | : _shutdown(false), | 45 | 9 | _max_elements(max_elements), | 46 | 9 | _total_get_wait_time(0), | 47 | 9 | _total_put_wait_time(0), | 48 | 9 | _get_waiting(0), | 49 | 9 | _put_waiting(0) {} |
_ZN5doris13BlockingQueueINS_14WorkThreadPoolILb0EE4TaskEEC2Ej Line | Count | Source | 44 | 56 | : _shutdown(false), | 45 | 56 | _max_elements(max_elements), | 46 | 56 | _total_get_wait_time(0), | 47 | 56 | _total_put_wait_time(0), | 48 | 56 | _get_waiting(0), | 49 | 56 | _put_waiting(0) {} |
_ZN5doris13BlockingQueueIPN7RdKafka7MessageEEC2Ej Line | Count | Source | 44 | 73 | : _shutdown(false), | 45 | 73 | _max_elements(max_elements), | 46 | 73 | _total_get_wait_time(0), | 47 | 73 | _total_put_wait_time(0), | 48 | 73 | _get_waiting(0), | 49 | 73 | _put_waiting(0) {} |
Unexecuted instantiation: _ZN5doris13BlockingQueueISt10shared_ptrIN3Aws7Kinesis5Model6RecordEEEC2Ej |
50 | | |
51 | | // Get an element from the queue, waiting indefinitely for one to become available. |
52 | | // Returns false if we were shut down prior to getting the element, and there |
53 | | // are no more elements available. |
54 | 886k | bool blocking_get(T* out) { return controlled_blocking_get(out, MAX_CV_WAIT_TIMEOUT_MS); }_ZN5doris13BlockingQueueISt10shared_ptrIN5arrow11RecordBatchEEE12blocking_getEPS4_ Line | Count | Source | 54 | 7 | bool blocking_get(T* out) { return controlled_blocking_get(out, MAX_CV_WAIT_TIMEOUT_MS); } |
_ZN5doris13BlockingQueueINS_14WorkThreadPoolILb0EE4TaskEE12blocking_getEPS3_ Line | Count | Source | 54 | 886k | bool blocking_get(T* out) { return controlled_blocking_get(out, MAX_CV_WAIT_TIMEOUT_MS); } |
_ZN5doris13BlockingQueueIPN7RdKafka7MessageEE12blocking_getEPS3_ Line | Count | Source | 54 | 73 | bool blocking_get(T* out) { return controlled_blocking_get(out, MAX_CV_WAIT_TIMEOUT_MS); } |
Unexecuted instantiation: _ZN5doris13BlockingQueueISt10shared_ptrIN3Aws7Kinesis5Model6RecordEEE12blocking_getEPS6_ |
55 | | |
56 | | // Periodically recheck the predicate as a defensive liveness fallback. |
57 | 886k | bool controlled_blocking_get(T* out, int64_t cv_wait_timeout_ms) { |
58 | 886k | MonotonicStopWatch timer; |
59 | 886k | timer.start(); |
60 | 886k | std::unique_lock<std::mutex> unique_lock(_lock); |
61 | | #ifdef BE_TEST |
62 | | TEST_SYNC_POINT("BlockingQueue::controlled_blocking_get::after_lock"); |
63 | | #endif |
64 | 1.78M | while (!(_shutdown || !_list.empty())) { |
65 | 893k | ++_get_waiting; |
66 | | #ifdef BE_TEST |
67 | | TEST_SYNC_POINT("BlockingQueue::controlled_blocking_get::before_wait"); |
68 | | #endif |
69 | 893k | _get_cv.wait_for(unique_lock, std::chrono::milliseconds(cv_wait_timeout_ms)); |
70 | 893k | DCHECK_GT(_get_waiting, 0); |
71 | 893k | --_get_waiting; |
72 | 893k | } |
73 | 886k | _total_get_wait_time += timer.elapsed_time(); |
74 | | |
75 | 886k | if (!_list.empty()) { |
76 | 886k | *out = _list.front(); |
77 | 886k | _list.pop_front(); |
78 | 886k | const bool has_put_waiter = _put_waiting > 0; |
79 | 886k | unique_lock.unlock(); |
80 | 886k | if (has_put_waiter) { |
81 | 0 | _put_cv.notify_one(); |
82 | 0 | } |
83 | 886k | return true; |
84 | 886k | } else { |
85 | 288 | assert(_shutdown); |
86 | 2.75k | return false; |
87 | 288 | } |
88 | 886k | } _ZN5doris13BlockingQueueISt10shared_ptrIN5arrow11RecordBatchEEE23controlled_blocking_getEPS4_l Line | Count | Source | 57 | 7 | bool controlled_blocking_get(T* out, int64_t cv_wait_timeout_ms) { | 58 | 7 | MonotonicStopWatch timer; | 59 | 7 | timer.start(); | 60 | 7 | std::unique_lock<std::mutex> unique_lock(_lock); | 61 | | #ifdef BE_TEST | 62 | | TEST_SYNC_POINT("BlockingQueue::controlled_blocking_get::after_lock"); | 63 | | #endif | 64 | 10 | while (!(_shutdown || !_list.empty())) { | 65 | 3 | ++_get_waiting; | 66 | | #ifdef BE_TEST | 67 | | TEST_SYNC_POINT("BlockingQueue::controlled_blocking_get::before_wait"); | 68 | | #endif | 69 | 3 | _get_cv.wait_for(unique_lock, std::chrono::milliseconds(cv_wait_timeout_ms)); | 70 | 3 | DCHECK_GT(_get_waiting, 0); | 71 | 3 | --_get_waiting; | 72 | 3 | } | 73 | 7 | _total_get_wait_time += timer.elapsed_time(); | 74 | | | 75 | 7 | if (!_list.empty()) { | 76 | 7 | *out = _list.front(); | 77 | 7 | _list.pop_front(); | 78 | 7 | const bool has_put_waiter = _put_waiting > 0; | 79 | 7 | unique_lock.unlock(); | 80 | 7 | if (has_put_waiter) { | 81 | 0 | _put_cv.notify_one(); | 82 | 0 | } | 83 | 7 | return true; | 84 | 7 | } else { | 85 | 0 | assert(_shutdown); | 86 | 0 | return false; | 87 | 0 | } | 88 | 7 | } |
_ZN5doris13BlockingQueueINS_14WorkThreadPoolILb0EE4TaskEE23controlled_blocking_getEPS3_l Line | Count | Source | 57 | 885k | bool controlled_blocking_get(T* out, int64_t cv_wait_timeout_ms) { | 58 | 885k | MonotonicStopWatch timer; | 59 | 885k | timer.start(); | 60 | 885k | std::unique_lock<std::mutex> unique_lock(_lock); | 61 | | #ifdef BE_TEST | 62 | | TEST_SYNC_POINT("BlockingQueue::controlled_blocking_get::after_lock"); | 63 | | #endif | 64 | 1.77M | while (!(_shutdown || !_list.empty())) { | 65 | 893k | ++_get_waiting; | 66 | | #ifdef BE_TEST | 67 | | TEST_SYNC_POINT("BlockingQueue::controlled_blocking_get::before_wait"); | 68 | | #endif | 69 | 893k | _get_cv.wait_for(unique_lock, std::chrono::milliseconds(cv_wait_timeout_ms)); | 70 | 893k | DCHECK_GT(_get_waiting, 0); | 71 | 893k | --_get_waiting; | 72 | 893k | } | 73 | 885k | _total_get_wait_time += timer.elapsed_time(); | 74 | | | 75 | 885k | if (!_list.empty()) { | 76 | 885k | *out = _list.front(); | 77 | 885k | _list.pop_front(); | 78 | 885k | const bool has_put_waiter = _put_waiting > 0; | 79 | 885k | unique_lock.unlock(); | 80 | 885k | if (has_put_waiter) { | 81 | 0 | _put_cv.notify_one(); | 82 | 0 | } | 83 | 885k | return true; | 84 | 885k | } else { | 85 | 142 | assert(_shutdown); | 86 | 2.60k | return false; | 87 | 142 | } | 88 | 885k | } |
_ZN5doris13BlockingQueueIPN7RdKafka7MessageEE23controlled_blocking_getEPS3_l Line | Count | Source | 57 | 888 | bool controlled_blocking_get(T* out, int64_t cv_wait_timeout_ms) { | 58 | 888 | MonotonicStopWatch timer; | 59 | 888 | timer.start(); | 60 | 888 | std::unique_lock<std::mutex> unique_lock(_lock); | 61 | | #ifdef BE_TEST | 62 | | TEST_SYNC_POINT("BlockingQueue::controlled_blocking_get::after_lock"); | 63 | | #endif | 64 | 1.11k | while (!(_shutdown || !_list.empty())) { | 65 | 222 | ++_get_waiting; | 66 | | #ifdef BE_TEST | 67 | | TEST_SYNC_POINT("BlockingQueue::controlled_blocking_get::before_wait"); | 68 | | #endif | 69 | 222 | _get_cv.wait_for(unique_lock, std::chrono::milliseconds(cv_wait_timeout_ms)); | 70 | 222 | DCHECK_GT(_get_waiting, 0); | 71 | 222 | --_get_waiting; | 72 | 222 | } | 73 | 888 | _total_get_wait_time += timer.elapsed_time(); | 74 | | | 75 | 888 | if (!_list.empty()) { | 76 | 742 | *out = _list.front(); | 77 | 742 | _list.pop_front(); | 78 | 742 | const bool has_put_waiter = _put_waiting > 0; | 79 | 742 | unique_lock.unlock(); | 80 | 742 | if (has_put_waiter) { | 81 | 0 | _put_cv.notify_one(); | 82 | 0 | } | 83 | 742 | return true; | 84 | 742 | } else { | 85 | 146 | assert(_shutdown); | 86 | 146 | return false; | 87 | 146 | } | 88 | 888 | } |
Unexecuted instantiation: _ZN5doris13BlockingQueueISt10shared_ptrIN3Aws7Kinesis5Model6RecordEEE23controlled_blocking_getEPS6_l |
89 | | |
90 | | // Puts an element into the queue, waiting indefinitely until there is space. |
91 | | // If the queue is shut down, returns false. |
92 | 12 | bool blocking_put(const T& val) { return controlled_blocking_put(val, MAX_CV_WAIT_TIMEOUT_MS); }_ZN5doris13BlockingQueueISt10shared_ptrIN5arrow11RecordBatchEEE12blocking_putERKS4_ Line | Count | Source | 92 | 12 | bool blocking_put(const T& val) { return controlled_blocking_put(val, MAX_CV_WAIT_TIMEOUT_MS); } |
Unexecuted instantiation: _ZN5doris13BlockingQueueINS_14WorkThreadPoolILb0EE4TaskEE12blocking_putERKS3_ |
93 | | |
94 | | // Periodically recheck the predicate as a defensive liveness fallback. |
95 | 754 | bool controlled_blocking_put(const T& val, int64_t cv_wait_timeout_ms) { |
96 | 754 | MonotonicStopWatch timer; |
97 | 754 | timer.start(); |
98 | 754 | std::unique_lock<std::mutex> unique_lock(_lock); |
99 | 754 | while (!(_shutdown || _list.size() < _max_elements)) { |
100 | 0 | ++_put_waiting; |
101 | | #ifdef BE_TEST |
102 | | TEST_SYNC_POINT("BlockingQueue::controlled_blocking_put::before_wait"); |
103 | | #endif |
104 | 0 | _put_cv.wait_for(unique_lock, std::chrono::milliseconds(cv_wait_timeout_ms)); |
105 | 0 | DCHECK_GT(_put_waiting, 0); |
106 | 0 | --_put_waiting; |
107 | 0 | } |
108 | 754 | _total_put_wait_time += timer.elapsed_time(); |
109 | | |
110 | 754 | if (_shutdown) { |
111 | 0 | return false; |
112 | 0 | } |
113 | | |
114 | 754 | _list.push_back(val); |
115 | 754 | const bool has_get_waiter = _get_waiting > 0; |
116 | 754 | unique_lock.unlock(); |
117 | 754 | if (has_get_waiter) { |
118 | 226 | _get_cv.notify_one(); |
119 | 226 | } |
120 | 754 | return true; |
121 | 754 | } _ZN5doris13BlockingQueueISt10shared_ptrIN5arrow11RecordBatchEEE23controlled_blocking_putERKS4_l Line | Count | Source | 95 | 12 | bool controlled_blocking_put(const T& val, int64_t cv_wait_timeout_ms) { | 96 | 12 | MonotonicStopWatch timer; | 97 | 12 | timer.start(); | 98 | 12 | std::unique_lock<std::mutex> unique_lock(_lock); | 99 | 12 | while (!(_shutdown || _list.size() < _max_elements)) { | 100 | 0 | ++_put_waiting; | 101 | | #ifdef BE_TEST | 102 | | TEST_SYNC_POINT("BlockingQueue::controlled_blocking_put::before_wait"); | 103 | | #endif | 104 | 0 | _put_cv.wait_for(unique_lock, std::chrono::milliseconds(cv_wait_timeout_ms)); | 105 | 0 | DCHECK_GT(_put_waiting, 0); | 106 | 0 | --_put_waiting; | 107 | 0 | } | 108 | 12 | _total_put_wait_time += timer.elapsed_time(); | 109 | | | 110 | 12 | if (_shutdown) { | 111 | 0 | return false; | 112 | 0 | } | 113 | | | 114 | 12 | _list.push_back(val); | 115 | 12 | const bool has_get_waiter = _get_waiting > 0; | 116 | 12 | unique_lock.unlock(); | 117 | 12 | if (has_get_waiter) { | 118 | 3 | _get_cv.notify_one(); | 119 | 3 | } | 120 | 12 | return true; | 121 | 12 | } |
Unexecuted instantiation: _ZN5doris13BlockingQueueINS_14WorkThreadPoolILb0EE4TaskEE23controlled_blocking_putERKS3_l _ZN5doris13BlockingQueueIPN7RdKafka7MessageEE23controlled_blocking_putERKS3_l Line | Count | Source | 95 | 742 | bool controlled_blocking_put(const T& val, int64_t cv_wait_timeout_ms) { | 96 | 742 | MonotonicStopWatch timer; | 97 | 742 | timer.start(); | 98 | 742 | std::unique_lock<std::mutex> unique_lock(_lock); | 99 | 742 | while (!(_shutdown || _list.size() < _max_elements)) { | 100 | 0 | ++_put_waiting; | 101 | | #ifdef BE_TEST | 102 | | TEST_SYNC_POINT("BlockingQueue::controlled_blocking_put::before_wait"); | 103 | | #endif | 104 | 0 | _put_cv.wait_for(unique_lock, std::chrono::milliseconds(cv_wait_timeout_ms)); | 105 | 0 | DCHECK_GT(_put_waiting, 0); | 106 | 0 | --_put_waiting; | 107 | 0 | } | 108 | 742 | _total_put_wait_time += timer.elapsed_time(); | 109 | | | 110 | 742 | if (_shutdown) { | 111 | 0 | return false; | 112 | 0 | } | 113 | | | 114 | 742 | _list.push_back(val); | 115 | 742 | const bool has_get_waiter = _get_waiting > 0; | 116 | 742 | unique_lock.unlock(); | 117 | 742 | if (has_get_waiter) { | 118 | 223 | _get_cv.notify_one(); | 119 | 223 | } | 120 | 742 | return true; | 121 | 742 | } |
Unexecuted instantiation: _ZN5doris13BlockingQueueISt10shared_ptrIN3Aws7Kinesis5Model6RecordEEE23controlled_blocking_putERKS6_l |
122 | | |
123 | | // Return false if queue full or has been shutdown. |
124 | 884k | bool try_put(const T& val) { |
125 | 884k | MonotonicStopWatch timer; |
126 | 884k | timer.start(); |
127 | 884k | std::unique_lock<std::mutex> unique_lock(_lock); |
128 | | #ifdef BE_TEST |
129 | | TEST_SYNC_POINT("BlockingQueue::try_put::after_lock"); |
130 | | #endif |
131 | 884k | _total_put_wait_time += timer.elapsed_time(); |
132 | | |
133 | 885k | if (_shutdown || _list.size() >= _max_elements) { |
134 | 0 | return false; |
135 | 0 | } |
136 | | |
137 | 884k | _list.push_back(val); |
138 | 884k | const bool has_get_waiter = _get_waiting > 0; |
139 | 884k | unique_lock.unlock(); |
140 | 885k | if (has_get_waiter) { |
141 | 885k | _get_cv.notify_one(); |
142 | 885k | } |
143 | 884k | return true; |
144 | 884k | } |
145 | | |
146 | | // Shut down the queue. Wakes up all threads waiting on BlockingGet or BlockingPut. |
147 | 264 | void shutdown() { |
148 | 264 | { |
149 | 264 | std::lock_guard<std::mutex> guard(_lock); |
150 | 264 | _shutdown = true; |
151 | 264 | } |
152 | | |
153 | 264 | _get_cv.notify_all(); |
154 | 264 | _put_cv.notify_all(); |
155 | 264 | } _ZN5doris13BlockingQueueISt10shared_ptrIN5arrow11RecordBatchEEE8shutdownEv Line | Count | Source | 147 | 5 | void shutdown() { | 148 | 5 | { | 149 | 5 | std::lock_guard<std::mutex> guard(_lock); | 150 | 5 | _shutdown = true; | 151 | 5 | } | 152 | | | 153 | 5 | _get_cv.notify_all(); | 154 | 5 | _put_cv.notify_all(); | 155 | 5 | } |
_ZN5doris13BlockingQueueINS_14WorkThreadPoolILb0EE4TaskEE8shutdownEv Line | Count | Source | 147 | 40 | void shutdown() { | 148 | 40 | { | 149 | 40 | std::lock_guard<std::mutex> guard(_lock); | 150 | 40 | _shutdown = true; | 151 | 40 | } | 152 | | | 153 | 40 | _get_cv.notify_all(); | 154 | 40 | _put_cv.notify_all(); | 155 | 40 | } |
_ZN5doris13BlockingQueueIPN7RdKafka7MessageEE8shutdownEv Line | Count | Source | 147 | 219 | void shutdown() { | 148 | 219 | { | 149 | 219 | std::lock_guard<std::mutex> guard(_lock); | 150 | 219 | _shutdown = true; | 151 | 219 | } | 152 | | | 153 | 219 | _get_cv.notify_all(); | 154 | 219 | _put_cv.notify_all(); | 155 | 219 | } |
Unexecuted instantiation: _ZN5doris13BlockingQueueISt10shared_ptrIN3Aws7Kinesis5Model6RecordEEE8shutdownEv |
156 | | |
157 | 898k | uint32_t get_size() const { |
158 | 898k | std::lock_guard<std::mutex> l(_lock); |
159 | 898k | return static_cast<uint32_t>(_list.size()); |
160 | 898k | } _ZNK5doris13BlockingQueueISt10shared_ptrIN5arrow11RecordBatchEEE8get_sizeEv Line | Count | Source | 157 | 8 | uint32_t get_size() const { | 158 | 8 | std::lock_guard<std::mutex> l(_lock); | 159 | 8 | return static_cast<uint32_t>(_list.size()); | 160 | 8 | } |
_ZNK5doris13BlockingQueueINS_14WorkThreadPoolILb0EE4TaskEE8get_sizeEv Line | Count | Source | 157 | 898k | uint32_t get_size() const { | 158 | 898k | std::lock_guard<std::mutex> l(_lock); | 159 | 898k | return static_cast<uint32_t>(_list.size()); | 160 | 898k | } |
_ZNK5doris13BlockingQueueIPN7RdKafka7MessageEE8get_sizeEv Line | Count | Source | 157 | 73 | uint32_t get_size() const { | 158 | 73 | std::lock_guard<std::mutex> l(_lock); | 159 | 73 | return static_cast<uint32_t>(_list.size()); | 160 | 73 | } |
Unexecuted instantiation: _ZNK5doris13BlockingQueueISt10shared_ptrIN3Aws7Kinesis5Model6RecordEEE8get_sizeEv |
161 | | |
162 | 8.54k | uint32_t get_capacity() const { return _max_elements; } |
163 | | |
164 | | // Returns the total amount of time threads have blocked in BlockingGet. |
165 | 8.53k | uint64_t total_get_wait_time() const { return _total_get_wait_time; } |
166 | | |
167 | | // Returns the total amount of time threads have blocked in BlockingPut. |
168 | 8.53k | uint64_t total_put_wait_time() const { return _total_put_wait_time; } |
169 | | |
170 | | private: |
171 | | static constexpr int64_t MAX_CV_WAIT_TIMEOUT_MS = 60 * 60 * 1000; // 1 hour |
172 | | bool _shutdown; |
173 | | const int _max_elements; |
174 | | std::condition_variable _get_cv; // 'get' callers wait on this |
175 | | std::condition_variable _put_cv; // 'put' callers wait on this |
176 | | // _lock guards access to _shutdown, _list, _get_waiting, and _put_waiting. |
177 | | mutable std::mutex _lock; |
178 | | std::list<T> _list; |
179 | | std::atomic<uint64_t> _total_get_wait_time; |
180 | | std::atomic<uint64_t> _total_put_wait_time; |
181 | | // Number of threads currently inside the corresponding wait_for() call. The waiter that |
182 | | // increments a counter is also responsible for decrementing it after every wakeup reason. |
183 | | // Notifiers only inspect these counters and never consume a waiter's registration. |
184 | | size_t _get_waiting; |
185 | | size_t _put_waiting; |
186 | | }; |
187 | | |
188 | | } // namespace doris |