neoGFX
Cross-platform C++ app/game engine
Loading...
Searching...
No Matches
mutex.hpp
Go to the documentation of this file.
1// mutex.hpp
2/*
3 * Copyright (c) 2020 Leigh Johnston.
4 *
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
9 * met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * * Neither the name of Leigh Johnston nor the names of any
19 * other contributors to this software may be used to endorse or
20 * promote products derived from this software without specific prior
21 * written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
24 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
25 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34*/
35
36// Based on code that is:
37//
38// Copyright Oliver Kowalke 2016.
39//
40// Boost Software License - Version 1.0 - August 17th, 2003
41//
42// Permission is hereby granted, free of charge, to any person or organization
43// obtaining a copy of the software and accompanying documentation covered by
44// this license(the "Software") to use, reproduce, display, distribute,
45// execute, and transmit the Software, and to prepare derivative works of the
46// Software, and to permit third - parties to whom the Software is furnished to
47// do so, all subject to the following :
48//
49// The copyright notices in the Softwareand this entire statement, including
50// the above license grant, this restrictionand the following disclaimer,
51// must be included in all copies of the Software, in whole or in part, and
52// all derivative works of the Software, unless such copies or derivative
53// works are solely in the form of machine - executable object code generated by
54// a source language processor.
55//
56// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
57// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
58// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON - INFRINGEMENT.IN NO EVENT
59// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
60// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
61// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
62// DEALINGS IN THE SOFTWARE.
63
64#pragma once
65
66#include <neolib/neolib.hpp>
67#include <atomic>
68#include <mutex>
69#include <boost/thread/locks.hpp>
70#include <boost/lockfree/detail/prefix.hpp>
71#include <boost/fiber/detail/spinlock.hpp>
73
74namespace neolib
75{
76 struct null_mutex : public i_lockable
77 {
78 void lock() noexcept final {}
79 void unlock() noexcept final {}
80 bool try_lock() noexcept final { return true; }
81 };
82
83 template <typename Subject>
84 class proxy_mutex : public i_lockable
85 {
86 public:
87 proxy_mutex(Subject& aSubject) :
88 iSubject{ &aSubject }
89 {
90 }
91 public:
92 void lock() noexcept final
93 {
94 iSubject->lock();
95 }
96 void unlock() noexcept final
97 {
98 iSubject->unlock();
99 }
100 bool try_lock() noexcept final
101 {
102 return iSubject->try_lock();
103 }
104 private:
105 Subject* iSubject;
106 };
107
108 using boost::fibers::detail::spinlock_status;
109
110 using spinlock = boost::fibers::detail::spinlock;
111
112 class alignas(BOOST_LOCKFREE_CACHELINE_BYTES) recursive_spinlock : public i_lockable
113 {
114 public:
116 iState{ spinlock_status::unlocked },
117 iLockingThread{},
118 iLockCount{ 0u },
119 iGenerator{ std::random_device{}() }
120 {
121 }
123 {
124 assert(iState.load(std::memory_order_acquire) == spinlock_status::unlocked);
125 }
126 public:
127 void lock() noexcept final
128 {
129 auto const thisThread = std::this_thread::get_id();
130 if (iState.load(std::memory_order_acquire) == spinlock_status::locked && iLockingThread.load(std::memory_order_acquire) == thisThread)
131 {
132 ++iLockCount;
133 return;
134 }
135 std::size_t collisions = 0;
136 for (;;)
137 {
138 std::size_t retries = 0;
139 while (spinlock_status::locked == iState.load(std::memory_order_relaxed))
140 {
141 if (BOOST_FIBERS_SPIN_BEFORE_SLEEP0 > retries)
142 {
143 ++retries;
144 cpu_relax();
145 }
146 else if (BOOST_FIBERS_SPIN_BEFORE_YIELD > retries)
147 {
148 static constexpr std::chrono::microseconds us0{ 0 };
149 std::this_thread::sleep_for(us0);
150 }
151 else
152 {
153 std::this_thread::yield();
154 }
155 }
156 if (spinlock_status::locked == iState.exchange(spinlock_status::locked, std::memory_order_acquire))
157 {
158 std::uniform_int_distribution< std::size_t > distribution
159 {
160 0, static_cast<std::size_t>(1) << (std::min)(collisions, static_cast<std::size_t>(BOOST_FIBERS_CONTENTION_WINDOW_THRESHOLD))
161 };
162 const std::size_t z = distribution(iGenerator);
163 ++collisions;
164 for (std::size_t i = 0; i < z; ++i)
165 {
166 cpu_relax();
167 }
168 }
169 else
170 {
171 iLockingThread.store(thisThread);
172 ++iLockCount;
173 break;
174 }
175 }
176 }
177 void unlock() noexcept final
178 {
179 if (--iLockCount == 0u)
180 {
181 iLockingThread.store(std::thread::id{}, std::memory_order_release);
182 iState.store(spinlock_status::unlocked, std::memory_order_release);
183 }
184 }
185 bool try_lock() noexcept final
186 {
187 auto const thisThread = std::this_thread::get_id();
188 if (iState.load(std::memory_order_acquire) == spinlock_status::locked && iLockingThread.load(std::memory_order_acquire) == thisThread)
189 {
190 ++iLockCount;
191 return true;
192 }
193 bool locked = (spinlock_status::unlocked == iState.exchange(spinlock_status::locked, std::memory_order_acquire));
194 if (locked)
195 {
196 iLockingThread.store(thisThread);
197 ++iLockCount;
198 }
199 return locked;
200 }
201 private:
202 std::atomic<spinlock_status> iState;
203 std::atomic<std::thread::id> iLockingThread;
204 std::atomic<uint32_t> iLockCount;
205 std::minstd_rand iGenerator;
206 };
207
208 class alignas(BOOST_LOCKFREE_CACHELINE_BYTES) switchable_mutex : public i_lockable
209 {
210 public:
212 {
213 set_multi_threaded();
214 }
215 public:
217 {
218 iActiveMutex.emplace<neolib::null_mutex>();
219 }
221 {
222 iActiveMutex.emplace<std::recursive_mutex>();
223 }
225 {
226 iActiveMutex.emplace<neolib::recursive_spinlock>();
227 }
228 public:
229 void lock() noexcept final
230 {
231 if (std::holds_alternative<std::recursive_mutex>(iActiveMutex))
232 std::get<std::recursive_mutex>(iActiveMutex).lock();
233 else if (std::holds_alternative<neolib::recursive_spinlock>(iActiveMutex))
234 std::get<neolib::recursive_spinlock>(iActiveMutex).lock();
235 else
236 std::get<neolib::null_mutex>(iActiveMutex).lock();
237 }
238 void unlock() noexcept final
239 {
240 if (std::holds_alternative<std::recursive_mutex>(iActiveMutex))
241 std::get<std::recursive_mutex>(iActiveMutex).unlock();
242 else if (std::holds_alternative<neolib::recursive_spinlock>(iActiveMutex))
243 std::get<neolib::recursive_spinlock>(iActiveMutex).unlock();
244 else
245 std::get<neolib::null_mutex>(iActiveMutex).unlock();
246 }
247 bool try_lock() noexcept final
248 {
249 if (std::holds_alternative<std::recursive_mutex>(iActiveMutex))
250 return std::get<std::recursive_mutex>(iActiveMutex).try_lock();
251 else if (std::holds_alternative<neolib::recursive_spinlock>(iActiveMutex))
252 return std::get<neolib::recursive_spinlock>(iActiveMutex).try_lock();
253 else
254 return std::get<neolib::null_mutex>(iActiveMutex).try_lock();
255 }
256 private:
257 std::variant<std::recursive_mutex, neolib::recursive_spinlock, neolib::null_mutex> iActiveMutex;
258 };
259
260 template <typename Mutexes>
262 {
263 public:
264 scoped_multi_lock(Mutexes& aMutexes) :
265 iMutexes{ aMutexes }
266 {
267 boost::lock(iMutexes.begin(), iMutexes.end());
268 }
270 {
271 for (auto& m : iMutexes)
272 m.unlock();
273 }
274 private:
275 Mutexes& iMutexes;
276 };
277}
void lock() noexcept final
Definition mutex.hpp:92
void unlock() noexcept final
Definition mutex.hpp:96
bool try_lock() noexcept final
Definition mutex.hpp:100
proxy_mutex(Subject &aSubject)
Definition mutex.hpp:87
void unlock() noexcept final
Definition mutex.hpp:177
void lock() noexcept final
Definition mutex.hpp:127
bool try_lock() noexcept final
Definition mutex.hpp:185
scoped_multi_lock(Mutexes &aMutexes)
Definition mutex.hpp:264
void set_multi_threaded_spinlock()
Definition mutex.hpp:224
bool try_lock() noexcept final
Definition mutex.hpp:247
void unlock() noexcept final
Definition mutex.hpp:238
void lock() noexcept final
Definition mutex.hpp:229
boost::fibers::detail::spinlock spinlock
Definition mutex.hpp:110
Definition plf_hive.h:79
void lock() noexcept final
Definition mutex.hpp:78
bool try_lock() noexcept final
Definition mutex.hpp:80
void unlock() noexcept final
Definition mutex.hpp:79