/root/doris/be/src/util/once.h
Line | Count | Source (jump to first uncovered line) |
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/once.h |
19 | | // and modified by Doris |
20 | | |
21 | | #pragma once |
22 | | |
23 | | #include <atomic> |
24 | | #include <mutex> |
25 | | #include <stdexcept> |
26 | | |
27 | | #include "common/exception.h" |
28 | | #include "olap/olap_common.h" |
29 | | |
30 | | namespace doris { |
31 | | |
32 | | // Utility class for implementing thread-safe call-once semantics. |
33 | | // |
34 | | // call() will return stored result regardless of whether the first invocation |
35 | | // returns a success status or not. |
36 | | // |
37 | | // Example: |
38 | | // class Resource { |
39 | | // public: |
40 | | // Status init() { |
41 | | // _init_once.call([this] { return _do_init(); }); |
42 | | // } |
43 | | // |
44 | | // bool is_inited() const { |
45 | | // return _init_once.has_called() && _init_once.stored_result().ok(); |
46 | | // } |
47 | | // private: |
48 | | // Status _do_init() { /* init logic here */ } |
49 | | // DorisCallOnce<Status> _init_once; |
50 | | // }; |
51 | | template <typename ReturnType> |
52 | | class DorisCallOnce { |
53 | | public: |
54 | 52.6k | DorisCallOnce() : _has_called(false) {} |
55 | | |
56 | | // this method is not exception safe, it will core when exception occurs in |
57 | | // callback method. I have tested the code https://en.cppreference.com/w/cpp/thread/call_once. |
58 | | // If the underlying `once_flag` has yet to be invoked, invokes the provided |
59 | | // lambda and stores its return value. Otherwise, returns the stored Status. |
60 | | // template <typename Fn> |
61 | | // ReturnType call(Fn fn) { |
62 | | // std::call_once(_once_flag, [this, fn] { |
63 | | // _status = fn(); |
64 | | // _has_called.store(true, std::memory_order_release); |
65 | | // }); |
66 | | // return _status; |
67 | | // } |
68 | | |
69 | | // If exception occurs in the function, the call flag is set, if user call |
70 | | // it again, the same exception will be thrown. |
71 | | // It is different from std::call_once. This is because if a method is called once |
72 | | // some internal state is changed, it maybe not called again although exception |
73 | | // occurred. |
74 | | template <typename Fn> |
75 | 57.1k | ReturnType call(Fn fn) { |
76 | | // Avoid lock to improve performance |
77 | 57.1k | if (has_called()) { |
78 | 36.7k | if (_eptr) { |
79 | 1 | std::rethrow_exception(_eptr); |
80 | 1 | } |
81 | 36.7k | return _status; |
82 | 36.7k | } |
83 | 20.4k | std::lock_guard l(_flag_lock); |
84 | | // should check again because maybe another thread call successfully. |
85 | 20.4k | if (has_called()) { |
86 | 0 | if (_eptr) { |
87 | 0 | std::rethrow_exception(_eptr); |
88 | 0 | } |
89 | 0 | return _status; |
90 | 0 | } |
91 | 20.4k | try { |
92 | 20.4k | _status = fn(); |
93 | 20.4k | } catch (...) { |
94 | | // Save the exception for next call. |
95 | 3 | _eptr = std::current_exception(); |
96 | 3 | _has_called.store(true, std::memory_order_release); |
97 | 3 | std::rethrow_exception(_eptr); |
98 | 3 | } |
99 | | // This memory order make sure both status and eptr is set |
100 | | // and will be seen in another thread. |
101 | 20.4k | _has_called.store(true, std::memory_order_release); |
102 | 20.4k | return _status; |
103 | 20.4k | } Unexecuted instantiation: _ZN5doris13DorisCallOnceINS_6StatusEE4callIZNS_10segment_v212ColumnReader22set_dict_encoding_typeENS5_16DictEncodingTypeEEUlvE_EES1_T_ doris_callonce_test.cpp:_ZN5doris13DorisCallOnceINS_6StatusEE4callIZNS_33DorisCallOnceTest_TestNormal_Test8TestBodyEvE3$_0EES1_T_ Line | Count | Source | 75 | 1 | ReturnType call(Fn fn) { | 76 | | // Avoid lock to improve performance | 77 | 1 | if (has_called()) { | 78 | 0 | if (_eptr) { | 79 | 0 | std::rethrow_exception(_eptr); | 80 | 0 | } | 81 | 0 | return _status; | 82 | 0 | } | 83 | 1 | std::lock_guard l(_flag_lock); | 84 | | // should check again because maybe another thread call successfully. | 85 | 1 | if (has_called()) { | 86 | 0 | if (_eptr) { | 87 | 0 | std::rethrow_exception(_eptr); | 88 | 0 | } | 89 | 0 | return _status; | 90 | 0 | } | 91 | 1 | try { | 92 | 1 | _status = fn(); | 93 | 1 | } catch (...) { | 94 | | // Save the exception for next call. | 95 | 0 | _eptr = std::current_exception(); | 96 | 0 | _has_called.store(true, std::memory_order_release); | 97 | 0 | std::rethrow_exception(_eptr); | 98 | 0 | } | 99 | | // This memory order make sure both status and eptr is set | 100 | | // and will be seen in another thread. | 101 | 1 | _has_called.store(true, std::memory_order_release); | 102 | 1 | return _status; | 103 | 1 | } |
doris_callonce_test.cpp:_ZN5doris13DorisCallOnceINS_6StatusEE4callIZNS_33DorisCallOnceTest_TestNormal_Test8TestBodyEvE3$_1EES1_T_ Line | Count | Source | 75 | 1 | ReturnType call(Fn fn) { | 76 | | // Avoid lock to improve performance | 77 | 1 | if (has_called()) { | 78 | 1 | if (_eptr) { | 79 | 0 | std::rethrow_exception(_eptr); | 80 | 0 | } | 81 | 1 | return _status; | 82 | 1 | } | 83 | 0 | std::lock_guard l(_flag_lock); | 84 | | // should check again because maybe another thread call successfully. | 85 | 0 | if (has_called()) { | 86 | 0 | if (_eptr) { | 87 | 0 | std::rethrow_exception(_eptr); | 88 | 0 | } | 89 | 0 | return _status; | 90 | 0 | } | 91 | 0 | try { | 92 | 0 | _status = fn(); | 93 | 0 | } catch (...) { | 94 | | // Save the exception for next call. | 95 | 0 | _eptr = std::current_exception(); | 96 | 0 | _has_called.store(true, std::memory_order_release); | 97 | 0 | std::rethrow_exception(_eptr); | 98 | 0 | } | 99 | | // This memory order make sure both status and eptr is set | 100 | | // and will be seen in another thread. | 101 | 0 | _has_called.store(true, std::memory_order_release); | 102 | 0 | return _status; | 103 | 0 | } |
doris_callonce_test.cpp:_ZN5doris13DorisCallOnceINS_6StatusEE4callIZNS_39DorisCallOnceTest_TestErrorHappens_Test8TestBodyEvE3$_0EES1_T_ Line | Count | Source | 75 | 1 | ReturnType call(Fn fn) { | 76 | | // Avoid lock to improve performance | 77 | 1 | if (has_called()) { | 78 | 0 | if (_eptr) { | 79 | 0 | std::rethrow_exception(_eptr); | 80 | 0 | } | 81 | 0 | return _status; | 82 | 0 | } | 83 | 1 | std::lock_guard l(_flag_lock); | 84 | | // should check again because maybe another thread call successfully. | 85 | 1 | if (has_called()) { | 86 | 0 | if (_eptr) { | 87 | 0 | std::rethrow_exception(_eptr); | 88 | 0 | } | 89 | 0 | return _status; | 90 | 0 | } | 91 | 1 | try { | 92 | 1 | _status = fn(); | 93 | 1 | } catch (...) { | 94 | | // Save the exception for next call. | 95 | 0 | _eptr = std::current_exception(); | 96 | 0 | _has_called.store(true, std::memory_order_release); | 97 | 0 | std::rethrow_exception(_eptr); | 98 | 0 | } | 99 | | // This memory order make sure both status and eptr is set | 100 | | // and will be seen in another thread. | 101 | 1 | _has_called.store(true, std::memory_order_release); | 102 | 1 | return _status; | 103 | 1 | } |
doris_callonce_test.cpp:_ZN5doris13DorisCallOnceINS_6StatusEE4callIZNS_39DorisCallOnceTest_TestErrorHappens_Test8TestBodyEvE3$_1EES1_T_ Line | Count | Source | 75 | 1 | ReturnType call(Fn fn) { | 76 | | // Avoid lock to improve performance | 77 | 1 | if (has_called()) { | 78 | 1 | if (_eptr) { | 79 | 0 | std::rethrow_exception(_eptr); | 80 | 0 | } | 81 | 1 | return _status; | 82 | 1 | } | 83 | 0 | std::lock_guard l(_flag_lock); | 84 | | // should check again because maybe another thread call successfully. | 85 | 0 | if (has_called()) { | 86 | 0 | if (_eptr) { | 87 | 0 | std::rethrow_exception(_eptr); | 88 | 0 | } | 89 | 0 | return _status; | 90 | 0 | } | 91 | 0 | try { | 92 | 0 | _status = fn(); | 93 | 0 | } catch (...) { | 94 | | // Save the exception for next call. | 95 | 0 | _eptr = std::current_exception(); | 96 | 0 | _has_called.store(true, std::memory_order_release); | 97 | 0 | std::rethrow_exception(_eptr); | 98 | 0 | } | 99 | | // This memory order make sure both status and eptr is set | 100 | | // and will be seen in another thread. | 101 | 0 | _has_called.store(true, std::memory_order_release); | 102 | 0 | return _status; | 103 | 0 | } |
doris_callonce_test.cpp:_ZN5doris13DorisCallOnceINS_6StatusEE4callIZNS_43DorisCallOnceTest_TestExceptionHappens_Test8TestBodyEvE3$_0EES1_T_ Line | Count | Source | 75 | 1 | ReturnType call(Fn fn) { | 76 | | // Avoid lock to improve performance | 77 | 1 | if (has_called()) { | 78 | 0 | if (_eptr) { | 79 | 0 | std::rethrow_exception(_eptr); | 80 | 0 | } | 81 | 0 | return _status; | 82 | 0 | } | 83 | 1 | std::lock_guard l(_flag_lock); | 84 | | // should check again because maybe another thread call successfully. | 85 | 1 | if (has_called()) { | 86 | 0 | if (_eptr) { | 87 | 0 | std::rethrow_exception(_eptr); | 88 | 0 | } | 89 | 0 | return _status; | 90 | 0 | } | 91 | 1 | try { | 92 | 1 | _status = fn(); | 93 | 1 | } catch (...) { | 94 | | // Save the exception for next call. | 95 | 1 | _eptr = std::current_exception(); | 96 | 1 | _has_called.store(true, std::memory_order_release); | 97 | 1 | std::rethrow_exception(_eptr); | 98 | 1 | } | 99 | | // This memory order make sure both status and eptr is set | 100 | | // and will be seen in another thread. | 101 | 0 | _has_called.store(true, std::memory_order_release); | 102 | 0 | return _status; | 103 | 1 | } |
doris_callonce_test.cpp:_ZN5doris13DorisCallOnceINS_6StatusEE4callIZNS_43DorisCallOnceTest_TestExceptionHappens_Test8TestBodyEvE3$_1EES1_T_ Line | Count | Source | 75 | 1 | ReturnType call(Fn fn) { | 76 | | // Avoid lock to improve performance | 77 | 1 | if (has_called()) { | 78 | 1 | if (_eptr) { | 79 | 1 | std::rethrow_exception(_eptr); | 80 | 1 | } | 81 | 0 | return _status; | 82 | 1 | } | 83 | 0 | std::lock_guard l(_flag_lock); | 84 | | // should check again because maybe another thread call successfully. | 85 | 0 | if (has_called()) { | 86 | 0 | if (_eptr) { | 87 | 0 | std::rethrow_exception(_eptr); | 88 | 0 | } | 89 | 0 | return _status; | 90 | 0 | } | 91 | 0 | try { | 92 | 0 | _status = fn(); | 93 | 0 | } catch (...) { | 94 | | // Save the exception for next call. | 95 | 0 | _eptr = std::current_exception(); | 96 | 0 | _has_called.store(true, std::memory_order_release); | 97 | 0 | std::rethrow_exception(_eptr); | 98 | 0 | } | 99 | | // This memory order make sure both status and eptr is set | 100 | | // and will be seen in another thread. | 101 | 0 | _has_called.store(true, std::memory_order_release); | 102 | 0 | return _status; | 103 | 0 | } |
doris_callonce_test.cpp:_ZN5doris13DorisCallOnceINS_6StatusEE4callIZNS_43DorisCallOnceTest_TestExceptionHappens_Test8TestBodyEvE3$_2EES1_T_ Line | Count | Source | 75 | 1 | ReturnType call(Fn fn) { | 76 | | // Avoid lock to improve performance | 77 | 1 | if (has_called()) { | 78 | 0 | if (_eptr) { | 79 | 0 | std::rethrow_exception(_eptr); | 80 | 0 | } | 81 | 0 | return _status; | 82 | 0 | } | 83 | 1 | std::lock_guard l(_flag_lock); | 84 | | // should check again because maybe another thread call successfully. | 85 | 1 | if (has_called()) { | 86 | 0 | if (_eptr) { | 87 | 0 | std::rethrow_exception(_eptr); | 88 | 0 | } | 89 | 0 | return _status; | 90 | 0 | } | 91 | 1 | try { | 92 | 1 | _status = fn(); | 93 | 1 | } catch (...) { | 94 | | // Save the exception for next call. | 95 | 1 | _eptr = std::current_exception(); | 96 | 1 | _has_called.store(true, std::memory_order_release); | 97 | 1 | std::rethrow_exception(_eptr); | 98 | 1 | } | 99 | | // This memory order make sure both status and eptr is set | 100 | | // and will be seen in another thread. | 101 | 0 | _has_called.store(true, std::memory_order_release); | 102 | 0 | return _status; | 103 | 1 | } |
doris_callonce_test.cpp:_ZN5doris13DorisCallOnceINS_6StatusEE4callIZNS_43DorisCallOnceTest_TestExceptionHappens_Test8TestBodyEvE3$_3EES1_T_ Line | Count | Source | 75 | 1 | ReturnType call(Fn fn) { | 76 | | // Avoid lock to improve performance | 77 | 1 | if (has_called()) { | 78 | 0 | if (_eptr) { | 79 | 0 | std::rethrow_exception(_eptr); | 80 | 0 | } | 81 | 0 | return _status; | 82 | 0 | } | 83 | 1 | std::lock_guard l(_flag_lock); | 84 | | // should check again because maybe another thread call successfully. | 85 | 1 | if (has_called()) { | 86 | 0 | if (_eptr) { | 87 | 0 | std::rethrow_exception(_eptr); | 88 | 0 | } | 89 | 0 | return _status; | 90 | 0 | } | 91 | 1 | try { | 92 | 1 | _status = fn(); | 93 | 1 | } catch (...) { | 94 | | // Save the exception for next call. | 95 | 1 | _eptr = std::current_exception(); | 96 | 1 | _has_called.store(true, std::memory_order_release); | 97 | 1 | std::rethrow_exception(_eptr); | 98 | 1 | } | 99 | | // This memory order make sure both status and eptr is set | 100 | | // and will be seen in another thread. | 101 | 0 | _has_called.store(true, std::memory_order_release); | 102 | 0 | return _status; | 103 | 1 | } |
beta_rowset_reader.cpp:_ZN5doris13DorisCallOnceINS_6StatusEE4callIZNS_16BetaRowsetReader19_init_iterator_onceEvE3$_0EES1_T_ Line | Count | Source | 75 | 6.74k | ReturnType call(Fn fn) { | 76 | | // Avoid lock to improve performance | 77 | 6.74k | if (has_called()) { | 78 | 6.49k | if (_eptr) { | 79 | 0 | std::rethrow_exception(_eptr); | 80 | 0 | } | 81 | 6.49k | return _status; | 82 | 6.49k | } | 83 | 247 | std::lock_guard l(_flag_lock); | 84 | | // should check again because maybe another thread call successfully. | 85 | 247 | if (has_called()) { | 86 | 0 | if (_eptr) { | 87 | 0 | std::rethrow_exception(_eptr); | 88 | 0 | } | 89 | 0 | return _status; | 90 | 0 | } | 91 | 247 | try { | 92 | 247 | _status = fn(); | 93 | 247 | } catch (...) { | 94 | | // Save the exception for next call. | 95 | 0 | _eptr = std::current_exception(); | 96 | 0 | _has_called.store(true, std::memory_order_release); | 97 | 0 | std::rethrow_exception(_eptr); | 98 | 0 | } | 99 | | // This memory order make sure both status and eptr is set | 100 | | // and will be seen in another thread. | 101 | 247 | _has_called.store(true, std::memory_order_release); | 102 | 247 | return _status; | 103 | 247 | } |
bitmap_index_reader.cpp:_ZN5doris13DorisCallOnceINS_6StatusEE4callIZNS_10segment_v217BitmapIndexReader4loadEbbE3$_0EES1_T_ Line | Count | Source | 75 | 4 | ReturnType call(Fn fn) { | 76 | | // Avoid lock to improve performance | 77 | 4 | if (has_called()) { | 78 | 0 | if (_eptr) { | 79 | 0 | std::rethrow_exception(_eptr); | 80 | 0 | } | 81 | 0 | return _status; | 82 | 0 | } | 83 | 4 | std::lock_guard l(_flag_lock); | 84 | | // should check again because maybe another thread call successfully. | 85 | 4 | if (has_called()) { | 86 | 0 | if (_eptr) { | 87 | 0 | std::rethrow_exception(_eptr); | 88 | 0 | } | 89 | 0 | return _status; | 90 | 0 | } | 91 | 4 | try { | 92 | 4 | _status = fn(); | 93 | 4 | } catch (...) { | 94 | | // Save the exception for next call. | 95 | 0 | _eptr = std::current_exception(); | 96 | 0 | _has_called.store(true, std::memory_order_release); | 97 | 0 | std::rethrow_exception(_eptr); | 98 | 0 | } | 99 | | // This memory order make sure both status and eptr is set | 100 | | // and will be seen in another thread. | 101 | 4 | _has_called.store(true, std::memory_order_release); | 102 | 4 | return _status; | 103 | 4 | } |
bloom_filter_index_reader.cpp:_ZN5doris13DorisCallOnceINS_6StatusEE4callIZNS_10segment_v222BloomFilterIndexReader4loadEbbPNS_20OlapReaderStatisticsEE3$_0EES1_T_ Line | Count | Source | 75 | 16 | ReturnType call(Fn fn) { | 76 | | // Avoid lock to improve performance | 77 | 16 | if (has_called()) { | 78 | 0 | if (_eptr) { | 79 | 0 | std::rethrow_exception(_eptr); | 80 | 0 | } | 81 | 0 | return _status; | 82 | 0 | } | 83 | 16 | std::lock_guard l(_flag_lock); | 84 | | // should check again because maybe another thread call successfully. | 85 | 16 | if (has_called()) { | 86 | 0 | if (_eptr) { | 87 | 0 | std::rethrow_exception(_eptr); | 88 | 0 | } | 89 | 0 | return _status; | 90 | 0 | } | 91 | 16 | try { | 92 | 16 | _status = fn(); | 93 | 16 | } catch (...) { | 94 | | // Save the exception for next call. | 95 | 0 | _eptr = std::current_exception(); | 96 | 0 | _has_called.store(true, std::memory_order_release); | 97 | 0 | std::rethrow_exception(_eptr); | 98 | 0 | } | 99 | | // This memory order make sure both status and eptr is set | 100 | | // and will be seen in another thread. | 101 | 16 | _has_called.store(true, std::memory_order_release); | 102 | 16 | return _status; | 103 | 16 | } |
ordinal_page_index.cpp:_ZN5doris13DorisCallOnceINS_6StatusEE4callIZNS_10segment_v218OrdinalIndexReader4loadEbbE3$_0EES1_T_ Line | Count | Source | 75 | 10.1k | ReturnType call(Fn fn) { | 76 | | // Avoid lock to improve performance | 77 | 10.1k | if (has_called()) { | 78 | 111 | if (_eptr) { | 79 | 0 | std::rethrow_exception(_eptr); | 80 | 0 | } | 81 | 111 | return _status; | 82 | 111 | } | 83 | 9.98k | std::lock_guard l(_flag_lock); | 84 | | // should check again because maybe another thread call successfully. | 85 | 9.98k | if (has_called()) { | 86 | 0 | if (_eptr) { | 87 | 0 | std::rethrow_exception(_eptr); | 88 | 0 | } | 89 | 0 | return _status; | 90 | 0 | } | 91 | 9.98k | try { | 92 | 9.98k | _status = fn(); | 93 | 9.98k | } catch (...) { | 94 | | // Save the exception for next call. | 95 | 0 | _eptr = std::current_exception(); | 96 | 0 | _has_called.store(true, std::memory_order_release); | 97 | 0 | std::rethrow_exception(_eptr); | 98 | 0 | } | 99 | | // This memory order make sure both status and eptr is set | 100 | | // and will be seen in another thread. | 101 | 9.98k | _has_called.store(true, std::memory_order_release); | 102 | 9.98k | return _status; | 103 | 9.98k | } |
segment.cpp:_ZN5doris13DorisCallOnceINS_6StatusEE4callIZNS_10segment_v27Segment21_load_pk_bloom_filterEvE3$_0EES1_T_ Line | Count | Source | 75 | 2 | ReturnType call(Fn fn) { | 76 | | // Avoid lock to improve performance | 77 | 2 | if (has_called()) { | 78 | 1 | if (_eptr) { | 79 | 0 | std::rethrow_exception(_eptr); | 80 | 0 | } | 81 | 1 | return _status; | 82 | 1 | } | 83 | 1 | std::lock_guard l(_flag_lock); | 84 | | // should check again because maybe another thread call successfully. | 85 | 1 | if (has_called()) { | 86 | 0 | if (_eptr) { | 87 | 0 | std::rethrow_exception(_eptr); | 88 | 0 | } | 89 | 0 | return _status; | 90 | 0 | } | 91 | 1 | try { | 92 | 1 | _status = fn(); | 93 | 1 | } catch (...) { | 94 | | // Save the exception for next call. | 95 | 0 | _eptr = std::current_exception(); | 96 | 0 | _has_called.store(true, std::memory_order_release); | 97 | 0 | std::rethrow_exception(_eptr); | 98 | 0 | } | 99 | | // This memory order make sure both status and eptr is set | 100 | | // and will be seen in another thread. | 101 | 1 | _has_called.store(true, std::memory_order_release); | 102 | 1 | return _status; | 103 | 1 | } |
segment.cpp:_ZN5doris13DorisCallOnceINS_6StatusEE4callIZNS_10segment_v27Segment21_load_pk_bloom_filterEvE3$_1EES1_T_ Line | Count | Source | 75 | 4 | ReturnType call(Fn fn) { | 76 | | // Avoid lock to improve performance | 77 | 4 | if (has_called()) { | 78 | 2 | if (_eptr) { | 79 | 0 | std::rethrow_exception(_eptr); | 80 | 0 | } | 81 | 2 | return _status; | 82 | 2 | } | 83 | 2 | std::lock_guard l(_flag_lock); | 84 | | // should check again because maybe another thread call successfully. | 85 | 2 | if (has_called()) { | 86 | 0 | if (_eptr) { | 87 | 0 | std::rethrow_exception(_eptr); | 88 | 0 | } | 89 | 0 | return _status; | 90 | 0 | } | 91 | 2 | try { | 92 | 2 | _status = fn(); | 93 | 2 | } catch (...) { | 94 | | // Save the exception for next call. | 95 | 0 | _eptr = std::current_exception(); | 96 | 0 | _has_called.store(true, std::memory_order_release); | 97 | 0 | std::rethrow_exception(_eptr); | 98 | 0 | } | 99 | | // This memory order make sure both status and eptr is set | 100 | | // and will be seen in another thread. | 101 | 2 | _has_called.store(true, std::memory_order_release); | 102 | 2 | return _status; | 103 | 2 | } |
segment.cpp:_ZN5doris13DorisCallOnceINS_6StatusEE4callIZNS_10segment_v27Segment10load_indexEvE3$_0EES1_T_ Line | Count | Source | 75 | 5.00k | ReturnType call(Fn fn) { | 76 | | // Avoid lock to improve performance | 77 | 5.00k | if (has_called()) { | 78 | 62 | if (_eptr) { | 79 | 0 | std::rethrow_exception(_eptr); | 80 | 0 | } | 81 | 62 | return _status; | 82 | 62 | } | 83 | 4.94k | std::lock_guard l(_flag_lock); | 84 | | // should check again because maybe another thread call successfully. | 85 | 4.94k | if (has_called()) { | 86 | 0 | if (_eptr) { | 87 | 0 | std::rethrow_exception(_eptr); | 88 | 0 | } | 89 | 0 | return _status; | 90 | 0 | } | 91 | 4.94k | try { | 92 | 4.94k | _status = fn(); | 93 | 4.94k | } catch (...) { | 94 | | // Save the exception for next call. | 95 | 0 | _eptr = std::current_exception(); | 96 | 0 | _has_called.store(true, std::memory_order_release); | 97 | 0 | std::rethrow_exception(_eptr); | 98 | 0 | } | 99 | | // This memory order make sure both status and eptr is set | 100 | | // and will be seen in another thread. | 101 | 4.94k | _has_called.store(true, std::memory_order_release); | 102 | 4.94k | return _status; | 103 | 4.94k | } |
segment.cpp:_ZN5doris13DorisCallOnceINS_6StatusEE4callIZNS_10segment_v27Segment27_create_column_readers_onceEvE3$_0EES1_T_ Line | Count | Source | 75 | 34.9k | ReturnType call(Fn fn) { | 76 | | // Avoid lock to improve performance | 77 | 34.9k | if (has_called()) { | 78 | 30.0k | if (_eptr) { | 79 | 0 | std::rethrow_exception(_eptr); | 80 | 0 | } | 81 | 30.0k | return _status; | 82 | 30.0k | } | 83 | 4.89k | std::lock_guard l(_flag_lock); | 84 | | // should check again because maybe another thread call successfully. | 85 | 4.89k | if (has_called()) { | 86 | 0 | if (_eptr) { | 87 | 0 | std::rethrow_exception(_eptr); | 88 | 0 | } | 89 | 0 | return _status; | 90 | 0 | } | 91 | 4.89k | try { | 92 | 4.89k | _status = fn(); | 93 | 4.89k | } catch (...) { | 94 | | // Save the exception for next call. | 95 | 0 | _eptr = std::current_exception(); | 96 | 0 | _has_called.store(true, std::memory_order_release); | 97 | 0 | std::rethrow_exception(_eptr); | 98 | 0 | } | 99 | | // This memory order make sure both status and eptr is set | 100 | | // and will be seen in another thread. | 101 | 4.89k | _has_called.store(true, std::memory_order_release); | 102 | 4.89k | return _status; | 103 | 4.89k | } |
segment.cpp:_ZN5doris13DorisCallOnceINS_6StatusEE4callIZNS_10segment_v27Segment27new_inverted_index_iteratorERKNS_12TabletColumnEPKNS_11TabletIndexERKNS_18StorageReadOptionsEPSt10unique_ptrINS4_21InvertedIndexIteratorESt14default_deleteISG_EEE3$_0EES1_T_ Line | Count | Source | 75 | 74 | ReturnType call(Fn fn) { | 76 | | // Avoid lock to improve performance | 77 | 74 | if (has_called()) { | 78 | 0 | if (_eptr) { | 79 | 0 | std::rethrow_exception(_eptr); | 80 | 0 | } | 81 | 0 | return _status; | 82 | 0 | } | 83 | 74 | std::lock_guard l(_flag_lock); | 84 | | // should check again because maybe another thread call successfully. | 85 | 74 | if (has_called()) { | 86 | 0 | if (_eptr) { | 87 | 0 | std::rethrow_exception(_eptr); | 88 | 0 | } | 89 | 0 | return _status; | 90 | 0 | } | 91 | 74 | try { | 92 | 74 | _status = fn(); | 93 | 74 | } catch (...) { | 94 | | // Save the exception for next call. | 95 | 0 | _eptr = std::current_exception(); | 96 | 0 | _has_called.store(true, std::memory_order_release); | 97 | 0 | std::rethrow_exception(_eptr); | 98 | 0 | } | 99 | | // This memory order make sure both status and eptr is set | 100 | | // and will be seen in another thread. | 101 | 74 | _has_called.store(true, std::memory_order_release); | 102 | 74 | return _status; | 103 | 74 | } |
zone_map_index.cpp:_ZN5doris13DorisCallOnceINS_6StatusEE4callIZNS_10segment_v218ZoneMapIndexReader4loadEbbE3$_0EES1_T_ Line | Count | Source | 75 | 4 | ReturnType call(Fn fn) { | 76 | | // Avoid lock to improve performance | 77 | 4 | if (has_called()) { | 78 | 0 | if (_eptr) { | 79 | 0 | std::rethrow_exception(_eptr); | 80 | 0 | } | 81 | 0 | return _status; | 82 | 0 | } | 83 | 4 | std::lock_guard l(_flag_lock); | 84 | | // should check again because maybe another thread call successfully. | 85 | 4 | if (has_called()) { | 86 | 0 | if (_eptr) { | 87 | 0 | std::rethrow_exception(_eptr); | 88 | 0 | } | 89 | 0 | return _status; | 90 | 0 | } | 91 | 4 | try { | 92 | 4 | _status = fn(); | 93 | 4 | } catch (...) { | 94 | | // Save the exception for next call. | 95 | 0 | _eptr = std::current_exception(); | 96 | 0 | _has_called.store(true, std::memory_order_release); | 97 | 0 | std::rethrow_exception(_eptr); | 98 | 0 | } | 99 | | // This memory order make sure both status and eptr is set | 100 | | // and will be seen in another thread. | 101 | 4 | _has_called.store(true, std::memory_order_release); | 102 | 4 | return _status; | 103 | 4 | } |
tablet.cpp:_ZN5doris13DorisCallOnceINS_6StatusEE4callIZNS_6Tablet4initEvE3$_0EES1_T_ Line | Count | Source | 75 | 259 | ReturnType call(Fn fn) { | 76 | | // Avoid lock to improve performance | 77 | 259 | if (has_called()) { | 78 | 21 | if (_eptr) { | 79 | 0 | std::rethrow_exception(_eptr); | 80 | 0 | } | 81 | 21 | return _status; | 82 | 21 | } | 83 | 238 | std::lock_guard l(_flag_lock); | 84 | | // should check again because maybe another thread call successfully. | 85 | 238 | if (has_called()) { | 86 | 0 | if (_eptr) { | 87 | 0 | std::rethrow_exception(_eptr); | 88 | 0 | } | 89 | 0 | return _status; | 90 | 0 | } | 91 | 238 | try { | 92 | 238 | _status = fn(); | 93 | 238 | } catch (...) { | 94 | | // Save the exception for next call. | 95 | 0 | _eptr = std::current_exception(); | 96 | 0 | _has_called.store(true, std::memory_order_release); | 97 | 0 | std::rethrow_exception(_eptr); | 98 | 0 | } | 99 | | // This memory order make sure both status and eptr is set | 100 | | // and will be seen in another thread. | 101 | 238 | _has_called.store(true, std::memory_order_release); | 102 | 238 | return _status; | 103 | 238 | } |
Unexecuted instantiation: load_path_mgr.cpp:_ZN5doris13DorisCallOnceINS_6StatusEE4callIZNS_11LoadPathMgr12allocate_dirERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEESC_PSA_E3$_0EES1_T_ |
104 | | |
105 | | // Has to pay attention to memory order |
106 | | // see https://en.cppreference.com/w/cpp/atomic/memory_order |
107 | | // Return whether `call` has been invoked or not. |
108 | 77.8k | bool has_called() const { |
109 | | // std::memory_order_acquire here and std::memory_order_release in |
110 | | // init(), taken together, mean that threads can safely synchronize on |
111 | | // _has_called. |
112 | 77.8k | return _has_called.load(std::memory_order_acquire); |
113 | 77.8k | } |
114 | | |
115 | | // Return the stored result. The result is only meaningful when `has_called() == true`. |
116 | 125 | ReturnType stored_result() const { |
117 | 125 | if (!has_called()) { |
118 | | // Could not return status if the method not called. |
119 | 0 | throw doris::Exception(doris::ErrorCode::INTERNAL_ERROR, |
120 | 0 | "calling stored_result while has not been called"); |
121 | 0 | } |
122 | 125 | if (_eptr) { |
123 | 1 | std::rethrow_exception(_eptr); |
124 | 1 | } |
125 | 124 | return _status; |
126 | 125 | } |
127 | | |
128 | | private: |
129 | | std::atomic<bool> _has_called; |
130 | | // std::once_flag _once_flag; |
131 | | std::mutex _flag_lock; |
132 | | std::exception_ptr _eptr; |
133 | | ReturnType _status; |
134 | | }; |
135 | | |
136 | | } // namespace doris |