Skip to content

Commit 14a94d3

Browse files
authored
maint: use synchronized_value where we use a mutex to protect data (#3992)
1 parent a918983 commit 14a94d3

File tree

12 files changed

+252
-137
lines changed

12 files changed

+252
-137
lines changed

libmamba/include/mamba/core/execution.hpp

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <vector>
1515

1616
#include "mamba/core/error_handling.hpp"
17+
#include "mamba/util/synchronized_value.hpp"
1718

1819
namespace mamba
1920
{
@@ -71,10 +72,10 @@ namespace mamba
7172
return;
7273
}
7374

74-
std::scoped_lock lock{ threads_mutex };
75+
auto synched_threads = threads.synchronize();
7576
if (is_open) // Double check necessary for correctness
7677
{
77-
threads.emplace_back(std::forward<Task>(task), std::forward<Args>(args)...);
78+
synched_threads->emplace_back(std::forward<Task>(task), std::forward<Args>(args)...);
7879
}
7980
}
8081

@@ -92,10 +93,10 @@ namespace mamba
9293
return;
9394
}
9495

95-
std::scoped_lock lock{ threads_mutex };
96+
auto synched_threads = threads.synchronize();
9697
if (is_open) // Double check necessary for correctness
9798
{
98-
threads.push_back(std::move(thread));
99+
synched_threads->push_back(std::move(thread));
99100
}
100101
}
101102

@@ -116,12 +117,12 @@ namespace mamba
116117

117118
invoke_close_handlers();
118119

119-
std::scoped_lock lock{ threads_mutex };
120-
for (auto&& t : threads)
120+
auto synched_threads = threads.synchronize();
121+
for (auto&& t : *synched_threads)
121122
{
122123
t.join();
123124
}
124-
threads.clear();
125+
synched_threads->clear();
125126
}
126127

127128
using on_close_handler = std::function<void()>;
@@ -133,21 +134,20 @@ namespace mamba
133134
return;
134135
}
135136

136-
std::scoped_lock lock{ handlers_mutex };
137+
auto handlers = close_handlers.synchronize();
137138
if (is_open) // Double check needed to avoid adding new handles while closing.
138139
{
139-
close_handlers.push_back(std::move(handler));
140+
handlers->push_back(std::move(handler));
140141
}
141142
}
142143

143144
private:
144145

145146
std::atomic<bool> is_open{ true };
146-
std::vector<std::thread> threads;
147-
std::recursive_mutex threads_mutex; // TODO: replace by synchronized_value once available
148-
149-
std::vector<on_close_handler> close_handlers;
150-
std::recursive_mutex handlers_mutex; // TODO: replace by synchronized_value once available
147+
using Threads = std::vector<std::thread>;
148+
using CloseHandlers = std::vector<on_close_handler>;
149+
util::synchronized_value<Threads, std::recursive_mutex> threads;
150+
util::synchronized_value<CloseHandlers, std::recursive_mutex> close_handlers;
151151

152152
void invoke_close_handlers();
153153
};

libmamba/include/mamba/core/thread_utils.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,15 +186,15 @@ namespace mamba
186186

187187
inline void counting_semaphore::lock()
188188
{
189-
std::unique_lock<std::mutex> lock(m_access_mutex);
189+
std::unique_lock lock{ m_access_mutex };
190190
m_cv.wait(lock, [&]() { return m_value > 0; });
191191
--m_value;
192192
}
193193

