This is an automated email from the ASF dual-hosted git repository. guangmingchen pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/brpc.git
The following commit(s) were added to refs/heads/master by this push: new da4e33a6 Support a more safe read function and variadic functions of DoublyBufferedData (#2898) da4e33a6 is described below commit da4e33a6ee44d107bc0dd9632b2db289aa815cdd Author: Bright Chen <chenguangmin...@foxmail.com> AuthorDate: Fri Mar 21 23:42:43 2025 +0800 Support a more safe read function and variadic functions of DoublyBufferedData (#2898) * Add a convenient and safe DoublyBufferedData::Read * Optimize and add a UT * Support variadic functions of DoublyBufferedData * Fix Fn copy --- src/butil/containers/doubly_buffered_data.h | 138 +++++++--------------------- src/butil/type_traits.h | 6 +- test/brpc_load_balancer_unittest.cpp | 25 ++++- 3 files changed, 60 insertions(+), 109 deletions(-) diff --git a/src/butil/containers/doubly_buffered_data.h b/src/butil/containers/doubly_buffered_data.h index 62f67ead..5c97038b 100644 --- a/src/butil/containers/doubly_buffered_data.h +++ b/src/butil/containers/doubly_buffered_data.h @@ -123,83 +123,26 @@ public: // Returns 0 on success, -1 otherwise. int Read(ScopedPtr* ptr); + // `fn(const T&)' will be called with foreground instance. + // This function is not blocked by Read() and Modify() in other threads. + // Returns 0 on success, otherwise on error. + template<typename Fn> + int Read(Fn&& fn); + // Modify background and foreground instances. fn(T&, ...) will be called // twice. Modify() from different threads are exclusive from each other. // NOTE: Call same series of fn to different equivalent instances should // result in equivalent instances, otherwise foreground and background // instance will be inconsistent. - template <typename Fn> size_t Modify(Fn& fn); - template <typename Fn, typename Arg1> size_t Modify(Fn& fn, const Arg1&); - template <typename Fn, typename Arg1, typename Arg2> - size_t Modify(Fn& fn, const Arg1&, const Arg2&); + template <typename Fn, typename... Args> + size_t Modify(Fn&& fn, Args&&... args); // fn(T& background, const T& foreground, ...) will be called to background // and foreground instances respectively. - template <typename Fn> size_t ModifyWithForeground(Fn& fn); - template <typename Fn, typename Arg1> - size_t ModifyWithForeground(Fn& fn, const Arg1&); - template <typename Fn, typename Arg1, typename Arg2> - size_t ModifyWithForeground(Fn& fn, const Arg1&, const Arg2&); + template <typename Fn, typename... Args> + size_t ModifyWithForeground(Fn&& fn, Args&&... args); private: - template <typename Fn> - struct WithFG0 { - WithFG0(Fn& fn, T* data) : _fn(fn), _data(data) { } - size_t operator()(T& bg) { - return _fn(bg, (const T&)_data[&bg == _data]); - } - private: - Fn& _fn; - T* _data; - }; - - template <typename Fn, typename Arg1> - struct WithFG1 { - WithFG1(Fn& fn, T* data, const Arg1& arg1) - : _fn(fn), _data(data), _arg1(arg1) {} - size_t operator()(T& bg) { - return _fn(bg, (const T&)_data[&bg == _data], _arg1); - } - private: - Fn& _fn; - T* _data; - const Arg1& _arg1; - }; - - template <typename Fn, typename Arg1, typename Arg2> - struct WithFG2 { - WithFG2(Fn& fn, T* data, const Arg1& arg1, const Arg2& arg2) - : _fn(fn), _data(data), _arg1(arg1), _arg2(arg2) {} - size_t operator()(T& bg) { - return _fn(bg, (const T&)_data[&bg == _data], _arg1, _arg2); - } - private: - Fn& _fn; - T* _data; - const Arg1& _arg1; - const Arg2& _arg2; - }; - - template <typename Fn, typename Arg1> - struct Closure1 { - Closure1(Fn& fn, const Arg1& arg1) : _fn(fn), _arg1(arg1) {} - size_t operator()(T& bg) { return _fn(bg, _arg1); } - private: - Fn& _fn; - const Arg1& _arg1; - }; - - template <typename Fn, typename Arg1, typename Arg2> - struct Closure2 { - Closure2(Fn& fn, const Arg1& arg1, const Arg2& arg2) - : _fn(fn), _arg1(arg1), _arg2(arg2) {} - size_t operator()(T& bg) { return _fn(bg, _arg1, _arg2); } - private: - Fn& _fn; - const Arg1& _arg1; - const Arg2& _arg2; - }; - const T* UnsafeRead() const { return _data + _index.load(butil::memory_order_acquire); } @@ -253,7 +196,8 @@ template <typename T, typename TLS, bool AllowBthreadSuspended> class DoublyBufferedData<T, TLS, AllowBthreadSuspended>::WrapperTLSGroup { public: const static size_t RAW_BLOCK_SIZE = 4096; - const static size_t ELEMENTS_PER_BLOCK = RAW_BLOCK_SIZE / sizeof(Wrapper) > 0 ? RAW_BLOCK_SIZE / sizeof(Wrapper) : 1; + const static size_t ELEMENTS_PER_BLOCK = + RAW_BLOCK_SIZE / sizeof(Wrapper) > 0 ? RAW_BLOCK_SIZE / sizeof(Wrapper) : 1; struct BAIDU_CACHELINE_ALIGNMENT ThreadBlock { inline DoublyBufferedData::Wrapper* at(size_t offset) { @@ -572,7 +516,20 @@ int DoublyBufferedData<T, TLS, AllowBthreadSuspended>::Read( template <typename T, typename TLS, bool AllowBthreadSuspended> template <typename Fn> -size_t DoublyBufferedData<T, TLS, AllowBthreadSuspended>::Modify(Fn& fn) { +int DoublyBufferedData<T, TLS, AllowBthreadSuspended>::Read(Fn&& fn) { + BAIDU_CASSERT((is_result_void<Fn, const T&>::value), + "Fn must accept `const T&' and return void"); + ScopedPtr ptr; + if (Read(&ptr) != 0) { + return -1; + } + fn(*ptr); + return 0; +} + +template <typename T, typename TLS, bool AllowBthreadSuspended> +template <typename Fn, typename... Args> +size_t DoublyBufferedData<T, TLS, AllowBthreadSuspended>::Modify(Fn&& fn, Args&&... args) { // _modify_mutex sequences modifications. Using a separate mutex rather // than _wrappers_mutex is to avoid blocking threads calling // AddWrapper() or RemoveWrapper() too long. Most of the time, modifications @@ -581,7 +538,7 @@ size_t DoublyBufferedData<T, TLS, AllowBthreadSuspended>::Modify(Fn& fn) { int bg_index = !_index.load(butil::memory_order_relaxed); // background instance is not accessed by other threads, being safe to // modify. - const size_t ret = fn(_data[bg_index]); + const size_t ret = fn(_data[bg_index], std::forward<Args>(args)...); if (!ret) { return 0; } @@ -607,46 +564,17 @@ size_t DoublyBufferedData<T, TLS, AllowBthreadSuspended>::Modify(Fn& fn) { } } - const size_t ret2 = fn(_data[bg_index]); + const size_t ret2 = fn(_data[bg_index], std::forward<Args>(args)...); CHECK_EQ(ret2, ret) << "index=" << _index.load(butil::memory_order_relaxed); return ret2; } template <typename T, typename TLS, bool AllowBthreadSuspended> -template <typename Fn, typename Arg1> -size_t DoublyBufferedData<T, TLS, AllowBthreadSuspended>::Modify(Fn& fn, const Arg1& arg1) { - Closure1<Fn, Arg1> c(fn, arg1); - return Modify(c); -} - -template <typename T, typename TLS, bool AllowBthreadSuspended> -template <typename Fn, typename Arg1, typename Arg2> -size_t DoublyBufferedData<T, TLS, AllowBthreadSuspended>::Modify( - Fn& fn, const Arg1& arg1, const Arg2& arg2) { - Closure2<Fn, Arg1, Arg2> c(fn, arg1, arg2); - return Modify(c); -} - -template <typename T, typename TLS, bool AllowBthreadSuspended> -template <typename Fn> -size_t DoublyBufferedData<T, TLS, AllowBthreadSuspended>::ModifyWithForeground(Fn& fn) { - WithFG0<Fn> c(fn, _data); - return Modify(c); -} - -template <typename T, typename TLS, bool AllowBthreadSuspended> -template <typename Fn, typename Arg1> -size_t DoublyBufferedData<T, TLS, AllowBthreadSuspended>::ModifyWithForeground(Fn& fn, const Arg1& arg1) { - WithFG1<Fn, Arg1> c(fn, _data, arg1); - return Modify(c); -} - -template <typename T, typename TLS, bool AllowBthreadSuspended> -template <typename Fn, typename Arg1, typename Arg2> -size_t DoublyBufferedData<T, TLS, AllowBthreadSuspended>::ModifyWithForeground( - Fn& fn, const Arg1& arg1, const Arg2& arg2) { - WithFG2<Fn, Arg1, Arg2> c(fn, _data, arg1, arg2); - return Modify(c); +template <typename Fn, typename... Args> +size_t DoublyBufferedData<T, TLS, AllowBthreadSuspended>::ModifyWithForeground(Fn&& fn, Args&&... args) { + return Modify([this, &fn](T& bg, Args&&... args) { + return fn(bg, (const T&)_data[&bg == _data], std::forward<Args>(args)...); + }, std::forward<Args>(args)...); } } // namespace butil diff --git a/src/butil/type_traits.h b/src/butil/type_traits.h index 0ae91135..735e1e55 100644 --- a/src/butil/type_traits.h +++ b/src/butil/type_traits.h @@ -99,7 +99,7 @@ template <class T> struct is_pod std::is_trivial<T>::value)> {}; #else template <class T> struct is_pod : std::is_pod<T> {}; -#endif +#endif // __cplusplus #else // We can't get is_pod right without compiler help, so fail conservatively. @@ -334,7 +334,7 @@ template<typename T> struct remove_cvref { typedef typename remove_cv<typename remove_reference<T>::type>::type type; }; -#endif +#endif // __cplusplus // is_reference is false except for reference types. template<typename T> struct is_reference : false_type {}; @@ -378,7 +378,7 @@ template <typename F> using result_of = std::result_of<F>; #else #error Only C++11 or later is supported. -#endif +#endif // __cplusplus template <typename F> using result_of_t = typename result_of<F>::type; diff --git a/test/brpc_load_balancer_unittest.cpp b/test/brpc_load_balancer_unittest.cpp index 509456e7..2abb869e 100644 --- a/test/brpc_load_balancer_unittest.cpp +++ b/test/brpc_load_balancer_unittest.cpp @@ -80,6 +80,16 @@ bool AddN(Foo& f, int n) { return true; } +void read_cb(const Foo& f) { + ASSERT_EQ(0, f.x); +} + +struct CallableObj { + void operator()(const Foo& f) { + ASSERT_EQ(0, f.x); + } +}; + template <typename DBD> void test_doubly_buffered_data() { // test doubly_buffered_data TLS limits @@ -97,6 +107,15 @@ void test_doubly_buffered_data() { ASSERT_EQ(0, d.Read(&ptr)); ASSERT_EQ(0, ptr->x); } + { + ASSERT_EQ(0, d.Read([](const Foo& f) { + ASSERT_EQ(0, f.x); + })); + ASSERT_EQ(0, d.Read(read_cb)); + ASSERT_EQ(0, d.Read(CallableObj())); + CallableObj co; + ASSERT_EQ(0, d.Read(co)); + } { typename DBD::ScopedPtr ptr; ASSERT_EQ(0, d.Read(&ptr)); @@ -104,10 +123,14 @@ void test_doubly_buffered_data() { } d.Modify(AddN, 10); + d.Modify([](Foo& f, int n) -> size_t { + f.x += n; + return 1; + }, 10); { typename DBD::ScopedPtr ptr; ASSERT_EQ(0, d.Read(&ptr)); - ASSERT_EQ(10, ptr->x); + ASSERT_EQ(20, ptr->x); } } --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@brpc.apache.org For additional commands, e-mail: dev-h...@brpc.apache.org