@@ -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
144145private:
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>
253196class DoublyBufferedData <T, TLS , AllowBthreadSuspended>::WrapperTLSGroup {
254197public:
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
573517template <typename T, typename TLS , bool AllowBthreadSuspended>
574518template <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
615572template <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
0 commit comments