From b1ac899f23f915c19dfd3e8be9dd8b94b43ef55d Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Sun, 9 Feb 2014 03:42:39 +0100 Subject: [PATCH] Pulled with_lock_guard patch. --- doc/mutex_concepts.qbk | 38 +++ doc/sync_tutorial.qbk | 44 +++- include/boost/thread/with_lock_guard.hpp | 232 ++++++++++++++++++ test/Jamfile.v2 | 8 + .../with_lock_guard/with_lock_guard_bind.cpp | 145 +++++++++++ .../with_lock_guard_lambda.cpp | 59 +++++ .../with_lock_guard/with_lock_guard_move.cpp | 110 +++++++++ .../with_lock_guard_simple.cpp | 139 +++++++++++ 8 files changed, 763 insertions(+), 12 deletions(-) create mode 100644 include/boost/thread/with_lock_guard.hpp create mode 100644 test/sync/mutual_exclusion/with_lock_guard/with_lock_guard_bind.cpp create mode 100644 test/sync/mutual_exclusion/with_lock_guard/with_lock_guard_lambda.cpp create mode 100644 test/sync/mutual_exclusion/with_lock_guard/with_lock_guard_move.cpp create mode 100644 test/sync/mutual_exclusion/with_lock_guard/with_lock_guard_simple.cpp diff --git a/doc/mutex_concepts.qbk b/doc/mutex_concepts.qbk index 4f02d24e..78ec5884 100644 --- a/doc/mutex_concepts.qbk +++ b/doc/mutex_concepts.qbk @@ -1115,6 +1115,44 @@ object passed to the constructor.]] ] +[endsect] +[endsect] + + +[section:with_lock_guard With Lock Guard] + + // #include + + namespace boost + { + template + auto with_lock_guard(Lockable& m, Function&& func, Args&&... args) -> decltype(func(boost::forward(args)...)); + } + +[section:with_lock_guard Non Member Function `with_lock_guard`] + + template + auto with_lock_guard( + Lockable& m, + Function&& func, + Args&&... args + ) -> decltype(func(boost::forward(args)...)); + +[variablelist + +[[Precondition:] [`m` must be in unlocked state]] +[[Effects:] [call `func` in scope locked by `m`]] +[[Returns:] [Result of `func(args...)` call]] +[[Throws:] [Any exception thrown by the call to `m.lock` and `func(args...)`]] +[[Postcondition:] [`m` is in unlocked state]] + +[[Limitations:] [Without c++11 variadic templates support number of arguments is limited to `4`]] +[[] [Without rvalue references support calling class method with `boost::bind` must be const]] +[[] [For correct work with lambda macro `BOOST_RESULT_OF_USE_DECLTYPE` may be needed to define]] + + +] + [endsect] [endsect] diff --git a/doc/sync_tutorial.qbk b/doc/sync_tutorial.qbk index 5561bcb6..1ea6dbf0 100644 --- a/doc/sync_tutorial.qbk +++ b/doc/sync_tutorial.qbk @@ -20,24 +20,44 @@ In addition to the C++11 standard locks, Boost.Thread provides other locks and s [section:with Executing Around a Function] -In particular, the library provides some lock factories. +In particular, the library provides a way to lock around the execution of a function. - template - auto with_lock_guard(Lockable& m, Function f) -> decltype(f()) - { - auto&& _ = boost::make_lock_guard(m); - f(); + template + auto with_lock_guard( + Lockable& m, + Function&& func, + Args&&... args + ) -> decltype(func(boost::forward(args)...)) { + boost::lock_guard lock(m); + return func(boost::forward(args)...); } +that can be used with regular functions: -that can be used as + int func(int, int&); + //... + boost::mutex m; + int a; + int result = boost::with_lock_guard(m, func, 1, boost::ref(a)); - int i = with_lock_guard(mtx, []() - { - // access the protected state - return true; - }); +with boost::bind: + int result = boost::with_lock_guard( + m, boost::bind(func, 2, boost::ref(a)) + ); + +or with lambda expression: + + int a; + int result = boost::with_lock_guard( + m, + [&a](int x) { + // this scope is protected by mutex m + a = 3; + return x + 4; + }, + 5 + ); [endsect] [/ With] diff --git a/include/boost/thread/with_lock_guard.hpp b/include/boost/thread/with_lock_guard.hpp new file mode 100644 index 00000000..cf3fdc24 --- /dev/null +++ b/include/boost/thread/with_lock_guard.hpp @@ -0,0 +1,232 @@ +// (C) Copyright 2013 Ruslan Baratov +// Copyright (C) 2014 Vicente J. Botet Escriba +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See www.boost.org/libs/thread for documentation. + +#ifndef BOOST_THREAD_WITH_LOCK_GUARD_HPP +#define BOOST_THREAD_WITH_LOCK_GUARD_HPP + +#include +#include +//#include + +namespace boost { + +#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && \ + !defined(BOOST_NO_CXX11_DECLTYPE) + +/** + * Utility to run functions in scope protected by mutex. + * + * Examples: + * + * int func(int, int&); + * boost::mutex m; + * int a; + * int result = boost::with_lock_guard(m, func, 1, boost::ref(a)); + * + * // using boost::bind + * int result = boost::with_lock_guard( + * m, boost::bind(func, 2, boost::ref(a)) + * ); + * + * // using lambda + * int a; + * int result = boost::with_lock_guard( + * m, + * [&a](int x) { + * a = 3; + * return x + 4; + * }, + * 5 + * ); + */ +template +auto with_lock_guard( + Lockable& m, + BOOST_FWD_REF(Function) func, + BOOST_FWD_REF(Args)... args +) -> decltype(func(boost::forward(args)...)) { + boost::lock_guard lock(m); + return func(boost::forward(args)...); +} + +#else + +// Workaround versions for compilers without c++11 variadic templates support. +// (function arguments limit: 4) +// (for lambda support define BOOST_RESULT_OF_USE_DECLTYPE may be needed) + +template +typename boost::result_of::type with_lock_guard( + Lockable& m, + BOOST_FWD_REF(Func) func +) { + boost::lock_guard lock(m); + return func(); +} + +template +typename boost::result_of::type with_lock_guard( + Lockable& m, + BOOST_FWD_REF(Func) func, + BOOST_FWD_REF(Arg) arg +) { + boost::lock_guard lock(m); + return func( + boost::forward(arg) + ); +} + +template +typename boost::result_of::type with_lock_guard( + Lockable& m, + BOOST_FWD_REF(Func) func, + BOOST_FWD_REF(Arg1) arg1, + BOOST_FWD_REF(Arg2) arg2 +) { + boost::lock_guard lock(m); + return func( + boost::forward(arg1), + boost::forward(arg2) + ); +} + +template +typename boost::result_of::type with_lock_guard( + Lockable& m, + BOOST_FWD_REF(Func) func, + BOOST_FWD_REF(Arg1) arg1, + BOOST_FWD_REF(Arg2) arg2, + BOOST_FWD_REF(Arg3) arg3 +) { + boost::lock_guard lock(m); + return func( + boost::forward(arg1), + boost::forward(arg2), + boost::forward(arg3) + ); +} + +template < + class Lockable, class Func, class Arg1, class Arg2, class Arg3, class Arg4 +> +typename boost::result_of::type with_lock_guard( + Lockable& m, + BOOST_FWD_REF(Func) func, + BOOST_FWD_REF(Arg1) arg1, + BOOST_FWD_REF(Arg2) arg2, + BOOST_FWD_REF(Arg3) arg3, + BOOST_FWD_REF(Arg4) arg4 +) { + boost::lock_guard lock(m); + return func( + boost::forward(arg1), + boost::forward(arg2), + boost::forward(arg3), + boost::forward(arg4) + ); +} + +// overloads for function pointer +// (if argument is not function pointer, static assert will trigger) +template +typename boost::result_of< + typename boost::add_pointer::type() +>::type with_lock_guard( + Lockable& m, + Func* func +) { + BOOST_STATIC_ASSERT(boost::is_function::value); + + boost::lock_guard lock(m); + return func(); +} + +template +typename boost::result_of< + typename boost::add_pointer::type(Arg) +>::type with_lock_guard( + Lockable& m, + Func* func, + BOOST_FWD_REF(Arg) arg +) { + BOOST_STATIC_ASSERT(boost::is_function::value); + + boost::lock_guard lock(m); + return func( + boost::forward(arg) + ); +} + +template +typename boost::result_of< + typename boost::add_pointer::type(Arg1, Arg2) +>::type with_lock_guard( + Lockable& m, + Func* func, + BOOST_FWD_REF(Arg1) arg1, + BOOST_FWD_REF(Arg2) arg2 +) { + BOOST_STATIC_ASSERT(boost::is_function::value); + + boost::lock_guard lock(m); + return func( + boost::forward(arg1), + boost::forward(arg2) + ); +} + +template +typename boost::result_of< + typename boost::add_pointer::type(Arg1, Arg2, Arg3) +>::type with_lock_guard( + Lockable& m, + Func* func, + BOOST_FWD_REF(Arg1) arg1, + BOOST_FWD_REF(Arg2) arg2, + BOOST_FWD_REF(Arg3) arg3 +) { + BOOST_STATIC_ASSERT(boost::is_function::value); + + boost::lock_guard lock(m); + return func( + boost::forward(arg1), + boost::forward(arg2), + boost::forward(arg3) + ); +} + +template < + class Lockable, class Func, class Arg1, class Arg2, class Arg3, class Arg4 +> +typename boost::result_of< + typename boost::add_pointer::type(Arg1, Arg2, Arg3, Arg4) +>::type with_lock_guard( + Lockable& m, + Func* func, + BOOST_FWD_REF(Arg1) arg1, + BOOST_FWD_REF(Arg2) arg2, + BOOST_FWD_REF(Arg3) arg3, + BOOST_FWD_REF(Arg4) arg4 +) { + BOOST_STATIC_ASSERT(boost::is_function::value); + + boost::lock_guard lock(m); + return func( + boost::forward(arg1), + boost::forward(arg2), + boost::forward(arg3), + boost::forward(arg4) + ); +} + +#endif + +} // namespace boost + +#endif // BOOST_THREAD_WITH_LOCK_GUARD_HPP + diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 1092c9b1..36591d04 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -782,6 +782,14 @@ rule thread-compile ( sources : reqs * : name ) ; + test-suite ts_with_lock_guard + : + [ thread-run2-noit ./sync/mutual_exclusion/with_lock_guard/with_lock_guard_simple.cpp : with_lock_guard_simple_p ] + [ thread-run2-noit ./sync/mutual_exclusion/with_lock_guard/with_lock_guard_bind.cpp : with_lock_guard_bind_p ] + [ thread-run2-noit ./sync/mutual_exclusion/with_lock_guard/with_lock_guard_move.cpp : with_lock_guard_move_p ] + [ thread-run2-noit ./sync/mutual_exclusion/with_lock_guard/with_lock_guard_lambda.cpp : with_lock_guard_lambda_p ] + ; + explicit ts_invoke ; test-suite ts_invoke : diff --git a/test/sync/mutual_exclusion/with_lock_guard/with_lock_guard_bind.cpp b/test/sync/mutual_exclusion/with_lock_guard/with_lock_guard_bind.cpp new file mode 100644 index 00000000..58ec01ab --- /dev/null +++ b/test/sync/mutual_exclusion/with_lock_guard/with_lock_guard_bind.cpp @@ -0,0 +1,145 @@ +// (C) Copyright 2013 Ruslan Baratov +// Copyright (C) 2014 Vicente J. Botet Escriba +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See www.boost.org/libs/thread for documentation. + +#define BOOST_THREAD_VERSION 4 + +#include // BOOST_TEST + +#include +#include +#include + +class Foo { + public: + Foo(int value): value_(value) { + } + + int func(int a, int b) const { + BOOST_TEST(a == 1); + BOOST_TEST(b == 31); + return a + b + value_; + } + + int func_ref(int& a) const { + a = 133; + return 36; + } + + void func_ref(int& a, int& b, int* c) const { + BOOST_TEST(value_ == 3); + a = 567; + b = 897; + *c = 345; + } + + private: + int value_; +}; + +void test_bind() { + boost::mutex m; + + Foo foo(2); + + int res_bind = boost::with_lock_guard( + m, + boost::bind(&Foo::func, foo, 1, 31) + ); + BOOST_TEST(res_bind == 34); + + int a = 0; + int res_bind_ref = boost::with_lock_guard( + m, + boost::bind(&Foo::func_ref, foo, boost::ref(a)) + ); + BOOST_TEST(res_bind_ref == 36); + BOOST_TEST(a == 133); + + a = 0; + int b = 0; + int c = 0; + Foo boo(3); + boost::with_lock_guard( + m, boost::bind(&Foo::func_ref, boo, boost::ref(a), boost::ref(b), &c) + ); + BOOST_TEST(a == 567); + BOOST_TEST(b == 897); + BOOST_TEST(c == 345); +} + +#if defined(BOOST_NO_VARIADIC_TEMPLATES) +void test_bind_non_const() { + std::cout << "c++11 variadic templates disabled" << std::endl; +} +#else + +// calling non-const bind methods supported only with c++11 variadic templates +class Boo { + public: + Boo(int value): value_(value) { + } + + int func(int a, int b) { + BOOST_TEST(a == 7); + BOOST_TEST(b == 3); + return a - b + value_; + } + + int func_ref(int& a) { + a = 598; + return 23; + } + + void func_ref(int& a, int& b, int* c) { + BOOST_TEST(value_ == 67); + a = 111; + b = 222; + *c = 333; + } + + private: + int value_; +}; + +void test_bind_non_const() { + boost::mutex m; + + Boo boo(20); + + int res_bind = boost::with_lock_guard( + m, + boost::bind(&Boo::func, boo, 7, 3) + ); + BOOST_TEST(res_bind == 24); + + int a = 0; + int res_bind_ref = boost::with_lock_guard( + m, + boost::bind(&Boo::func_ref, boo, boost::ref(a)) + ); + BOOST_TEST(res_bind_ref == 23); + BOOST_TEST(a == 598); + + a = 0; + int b = 0; + int c = 0; + Boo foo(67); + boost::with_lock_guard( + m, boost::bind(&Boo::func_ref, foo, boost::ref(a), boost::ref(b), &c) + ); + BOOST_TEST(a == 111); + BOOST_TEST(b == 222); + BOOST_TEST(c == 333); +} +#endif + +int main() { + test_bind(); + test_bind_non_const(); + return boost::report_errors(); +} diff --git a/test/sync/mutual_exclusion/with_lock_guard/with_lock_guard_lambda.cpp b/test/sync/mutual_exclusion/with_lock_guard/with_lock_guard_lambda.cpp new file mode 100644 index 00000000..2b04d71a --- /dev/null +++ b/test/sync/mutual_exclusion/with_lock_guard/with_lock_guard_lambda.cpp @@ -0,0 +1,59 @@ +// (C) Copyright 2013 Ruslan Baratov +// Copyright (C) 2014 Vicente J. Botet Escriba +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See www.boost.org/libs/thread for documentation. + +#include + +#if !defined(BOOST_NO_CXX11_DECLTYPE) +# define BOOST_RESULT_OF_USE_DECLTYPE +#endif + +#define BOOST_THREAD_VERSION 4 + +#include // BOOST_TEST + +#include // std::cout +#include +#include + +#if defined(BOOST_NO_CXX11_LAMBDAS) +void test_lambda() { + std::cout << "C++11 lambda disabled" << std::endl; +} +#else +void test_lambda() { + boost::mutex m; + int res_1 = boost::with_lock_guard( + m, + [](int a) { + BOOST_TEST(a == 13); + return a + 3; + }, + 13 + ); + BOOST_TEST(res_1 == 16); + + int v = 0; + int res_2 = boost::with_lock_guard( + m, + [&v](int a) { + BOOST_TEST(a == 55); + v = 15; + return 45; + }, + 55 + ); + BOOST_TEST(res_2 == 45); + BOOST_TEST(v == 15); +} +#endif + +int main() { + std::cout << std::boolalpha; + test_lambda(); + return boost::report_errors(); +} diff --git a/test/sync/mutual_exclusion/with_lock_guard/with_lock_guard_move.cpp b/test/sync/mutual_exclusion/with_lock_guard/with_lock_guard_move.cpp new file mode 100644 index 00000000..2c685790 --- /dev/null +++ b/test/sync/mutual_exclusion/with_lock_guard/with_lock_guard_move.cpp @@ -0,0 +1,110 @@ +// (C) Copyright 2013 Ruslan Baratov +// Copyright (C) 2014 Vicente J. Botet Escriba +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See www.boost.org/libs/thread for documentation. + +#define BOOST_THREAD_VERSION 4 + +#include // BOOST_TEST + +#include +#include + +class Foo { + public: + explicit Foo(int a) : a_(a) { + } + + Foo(BOOST_RV_REF(Foo) foo) : a_(foo.a_) { + BOOST_ASSERT(&foo != this); + foo.a_ = 0; + } + + Foo& operator=(BOOST_RV_REF(Foo) foo) { + BOOST_ASSERT(&foo != this); + a_ = foo.a_; + foo.a_ = 0; + return *this; + } + + int get() const { + return a_; + } + + private: + BOOST_MOVABLE_BUT_NOT_COPYABLE(Foo) + + int a_; +}; + +template +bool func_with_2_arg(BOOST_FWD_REF(T1) arg_1, BOOST_FWD_REF(T2) arg_2) { + BOOST_TEST(arg_1.get() == 3); + BOOST_TEST(arg_2.get() == 767); + return false; +} + +void test_movable() { + boost::mutex m; + + Foo foo_1(3); + Foo foo_2(767); + + bool res = boost::with_lock_guard( + m, &func_with_2_arg, boost::move(foo_1), boost::move(foo_2) + ); + BOOST_TEST(!res); +} + +#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) +void test_real_movable() { + std::cout << "c++11 move emulated" << std::endl; +} +#else +// test real one +class Boo { + public: + Boo(int a) : a_(a) { + } + + Boo(Boo&& boo) : a_(boo.a_) { + BOOST_ASSERT(&boo != this); + boo.a_ = 0; + } + + int get() const { + return a_; + } + + BOOST_DELETED_FUNCTION(Boo(Boo&)) + BOOST_DELETED_FUNCTION(Boo& operator=(Boo&)) + BOOST_DELETED_FUNCTION(Boo& operator=(Boo&&)) + private: + int a_; +}; + +void func_with_3_arg(Boo&& boo_1, Boo&& boo_2, Boo&& boo_3) { + BOOST_TEST(boo_1.get() == 11); + BOOST_TEST(boo_2.get() == 12); + BOOST_TEST(boo_3.get() == 13); +} + +void test_real_movable() { + boost::mutex m; + + Boo boo_3(13); + + boost::with_lock_guard( + m, func_with_3_arg, Boo(11), Boo(12), boost::move(boo_3) + ); +} +#endif + +int main() { + test_movable(); + test_real_movable(); + return boost::report_errors(); +} diff --git a/test/sync/mutual_exclusion/with_lock_guard/with_lock_guard_simple.cpp b/test/sync/mutual_exclusion/with_lock_guard/with_lock_guard_simple.cpp new file mode 100644 index 00000000..bf575b8e --- /dev/null +++ b/test/sync/mutual_exclusion/with_lock_guard/with_lock_guard_simple.cpp @@ -0,0 +1,139 @@ +// (C) Copyright 2013 Ruslan Baratov +// (C) Copyright 2013 Ruslan Baratov +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See www.boost.org/libs/thread for documentation. + +#define BOOST_THREAD_VERSION 4 + +#include // BOOST_TEST + +#include +#include +#include + +void func_with_0_arg() { +} + +void func_with_1_arg(int arg_1) { + BOOST_TEST(arg_1 == 3); +} + +bool func_with_2_arg(int arg_1, bool arg_2) { + BOOST_TEST(arg_1 == 3); + BOOST_TEST(arg_2 == true); + return !arg_2; +} + +int func_with_3_arg(int arg_1, bool arg_2, const char* arg_3) { + BOOST_TEST(arg_1 == 13); + BOOST_TEST(arg_2 == false); + BOOST_TEST(std::string(arg_3) == "message for func with 3 arg"); + return 12; +} + +const char* func_with_4_arg(int arg_1, bool arg_2, int* arg_3, int& arg_4) { + BOOST_TEST(arg_1 == 23); + BOOST_TEST(arg_2 == false); + *arg_3 = 128; + arg_4 = 456; + return "hello"; +} + +void test_simple() { + boost::mutex m; + + // #0 + boost::with_lock_guard(m, func_with_0_arg); + + // #1 + boost::with_lock_guard(m, func_with_1_arg, 3); + + // #2 + bool res2 = boost::with_lock_guard(m, func_with_2_arg, 3, true); + BOOST_TEST(res2 == false); + + // #3 + int arg1 = 13; + const char* mes = "message for func with 3 arg"; + int res3 = boost::with_lock_guard(m, func_with_3_arg, arg1, false, mes); + BOOST_TEST(res3 == 12); + + // #4 + int arg3 = 0; + int arg4 = 0; + const char* res4 = boost::with_lock_guard( + m, + func_with_4_arg, + 23, + false, + &arg3, + boost::ref(arg4) + ); + BOOST_TEST(arg3 == 128); + BOOST_TEST(arg4 == 456); + BOOST_TEST(std::string(res4) == "hello"); +} + +#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) +void test_variadic_templates() { + std::cout << "C++11 variadic templates disabled" << std::endl; +} +#else +int func_with_5_args(int a1, char a2, int& a3, bool* a4, bool a5) { + BOOST_TEST(a1 == 12); + BOOST_TEST(a2 == 'x'); + BOOST_TEST(a5 == false); + a3 = 135; + *a4 = false; + return 45; +} + +int func_with_6_args(int a1, char a2, int& a3, bool* a4, int&& a5, bool a6) { + BOOST_TEST(a1 == 12); + BOOST_TEST(a2 == 'N'); + BOOST_TEST(a5 == 2 || a5 == 13); + BOOST_TEST(a6 == false); + a3 = 200; + *a4 = true; + return 888; +} + +void test_variadic_templates() { + boost::mutex m; + + int a3 = 0; + bool a4 = true; + int res5 = boost::with_lock_guard( + m, func_with_5_args, 12, 'x', a3, &a4, false + ); + BOOST_TEST(a3 == 135); + BOOST_TEST(a4 == false); + BOOST_TEST(res5 == 45); + + int res6 = boost::with_lock_guard( + m, func_with_6_args, 12, 'N', a3, &a4, 2, false + ); + BOOST_TEST(a3 == 200); + BOOST_TEST(a4 == true); + BOOST_TEST(res6 == 888); + + a3 = 0; + a4 = false; + int a5 = 13; + int res6_move = boost::with_lock_guard( + m, func_with_6_args, 12, 'N', a3, &a4, boost::move(a5), false + ); + BOOST_TEST(a3 == 200); + BOOST_TEST(a4 == true); + BOOST_TEST(res6 == 888); +} +#endif + +int main() { + test_simple(); + test_variadic_templates(); + return boost::report_errors(); +}