Skip to content

Commit da4e33a

Browse files
authored
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
1 parent 2447f18 commit da4e33a

3 files changed

Lines changed: 60 additions & 109 deletions

File tree

src/butil/containers/doubly_buffered_data.h

Lines changed: 33 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -123,83 +123,26 @@ class DoublyBufferedData {
123123
// Returns 0 on success, -1 otherwise.
124124
int Read(ScopedPtr* ptr);
125125

126+
// `fn(const T&)' will be called with foreground instance.
127+
// This function is not blocked by Read() and Modify() in other threads.
128+
// Returns 0 on success, otherwise on error.
129+
template<typename Fn>
130+
int Read(Fn&& fn);
131+
126132
// Modify background and foreground instances. fn(T&, ...) will be called
127133
// twice. Modify() from different threads are exclusive from each other.
128134
// NOTE: Call same series of fn to different equivalent instances should
129135
// result in equivalent instances, otherwise foreground and background
130136
// instance will be inconsistent.
131-
template <typename Fn> size_t Modify(Fn& fn);
132-
template <typename Fn, typename Arg1> size_t Modify(Fn& fn, const Arg1&);
133-
template <typename Fn, typename Arg1, typename Arg2>
134-
size_t Modify(Fn& fn, const Arg1&, const Arg2&);
137+
template <typename Fn, typename... Args>
138+
size_t Modify(Fn&& fn, Args&&... args);
135139

136140
// fn(T& background, const T& foreground, ...) will be called to background
137141
// and foreground instances respectively.
138-
template <typename Fn> size_t ModifyWithForeground(Fn& fn);
139-
template <typename Fn, typename Arg1>
140-
size_t ModifyWithForeground(Fn& fn, const Arg1&);
141-
template <typename Fn, typename Arg1, typename Arg2>
142-
size_t ModifyWithForeground(Fn& fn, const Arg1&, const Arg2&);
142+
template <typename Fn, typename... Args>
143+
size_t ModifyWithForeground(Fn&& fn, Args&&... args);
143144

144145
private:
145-
template <typename Fn>
146-
struct WithFG0 {
147-
WithFG0(Fn& fn, T* data) : _fn(fn), _data(data) { }
148-
size_t operator()(T& bg) {
149-
return _fn(bg, (const T&)_data[&bg == _data]);
150-
}
151-
private:
152-
Fn& _fn;
153-
T* _data;
154-
};
155-
156-
template <typename Fn, typename Arg1>
157-
struct WithFG1 {
158-
WithFG1(Fn& fn, T* data, const Arg1& arg1)
159-
: _fn(fn), _data(data), _arg1(arg1) {}
160-
size_t operator()(T& bg) {
161-
return _fn(bg, (const T&)_data[&bg == _data], _arg1);
162-
}
163-
private:
164-
Fn& _fn;
165-
T* _data;
166-
const Arg1& _arg1;
167-
};
168-
169-
template <typename Fn, typename Arg1, typename Arg2>
170-
struct WithFG2 {
171-
WithFG2(Fn& fn, T* data, const Arg1& arg1, const Arg2& arg2)
172-
: _fn(fn), _data(data), _arg1(arg1), _arg2(arg2) {}
173-
size_t operator()(T& bg) {
174-
return _fn(bg, (const T&)_data[&bg == _data], _arg1, _arg2);
175-
}
176-
private:
177-
Fn& _fn;
178-
T* _data;
179-
const Arg1& _arg1;
180-
const Arg2& _arg2;
181-
};
182-
183-
template <typename Fn, typename Arg1>
184-
struct Closure1 {
185-
Closure1(Fn& fn, const Arg1& arg1) : _fn(fn), _arg1(arg1) {}
186-
size_t operator()(T& bg) { return _fn(bg, _arg1); }
187-
private:
188-
Fn& _fn;
189-
const Arg1& _arg1;
190-
};
191-
192-
template <typename Fn, typename Arg1, typename Arg2>
193-
struct Closure2 {
194-
Closure2(Fn& fn, const Arg1& arg1, const Arg2& arg2)
195-
: _fn(fn), _arg1(arg1), _arg2(arg2) {}
196-
size_t operator()(T& bg) { return _fn(bg, _arg1, _arg2); }
197-
private:
198-
Fn& _fn;
199-
const Arg1& _arg1;
200-
const Arg2& _arg2;
201-
};
202-
203146
const T* UnsafeRead() const {
204147
return _data + _index.load(butil::memory_order_acquire);
205148
}
@@ -253,7 +196,8 @@ template <typename T, typename TLS, bool AllowBthreadSuspended>
253196
class DoublyBufferedData<T, TLS, AllowBthreadSuspended>::WrapperTLSGroup {
254197
public:
255198
const static size_t RAW_BLOCK_SIZE = 4096;
256-
const static size_t ELEMENTS_PER_BLOCK = RAW_BLOCK_SIZE / sizeof(Wrapper) > 0 ? RAW_BLOCK_SIZE / sizeof(Wrapper) : 1;
199+
const static size_t ELEMENTS_PER_BLOCK =
200+
RAW_BLOCK_SIZE / sizeof(Wrapper) > 0 ? RAW_BLOCK_SIZE / sizeof(Wrapper) : 1;
257201

258202
struct BAIDU_CACHELINE_ALIGNMENT ThreadBlock {
259203
inline DoublyBufferedData::Wrapper* at(size_t offset) {
@@ -572,7 +516,20 @@ int DoublyBufferedData<T, TLS, AllowBthreadSuspended>::Read(
572516

573517
template <typename T, typename TLS, bool AllowBthreadSuspended>
574518
template <typename Fn>
575-
size_t DoublyBufferedData<T, TLS, AllowBthreadSuspended>::Modify(Fn& fn) {
519+
int DoublyBufferedData<T, TLS, AllowBthreadSuspended>::Read(Fn&& fn) {
520+
BAIDU_CASSERT((is_result_void<Fn, const T&>::value),
521+
"Fn must accept `const T&' and return void");
522+
ScopedPtr ptr;
523+
if (Read(&ptr) != 0) {
524+
return -1;
525+
}
526+
fn(*ptr);
527+
return 0;
528+
}
529+
530+
template <typename T, typename TLS, bool AllowBthreadSuspended>
531+
template <typename Fn, typename... Args>
532+
size_t DoublyBufferedData<T, TLS, AllowBthreadSuspended>::Modify(Fn&& fn, Args&&... args) {
576533
// _modify_mutex sequences modifications. Using a separate mutex rather
577534
// than _wrappers_mutex is to avoid blocking threads calling
578535
// AddWrapper() or RemoveWrapper() too long. Most of the time, modifications
@@ -581,7 +538,7 @@ size_t DoublyBufferedData<T, TLS, AllowBthreadSuspended>::Modify(Fn& fn) {
581538
int bg_index = !_index.load(butil::memory_order_relaxed);
582539
// background instance is not accessed by other threads, being safe to
583540
// modify.
584-
const size_t ret = fn(_data[bg_index]);
541+
const size_t ret = fn(_data[bg_index], std::forward<Args>(args)...);
585542
if (!ret) {
586543
return 0;
587544
}
@@ -607,46 +564,17 @@ size_t DoublyBufferedData<T, TLS, AllowBthreadSuspended>::Modify(Fn& fn) {
607564
}
608565
}
609566

610-
const size_t ret2 = fn(_data[bg_index]);
567+
const size_t ret2 = fn(_data[bg_index], std::forward<Args>(args)...);
611568
CHECK_EQ(ret2, ret) << "index=" << _index.load(butil::memory_order_relaxed);
612569
return ret2;
613570
}
614571

615572
template <typename T, typename TLS, bool AllowBthreadSuspended>
616-
template <typename Fn, typename Arg1>
617-
size_t DoublyBufferedData<T, TLS, AllowBthreadSuspended>::Modify(Fn& fn, const Arg1& arg1) {
618-
Closure1<Fn, Arg1> c(fn, arg1);
619-
return Modify(c);
620-
}
621-
622-
template <typename T, typename TLS, bool AllowBthreadSuspended>
623-
template <typename Fn, typename Arg1, typename Arg2>
624-
size_t DoublyBufferedData<T, TLS, AllowBthreadSuspended>::Modify(
625-
Fn& fn, const Arg1& arg1, const Arg2& arg2) {
626-
Closure2<Fn, Arg1, Arg2> c(fn, arg1, arg2);
627-
return Modify(c);
628-
}
629-
630-
template <typename T, typename TLS, bool AllowBthreadSuspended>
631-
template <typename Fn>
632-
size_t DoublyBufferedData<T, TLS, AllowBthreadSuspended>::ModifyWithForeground(Fn& fn) {
633-
WithFG0<Fn> c(fn, _data);
634-
return Modify(c);
635-
}
636-
637-
template <typename T, typename TLS, bool AllowBthreadSuspended>
638-
template <typename Fn, typename Arg1>
639-
size_t DoublyBufferedData<T, TLS, AllowBthreadSuspended>::ModifyWithForeground(Fn& fn, const Arg1& arg1) {
640-
WithFG1<Fn, Arg1> c(fn, _data, arg1);
641-
return Modify(c);
642-
}
643-
644-
template <typename T, typename TLS, bool AllowBthreadSuspended>
645-
template <typename Fn, typename Arg1, typename Arg2>
646-
size_t DoublyBufferedData<T, TLS, AllowBthreadSuspended>::ModifyWithForeground(
647-
Fn& fn, const Arg1& arg1, const Arg2& arg2) {
648-
WithFG2<Fn, Arg1, Arg2> c(fn, _data, arg1, arg2);
649-
return Modify(c);
573+
template <typename Fn, typename... Args>
574+
size_t DoublyBufferedData<T, TLS, AllowBthreadSuspended>::ModifyWithForeground(Fn&& fn, Args&&... args) {
575+
return Modify([this, &fn](T& bg, Args&&... args) {
576+
return fn(bg, (const T&)_data[&bg == _data], std::forward<Args>(args)...);
577+
}, std::forward<Args>(args)...);
650578
}
651579

652580
} // namespace butil

src/butil/type_traits.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ template <class T> struct is_pod
9999
std::is_trivial<T>::value)> {};
100100
#else
101101
template <class T> struct is_pod : std::is_pod<T> {};
102-
#endif
102+
#endif // __cplusplus
103103

104104
#else
105105
// We can't get is_pod right without compiler help, so fail conservatively.
@@ -334,7 +334,7 @@ template<typename T>
334334
struct remove_cvref {
335335
typedef typename remove_cv<typename remove_reference<T>::type>::type type;
336336
};
337-
#endif
337+
#endif // __cplusplus
338338

339339
// is_reference is false except for reference types.
340340
template<typename T> struct is_reference : false_type {};
@@ -378,7 +378,7 @@ template <typename F>
378378
using result_of = std::result_of<F>;
379379
#else
380380
#error Only C++11 or later is supported.
381-
#endif
381+
#endif // __cplusplus
382382

383383
template <typename F>
384384
using result_of_t = typename result_of<F>::type;

test/brpc_load_balancer_unittest.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,16 @@ bool AddN(Foo& f, int n) {
8080
return true;
8181
}
8282

83+
void read_cb(const Foo& f) {
84+
ASSERT_EQ(0, f.x);
85+
}
86+
87+
struct CallableObj {
88+
void operator()(const Foo& f) {
89+
ASSERT_EQ(0, f.x);
90+
}
91+
};
92+
8393
template <typename DBD>
8494
void test_doubly_buffered_data() {
8595
// test doubly_buffered_data TLS limits
@@ -97,17 +107,30 @@ void test_doubly_buffered_data() {
97107
ASSERT_EQ(0, d.Read(&ptr));
98108
ASSERT_EQ(0, ptr->x);
99109
}
110+
{
111+
ASSERT_EQ(0, d.Read([](const Foo& f) {
112+
ASSERT_EQ(0, f.x);
113+
}));
114+
ASSERT_EQ(0, d.Read(read_cb));
115+
ASSERT_EQ(0, d.Read(CallableObj()));
116+
CallableObj co;
117+
ASSERT_EQ(0, d.Read(co));
118+
}
100119
{
101120
typename DBD::ScopedPtr ptr;
102121
ASSERT_EQ(0, d.Read(&ptr));
103122
ASSERT_EQ(0, ptr->x);
104123
}
105124

106125
d.Modify(AddN, 10);
126+
d.Modify([](Foo& f, int n) -> size_t {
127+
f.x += n;
128+
return 1;
129+
}, 10);
107130
{
108131
typename DBD::ScopedPtr ptr;
109132
ASSERT_EQ(0, d.Read(&ptr));
110-
ASSERT_EQ(10, ptr->x);
133+
ASSERT_EQ(20, ptr->x);
111134
}
112135
}
113136

0 commit comments

Comments
 (0)