• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

STEllAR-GROUP / hpx / #867

15 Jan 2023 08:00PM UTC coverage: 86.487% (+0.5%) from 85.951%
#867

push

StellarBot
Merge #6135

6135: Fixing warnings reported by MSVC analysis r=hkaiser a=hkaiser

- adding MSVC specific #pragma's to suppress the benign warnings


Co-authored-by: Hartmut Kaiser <hartmut.kaiser@gmail.com>

120 of 120 new or added lines in 33 files covered. (100.0%)

174599 of 201880 relevant lines covered (86.49%)

1945607.64 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.91
/libs/core/synchronization/include/hpx/synchronization/latch.hpp
1
//  Copyright (c) 2015-2022 Hartmut Kaiser
2
//
3
//  SPDX-License-Identifier: BSL-1.0
4
//  Distributed under the Boost Software License, Version 1.0. (See accompanying
5
//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6

7
/// \file hpx/synchronization/latch.hpp
8

9
#pragma once
10

11
#include <hpx/assert.hpp>
12
#include <hpx/concurrency/cache_line_data.hpp>
13
#include <hpx/synchronization/detail/condition_variable.hpp>
14
#include <hpx/synchronization/spinlock.hpp>
15
#include <hpx/type_support/unused.hpp>
16

17
#include <atomic>
18
#include <cstddef>
19
#include <cstdint>
20
#include <limits>
21
#include <mutex>
22
#include <utility>
23

