libjmmcg  release_579_6_g8cffd
A C++ library containing an eclectic mix of useful, advanced components.
mutex.cpp
Go to the documentation of this file.
1 /******************************************************************************
2 ** Copyright © 2020 by J.M.McGuiness, coder@hussar.me.uk
3 **
4 ** This library is free software; you can redistribute it and/or
5 ** modify it under the terms of the GNU Lesser General Public
6 ** License as published by the Free Software Foundation; either
7 ** version 2.1 of the License, or (at your option) any later version.
8 **
9 ** This library is distributed in the hope that it will be useful,
10 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 ** Lesser General Public License for more details.
13 **
14 ** You should have received a copy of the GNU Lesser General Public
15 ** License along with this library; if not, write to the Free Software
16 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18 
19 #include "stdafx.h"
20 
21 #define BOOST_TEST_MODULE libjmmcg_tests
22 #include <boost/test/included/unit_test.hpp>
23 
24 #include <boost/mpl/list.hpp>
25 #include <boost/thread/mutex.hpp>
26 
27 #include "core/ave_deviation_meter.hpp"
28 #include "core/stats_output.hpp"
29 
30 #include "core/jthread.hpp"
31 #include "core/thread_wrapper.hpp"
32 
33 #include <chrono>
34 
35 using namespace libjmmcg;
36 using namespace ppd;
37 
38 using timed_results_t=ave_deviation_meter<double>;
39 
40 template<class LkT>
41 struct details;
42 template<>
43 struct details<api_lock_traits<platform_api, heavyweight_threading>::anon_spin_event_type> {
44  using mutex_t=api_lock_traits<platform_api, heavyweight_threading>::anon_spin_event_type;
45  using lock_t=mutex_t::lock_type;
46  using thread_t=jthread;
47 // TODO using thread_t=thread<platform_api, heavyweight_threading>;
48 };
49 template<>
50 struct details<api_lock_traits<platform_api, heavyweight_threading>::mutex_type> {
51  using mutex_t=api_lock_traits<platform_api, heavyweight_threading>::mutex_type;
52  using lock_t=mutex_t::lock_type;
53  using thread_t=jthread;
54 // TODO using thread_t=thread<platform_api, heavyweight_threading>;
55 };
56 template<>
57 struct details<std::mutex> {
58  using mutex_t=std::mutex;
59  using lock_t=std::scoped_lock<mutex_t>;
60  using thread_t=jthread;
61 };
62 template<>
63 struct details<boost::mutex> {
64  using mutex_t=boost::mutex;
65  using lock_t=boost::mutex::scoped_lock;
66  using thread_t=jthread;
67 };
68 
69 using lock_types=boost::mpl::list<
70  details<api_lock_traits<platform_api, heavyweight_threading>::anon_spin_event_type>,
71  details<api_lock_traits<platform_api, heavyweight_threading>::mutex_type>,
72  details<std::mutex>,
73  details<boost::mutex>
74 >;
75 
76 BOOST_AUTO_TEST_SUITE(mutex_tests)
77 
78 BOOST_AUTO_TEST_CASE(ctor) {
79  using mutex_t=api_lock_traits<platform_api, heavyweight_threading>::anon_spin_event_type;
80 
81  mutex_t lk;
82  BOOST_CHECK_NO_THROW(lk.lock());
83  BOOST_CHECK_NO_THROW(lk.unlock());
84 }
85 
86 BOOST_AUTO_TEST_CASE(scoped_lock) {
87  using mutex_t=api_lock_traits<platform_api, heavyweight_threading>::anon_spin_event_type;
88  using lock_t=mutex_t::lock_type;
89 
90  mutex_t lk;
91  BOOST_CHECK_NO_THROW(const lock_t lker(lk));
92 }
93 
94 BOOST_AUTO_TEST_CASE(multithreaded_scoped_lock) {
95  using mutex_t=api_lock_traits<platform_api, heavyweight_threading>::anon_spin_event_type;
96  using lock_t=mutex_t::lock_type;
97  using thread_t=jthread;
98 
99  const std::size_t num_loops=1000;
100  mutex_t lk;
101  unsigned long i=0;
102  {
103  const thread_t thr1(
104  [&lk, &i]() {
105  for (std::size_t j=0; j<num_loops; ++j) {
106  const lock_t lker(lk);
107  ++i;
108  }
109  }
110  );
111  const thread_t thr2(
112  [&lk, &i]() {
113  for (std::size_t j=0; j<num_loops; ++j) {
114  const lock_t lker(lk);
115  ++i;
116  }
117  }
118  );
119  const thread_t thr3(
120  [&lk, &i]() {
121  for (std::size_t j=0; j<num_loops; ++j) {
122  const lock_t lker(lk);
123  ++i;
124  }
125  }
126  );
127  const thread_t thr4(
128  [&lk, &i]() {
129  for (std::size_t j=0; j<num_loops; ++j) {
130  const lock_t lker(lk);
131  ++i;
132  }
133  }
134  );
135  }
136  BOOST_CHECK_EQUAL(i, 4*num_loops);
137 }
138 
139 BOOST_AUTO_TEST_SUITE(performance, *stats_to_csv::make_fixture("mutex.csv"))
140 
141 /**
142  \test <a href="./examples/mutex.svg">Graph</a> of performance results for a spin-lock based mutex versus other common mutexes.
143  ==========================================================================================
144  Results for 100*10000 repetitions.
145 */
146 BOOST_AUTO_TEST_CASE_TEMPLATE(multithreaded_scoped_lock, details_t, lock_types) {
147 #ifdef JMMCG_PERFORMANCE_TESTS
148  const std::size_t num_tests=5000;
149  const std::size_t num_loops=500000;
150 #else
151  const std::size_t num_tests=1;
152  const std::size_t num_loops=1000;
153 #endif
154 
155  const std::pair<timed_results_t, bool> timed_results(compute_average_deviation<timed_results_t::value_type>(
156  2.0,
157  num_tests,
158  []() {
159  using mutex_t=typename details_t::mutex_t;
160  using lock_t=typename details_t::lock_t;
161  using thread_t=typename details_t::thread_t;
162 
163  mutex_t lk;
164  unsigned long i=0;
165  const std::chrono::high_resolution_clock::time_point t1=std::chrono::high_resolution_clock::now();
166  {
167  const thread_t thr1(
168  [&lk, &i]() {
169  for (std::size_t j=0; j<num_loops; ++j) {
170  const lock_t lker(lk);
171  ++i;
172  }
173  }
174  );
175  const thread_t thr2(
176  [&lk, &i]() {
177  for (std::size_t j=0; j<num_loops; ++j) {
178  const lock_t lker(lk);
179  ++i;
180  }
181  }
182  );
183  const thread_t thr3(
184  [&lk, &i]() {
185  for (std::size_t j=0; j<num_loops; ++j) {
186  const lock_t lker(lk);
187  ++i;
188  }
189  }
190  );
191  const thread_t thr4(
192  [&lk, &i]() {
193  for (std::size_t j=0; j<num_loops; ++j) {
194  const lock_t lker(lk);
195  ++i;
196  }
197  }
198  );
199  }
200  const std::chrono::high_resolution_clock::time_point t2=std::chrono::high_resolution_clock::now();
201  BOOST_CHECK_EQUAL(i, 4*num_loops);
202  return timed_results_t::value_type(static_cast<double>(std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count())/(4*num_loops));
203  }
204  ));
205  std::cout<<api_threading_traits<platform_api, heavyweight_threading>::demangle_name(typeid(typename details_t::mutex_t))<<" locks-unlock in usec="<<timed_results.first<<std::endl;
206 #ifdef JMMCG_PERFORMANCE_TESTS
207  stats_to_csv::handle->stats<<timed_results.first.to_csv()<<std::flush;
208  BOOST_CHECK(!timed_results.second);
209 #endif
210 }
211 
212 BOOST_AUTO_TEST_SUITE_END()
213 
214 BOOST_AUTO_TEST_SUITE_END()