194194
inline void counting_semaphore::unlock()
195195
{
196196
{
197-
std::lock_guard<std::mutex> lock(m_access_mutex);
197+
std::unique_lock lock{ m_access_mutex };
198198
if (++m_value <= 0)
199199
{
200200
return;

libmamba/include/mamba/download/mirror.hpp

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99

1010
#include <functional>
1111
#include <memory>
12-
#include <mutex>
1312
#include <optional>
1413
#include <string>
1514
#include <vector>
1615

1716
#include <fmt/core.h>
1817

1918
#include "mamba/download/request.hpp"
19+
#include "mamba/util/synchronized_value.hpp"
2020

2121
namespace mamba::download
2222
{
@@ -66,6 +66,16 @@ namespace mamba::download
6666
MirrorRequest& operator=(MirrorRequest&&) = default;
6767
};
6868

69+
struct MirrorStats // Moved out of Mirror internals because of compilers not agreeing:
70+
// https://godbolt.org/z/GcjWhrb9W
71+
{
72+
std::optional<std::size_t> allowed_connections = std::nullopt;
73+
std::size_t max_tried_connections = 0;
74+
std::size_t running_transfers = 0;
75+
std::size_t successful_transfers = 0;
76+
std::size_t failed_transfers = 0;
77+
};
78+
6979
// A Mirror represents a location from where an asset can be downloaded.
7080
// It handles the generation of required requests to get the asset, and
7181
// provides some statistics about its usage.
@@ -109,16 +119,13 @@ namespace mamba::download
109119
MirrorID m_id;
110120
size_t m_max_retries;
111121

112-
// TODO: use synchronized value
113-
std::mutex m_stats_mutex;
114-
std::optional<std::size_t> m_allowed_connections = std::nullopt;
115-
std::size_t m_max_tried_connections = 0;
116-
std::size_t m_running_transfers = 0;
117-
std::size_t m_successful_transfers = 0;
118-
std::size_t m_failed_transfers = 0;
122+
static_assert(std::default_initializable<MirrorStats>);
123+
124+
util::synchronized_value<MirrorStats> m_stats;
119125
};
120126

121127
std::unique_ptr<Mirror> make_mirror(std::string url);
128+
122129
}
123130

124131
#endif

libmamba/include/mamba/util/synchronized_value.hpp

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
#define MAMBA_UTIL_SYNCHRONIZED_VALUE_HPP
99

1010
#include <concepts>
11+
#include <functional>
1112
#include <mutex>
1213
#include <shared_mutex>
1314
#include <tuple>
15+
#include <utility>
1416

1517
namespace mamba::util
1618
{
@@ -35,6 +37,26 @@ namespace mamba::util
3537
template <class T, template <class...> class U>
3638
constexpr bool is_type_instance_of_v = is_type_instance_of<T, U>::value;
3739

40+
/// `true` if the instances of two provided types can be compared with operator==.
41+
/// Notice that this concept is less restrictive than `std::equality_comparable_with`,
42+
/// which requires the existence of a common reference type for T and U. This additional
43+
/// restriction makes it impossible to use it in the context here (originally of sparrow), where
44+
/// we want to compare objects that are logically similar while being "physically" different.
45+
// Source:
46+
// https://github.com/man-group/sparrow/blob/66f70418cf1b00cc294c99bbbe04b5b4d2f83c98/include/sparrow/utils/mp_utils.hpp#L604-L619
47+
48+
template <class T, class U>
49+
concept weakly_equality_comparable_with = requires(
50+
const std::remove_reference_t<T>& t,
51+
const std::remove_reference_t<U>& u
52+
) {
53+
{ t == u } -> std::convertible_to<bool>;
54+
{ t != u } -> std::convertible_to<bool>;
55+
{ u == t } -> std::convertible_to<bool>;
56+
{ u != t } -> std::convertible_to<bool>;
57+
};
58+
59+
3860
/////////////////////////////
3961

4062

@@ -256,7 +278,9 @@ namespace mamba::util
256278

257279
/// Constructs with a provided value as initializer for the stored object.
258280
template <typename V>
259-
requires std::assignable_from<T&, V> and (not std::same_as<this_type, std::decay_t<V>>)
281+
requires(not std::same_as<T, std::decay_t<V>>)
282+
and (not std::same_as<this_type, std::decay_t<V>>)
283+
and std::assignable_from<T&, V>
260284
synchronized_value(V&& value) noexcept
261285
: m_value(std::forward<V>(value))
262286
{
@@ -266,6 +290,11 @@ namespace mamba::util
266290
// the definition here.
267291
}
268292

293+
/// Constructs with a provided value as initializer for the stored object.
294+
// NOTE: this is redundant with the generic impl, but required to workaround
295+
// apple-clang failing to properly constrain the generic impl.
296+
synchronized_value(T value) noexcept;
297+
269298
/// Constructs with a provided initializer list used to initialize the stored object.
270299
template <typename V>
271300
requires std::constructible_from<T, std::initializer_list<V>>
@@ -284,14 +313,17 @@ namespace mamba::util
284313
the call. If `SharedMutex<M> == true`, the lock is a shared-lock for the provided
285314
`synchronized_value`'s mutex.
286315
*/
287-
template <std::equality_comparable_with<T> U, Mutex OtherMutex>
316+
template <std::default_initializable U, Mutex OtherMutex>
317+
requires std::assignable_from<T&, U>
288318
auto operator=(const synchronized_value<U, OtherMutex>& other) -> synchronized_value&;
289319

290320
/** Locks and assign the provided value to the stored object.
291321
The lock is released before the end of the call.
292322
*/
293323
template <typename V>
294-
requires std::assignable_from<T&, V> and (not std::same_as<this_type, std::decay_t<V>>)
324+
requires(not std::same_as<T, std::decay_t<V>>)
325+
and (not std::same_as<this_type, std::decay_t<V>>)
326+
and std::assignable_from<T&, V>
295327
auto operator=(V&& value) noexcept -> synchronized_value&
296328
{
297329
// NOTE: when moving the definition outside the class,
@@ -303,6 +335,13 @@ namespace mamba::util
303335
return *this;
304336
}
305337

338+
/** Locks and assign the provided value to the stored object.
339+
The lock is released before the end of the call.
340+
*/
341+
// NOTE: this is redundant with the generic impl, but required to workaround
342+
// apple-clang failing to properly constrain the generic impl.
343+
auto operator=(const T& value) noexcept -> synchronized_value&;
344+
306345
/** Locks and return the value of the current object.
307346
The lock is released before the end of the call.
308347
If `SharedMutex<M> == true`, the lock is a shared-lock.
@@ -465,12 +504,12 @@ namespace mamba::util
465504
/** Locks (shared if possible) and compare equality of the stored object's value with the
466505
provided value.
467506
*/
468-
auto operator==(const std::equality_comparable_with<T> auto& other_value) const -> bool;
507+
auto operator==(const weakly_equality_comparable_with<T> auto& other_value) const -> bool;
469508

470509
/** Locks both (shared if possible) and compare equality of the stored object's value with
471510
the provided value.
472511
*/
473-
template <std::equality_comparable_with<T> U, Mutex OtherMutex>
512+
template <weakly_equality_comparable_with<T> U, Mutex OtherMutex>
474513
auto operator==(const synchronized_value<U, OtherMutex>& other_value) const -> bool;
475514

476515
auto swap(synchronized_value& other) -> void;
@@ -508,6 +547,12 @@ namespace mamba::util
508547
template <std::default_initializable T, Mutex M>
509548
synchronized_value<T, M>::synchronized_value() noexcept(std::is_nothrow_default_constructible_v<T>) = default;
510549

550+
template <std::default_initializable T, Mutex M>
551+
synchronized_value<T, M>::synchronized_value(T value) noexcept
552+
: m_value(std::move(value))
553+
{
554+
}
555+
511556
template <std::default_initializable T, Mutex M>
512557
synchronized_value<T, M>::synchronized_value(const synchronized_value& other)
513558
{
@@ -516,7 +561,8 @@ namespace mamba::util
516561
}
517562

518563
template <std::default_initializable T, Mutex M>
519-
template <std::equality_comparable_with<T> U, Mutex OtherMutex>
564+
template <std::default_initializable U, Mutex OtherMutex>
565+
requires std::assignable_from<T&, U>
520566
auto synchronized_value<T, M>::operator=(const synchronized_value<U, OtherMutex>& other)
521567
-> synchronized_value<T, M>&
522568
{
@@ -526,6 +572,14 @@ namespace mamba::util
526572
return *this;
527573
}
528574

575+
template <std::default_initializable T, Mutex M>
576+
auto synchronized_value<T, M>::operator=(const T& value) noexcept -> synchronized_value&
577+
{
578+
auto _ = lock_as_exclusive(m_mutex);
579+
m_value = value;
580+
return *this;
581+
}
582+
529583
template <std::default_initializable T, Mutex M>
530584
template <typename V>
531585
requires std::constructible_from<T, std::initializer_list<V>>
@@ -584,15 +638,16 @@ namespace mamba::util
584638
}
585639

586640
template <std::default_initializable T, Mutex M>
587-
auto synchronized_value<T, M>::operator==(const std::equality_comparable_with<T> auto& other_value
641+
auto
642+
synchronized_value<T, M>::operator==(const weakly_equality_comparable_with<T> auto& other_value
588643
) const -> bool
589644
{
590645
auto _ = lock_as_readonly(m_mutex);
591646
return m_value == other_value;
592647
}
593648

594649
template <std::default_initializable T, Mutex M>
595-
template <std::equality_comparable_with<T> U, Mutex OtherMutex>
650+
template <weakly_equality_comparable_with<T> U, Mutex OtherMutex>
596651
auto
597652
synchronized_value<T, M>::operator==(const synchronized_value<U, OtherMutex>& other_value) const
598653
-> bool
@@ -623,7 +678,6 @@ namespace mamba::util
623678
{
624679
return std::make_tuple(std::forward<SynchronizedValues>(sync_values).synchronize()...);
625680
}
626-
627681
}
628682

629683
#endif

libmamba/src/core/execution.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ namespace mamba
88

99
void MainExecutor::invoke_close_handlers()
1010
{
11-
std::scoped_lock lock{ handlers_mutex };
12-
for (auto&& handler : close_handlers)
11+
auto synched_handlers = close_handlers.synchronize();
12+
for (auto&& handler : *synched_handlers)
1313
{
1414
const auto result = safe_invoke(handler);
1515
if (!result)

0 commit comments

Comments
 (0)