24
///////////////////////////////////////////////////////////////////////////////
25
namespace hpx {
26

27
    /// Latches are a thread coordination mechanism that allow one or more
28
    /// threads to block until an operation is completed. An individual latch
29
    /// is a singleuse object; once the operation has been completed, the latch
30
    /// cannot be reused.
31
    class latch
32
    {
33
    public:
34
        HPX_NON_COPYABLE(latch);
35

36
    protected:
37
        using mutex_type = hpx::spinlock;
38

39
    public:
40
        /// Initialize the latch
41
        ///
42
        /// Requires: count >= 0.
43
        /// Synchronization: None
44
        /// Postconditions: counter_ == count.
45
        ///
46
        explicit latch(std::ptrdiff_t count)
88,825✔
47
          : mtx_()
88,825✔
48
          , cond_()
88,825✔
49
          , counter_(count)
88,825✔
50
          , notified_(count == 0)
88,825✔
51
        {
52
        }
88,825✔
53

54
        /// Requires: No threads are blocked at the synchronization point.
55
        ///
56
        /// \note May be called even if some threads have not yet returned
57
        ///       from wait() or count_down_and_wait(), provided that counter_
58
        ///       is 0.
59
        /// \note The destructor might not return until all threads have exited
60
        ///       wait() or count_down_and_wait().
61
        /// \note It is the caller's responsibility to ensure that no other
62
        ///       thread enters wait() after one thread has called the
63
        ///       destructor. This may require additional coordination.
64
#if defined(HPX_DEBUG)
65
        ~latch()
88,826✔
66
        {
67
            HPX_ASSERT(counter_ == 0);
88,826✔
68
        }
88,826✔
69
#else
70
        ~latch() = default;
71
#endif
72

73
        /// Returns:        The maximum value of counter that the implementation
74
        ///                 supports.
75
        static constexpr std::ptrdiff_t(max)() noexcept
76
        {
77
            return (std::numeric_limits<std::ptrdiff_t>::max)();
78
        }
79

80
        /// Decrements counter_ by n. Does not block.
81
        ///
82
        /// Requires: counter_ >= n and n >= 0.
83
        ///
84
        /// Synchronization: Synchronizes with all calls that block on this
85
        /// latch and with all try_wait calls on this latch that return true .
86
        ///
87
        /// \throws Nothing.
88
        ///
89
        void count_down(std::ptrdiff_t update)
681,621✔
90
        {
91
            HPX_ASSERT(update >= 0);
681,621✔
92

93
            std::ptrdiff_t new_count = (counter_ -= update);
681,650✔
94
            HPX_ASSERT(new_count >= 0);
681,621✔
95

96
            // 26111: Caller failing to release lock 'this->mtx_.data_'
97
            // 26115: Failing to release lock 'this->mtx_.data_'
98
            // 26117: Releasing unheld lock 'this->mtx_.data_'
99
#if defined(HPX_MSVC)
100
#pragma warning(push)
101
#pragma warning(disable : 26111 26115 26117)
102
#endif
103
            if (new_count == 0)
681,663✔
104
            {
105
                std::unique_lock l(mtx_.data_);
86,074✔
106
                notified_ = true;
86,074✔
107

108
                // Note: we use notify_one repeatedly instead of notify_all as we
109
                // know that our implementation of condition_variable::notify_one
110
                // relinquishes the lock before resuming the waiting thread
111
                // that avoids suspension of this thread when it tries to
112
                // re-lock the mutex while exiting from condition_variable::wait
113
                while (cond_.data_.notify_one(
86,080✔
114
                    HPX_MOVE(l), threads::thread_priority::boost))
86,086✔
115
                {
116
                    l = std::unique_lock(mtx_.data_);
×
117
                }
118
            }
86,086✔
119

120
#if defined(HPX_MSVC)
121
#pragma warning(pop)
122
#endif
123
        }
681,669✔
124

125
        /// Returns:        With very low probability false. Otherwise
126
        ///                 counter == 0.
127
        bool try_wait() const noexcept
86,702✔
128
        {
129
            return counter_.load(std::memory_order_acquire) == 0;
86,702✔
130
        }
131

132
        /// If counter_ is 0, returns immediately. Otherwise, blocks the
133
        /// calling thread at the synchronization point until counter_
134
        /// reaches 0.
135
        ///
136
        /// \throws Nothing.
137
        ///
138
        void wait() const
4✔
139
        {
140
            // 26110: Caller failing to hold lock 'this->mtx_.data_'
141
            // 26117: Releasing unheld lock 'this->mtx_.data_'
142
#if defined(HPX_MSVC)
143
#pragma warning(push)
144
#pragma warning(disable : 26110 26117)
145
#endif
146

147
            std::unique_lock l(mtx_.data_);
4✔
148
            if (counter_.load(std::memory_order_relaxed) > 0 || !notified_)
4✔
149
            {
150
                cond_.data_.wait(l, "hpx::latch::wait");
1✔
151

152
                HPX_ASSERT_LOCKED(
1✔
153
                    l, counter_.load(std::memory_order_relaxed) == 0);
154
                HPX_ASSERT_LOCKED(l, notified_);
1✔
155
            }
1✔
156

157
#if defined(HPX_MSVC)
158
#pragma warning(pop)
159
#endif
160
        }
4✔
161

162
        /// Effects: Equivalent to:
163
        ///             count_down(update);
164
        ///             wait();
165
        void arrive_and_wait(std::ptrdiff_t update = 1)
89,453✔
166
        {
167
            HPX_ASSERT(update >= 0);
89,453✔
168

169
            std::unique_lock l(mtx_.data_);
89,452✔
170

171
            std::ptrdiff_t old_count =
89,452✔
172
                counter_.fetch_sub(update, std::memory_order_relaxed);
89,452✔
173
            HPX_ASSERT_LOCKED(l, old_count >= update);
89,453✔
174

175
            // 26110: Caller failing to hold lock 'this->mtx_.data_'
176
            // 26111: Caller failing to release lock 'this->mtx_.data_'
177
            // 26115: Failing to release lock 'this->mtx_.data_'
178
            // 26117: Releasing unheld lock 'this->mtx_.data_'
179
#if defined(HPX_MSVC)
180
#pragma warning(push)
181
#pragma warning(disable : 26110 26111 26115 26117)
182
#endif
183

184
            if (old_count > update)
89,452✔
185
            {
186
                cond_.data_.wait(l, "hpx::latch::arrive_and_wait");
86,709✔
187

188
                HPX_ASSERT_LOCKED(
86,709✔
189
                    l, counter_.load(std::memory_order_relaxed) == 0);
190
                HPX_ASSERT_LOCKED(l, notified_);
86,708✔
191
            }
86,708✔
192
            else
193
            {
194
                notified_ = true;
2,742✔
195

196
                // Note: we use notify_one repeatedly instead of notify_all as we
197
                // know that our implementation of condition_variable::notify_one
198
                // relinquishes the lock before resuming the waiting thread
199
                // which avoids suspension of this thread when it tries to
200
                // re-lock the mutex while exiting from condition_variable::wait
201
                while (cond_.data_.notify_one(
3,358✔
202
                    HPX_MOVE(l), threads::thread_priority::boost))
3,358✔
203
                {
204
                    l = std::unique_lock(mtx_.data_);
616✔
205
                }
206
            }
207

208
#if defined(HPX_MSVC)
209
#pragma warning(pop)
210
#endif
211
        }
89,450✔
212

213
    protected:
214
        mutable util::cache_line_data<mutex_type> mtx_;
215
        mutable util::cache_line_data<
216
            hpx::lcos::local::detail::condition_variable>
217
            cond_;
218
        std::atomic<std::ptrdiff_t> counter_;
219
        bool notified_;
220
    };
221
}    // namespace hpx
222

223
namespace hpx::lcos::local {
224

225
    /// \cond NOINTERNAL
226
    using cpp20_latch HPX_DEPRECATED_V(1, 8,
227
        "hpx::lcos::local::cpp20_latch is deprecated, use hpx::latch instead") =
228
        hpx::latch;
229
    /// \endcond
230

231
    ///////////////////////////////////////////////////////////////////////////
232
    /// A latch maintains an internal counter_ that is initialized when the
233
    /// latch is created. Threads may block at a synchronization point waiting
234
    /// for counter_ to be decremented to 0. When counter_ reaches 0, all such
235
    /// blocked threads are released.
236
    ///
237
    /// Calls to countdown_and_wait() , count_down() , wait() , is_ready(),
238
    /// count_up() , and reset() behave as atomic operations.
239
    ///
240
    /// \note   A \a hpx::latch is not an LCO in the sense that it has no global
241
    /// id and it can't be triggered using the action (parcel) mechanism. Use
242
    /// hpx::distributed::latch instead if this is required. It is just a low
243
    /// level synchronization primitive allowing to synchronize a given number
244
    /// of \a threads.
245
    class latch : public hpx::latch
246
    {
247
    public:
248
        HPX_NON_COPYABLE(latch);
249

250
    public:
251
        /// Initialize the latch
252
        ///
253
        /// Requires: count >= 0.
254
        /// Synchronization: None
255
        /// Postconditions: counter_ == count.
256
        ///
257
        explicit latch(std::ptrdiff_t count)
86,044✔
258
          : hpx::latch(count)
86,044✔
259
        {
86,044✔
260
        }
86,044✔
261

262
        /// Requires: No threads are blocked at the synchronization point.
263
        ///
264
        /// \note May be called even if some threads have not yet returned
265
        ///       from wait() or count_down_and_wait(), provided that counter_
266
        ///       is 0.
267
        /// \note The destructor might not return until all threads have exited
268
        ///       wait() or count_down_and_wait().
269
        /// \note It is the caller's responsibility to ensure that no other
270
        ///       thread enters wait() after one thread has called the
271
        ///       destructor. This may require additional coordination.
272
        ~latch() = default;
86,046✔
273

274
        /// Decrements counter_ by 1 . Blocks at the synchronization point
275
        /// until counter_ reaches 0.
276
        ///
277
        /// Requires: counter_ > 0.
278
        ///
279
        /// Synchronization: Synchronizes with all calls that block on this
280
        /// latch and with all is_ready calls on this latch that return true.
281
        ///
282
        /// \throws Nothing.
283
        ///
284
        void count_down_and_wait()
285
        {
286
            hpx::latch::arrive_and_wait();
287
        }
288

289
        /// Returns: counter_ == 0. Does not block.
290
        ///
291
        /// \throws Nothing.
292
        ///
293
        bool is_ready() const noexcept
86,070✔
294
        {
295
            return hpx::latch::try_wait();
86,070✔
296
        }
297

298
        void abort_all()
×
299
        {
300
            // 26115: Failing to release lock 'this->mtx_.data_'
301
#if defined(HPX_MSVC)
302
#pragma warning(push)
303
#pragma warning(disable : 26115)
304
#endif
305

306
            std::unique_lock l(mtx_.data_);
×
307
            cond_.data_.abort_all(HPX_MOVE(l));
×
308

309
#if defined(HPX_MSVC)
310
#pragma warning(pop)
311
#endif
312
        }
×
313

314
        /// Increments counter_ by n. Does not block.
315
        ///
316
        /// Requires:  n >= 0.
317
        ///
318
        /// \throws Nothing.
319
        ///
320
        void count_up(std::ptrdiff_t n)
100✔
321
        {
322
            HPX_ASSERT(n >= 0);
100✔
323

324
            std::ptrdiff_t old_count =
100✔
325
                counter_.fetch_add(n, std::memory_order_acq_rel);
100✔
326

327
            HPX_ASSERT(old_count > 0);
100✔
328
            HPX_UNUSED(old_count);
100✔
329
        }
100✔
330

331
        /// Reset counter_ to n. Does not block.
332
        ///
333
        /// Requires:  n >= 0.
334
        ///
335
        /// \throws Nothing.
336
        void reset(std::ptrdiff_t n)
1✔
337
        {
338
            HPX_ASSERT(n >= 0);
1✔
339

340
            std::ptrdiff_t old_count =
1✔
341
                counter_.exchange(n, std::memory_order_acq_rel);
1✔
342

343
            HPX_ASSERT(old_count == 0);
1✔
344
            HPX_UNUSED(old_count);
1✔
345

346
            std::scoped_lock l(mtx_.data_);
1✔
347
            notified_ = false;
1✔
348
        }
1✔
349

350
        /// Effects: Equivalent to:
351
        ///             if (is_ready())
352
        ///                 reset(count);
353
        ///             count_up(n);
354
        /// Returns: true if the latch was reset
355
        bool reset_if_needed_and_count_up(
172,020✔
356
            std::ptrdiff_t n, std::ptrdiff_t count)
357
        {
358
            HPX_ASSERT(n >= 0);
172,020✔
359
            HPX_ASSERT(count >= 0);
172,023✔
360

361
            std::unique_lock l(mtx_.data_);
172,012✔
362

363
            if (notified_)
172,012✔
364
            {
365
                notified_ = false;
×
366

367
                std::ptrdiff_t old_count =
×
368
                    counter_.fetch_add(n + count, std::memory_order_relaxed);
×
369

370
                HPX_ASSERT(old_count == 0);
×
371
                HPX_UNUSED(old_count);
×
372

373
                return true;
×
374
            }
375

376
            std::ptrdiff_t old_count =
172,061✔
377
                counter_.fetch_add(n, std::memory_order_relaxed);
172,061✔
378

379
            HPX_ASSERT(old_count > 0);
172,012✔
380
            HPX_UNUSED(old_count);
172,057✔
381

382
            return false;
172,057✔
383
        }
172,057✔
384
    };
385
}    // namespace hpx::lcos::local
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc