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

STEllAR-GROUP / hpx / #853

19 Dec 2022 01:01AM UTC coverage: 86.287% (+0.4%) from 85.912%
#853

push

StellarBot
Merge #6109

6109: Modernize serialization module r=hkaiser a=hkaiser

- flyby separate serialization of Boost types

working towards https://github.com/STEllAR-GROUP/hpx/issues/5497

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

53 of 53 new or added lines in 6 files covered. (100.0%)

173939 of 201582 relevant lines covered (86.29%)

1931657.12 hits per line

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

94.37
/libs/core/threading/tests/unit/condition_variable_race.cpp
1
//  Copyright (c) 2020-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
//  Parts of this code were inspired by https://github.com/josuttis/jthread. The
8
//  original code was published by Nicolai Josuttis and Lewis Baker under the
9
//  Creative Commons Attribution 4.0 International License
10
//  (http://creativecommons.org/licenses/by/4.0/).
11

12
#include <hpx/local/init.hpp>
13
#include <hpx/modules/synchronization.hpp>
14
#include <hpx/modules/testing.hpp>
15
#include <hpx/modules/threading.hpp>
16

17
#include <atomic>
18
#include <cstdlib>
19
#include <cstring>
20
#include <memory>
21
#include <mutex>
22

23
///////////////////////////////////////////////////////////////////////////////
24
//
25
// Test-Case by Howard Hinnant
26
// - emails 8.-9.11.18
27
//
28
// Original problem:
29
//  There's a bug in condition_variable_any2 that I don't think impacts the
30
//  implementation of jthread.
31
//  However this is such a complex subject that no stone should be left unturned.
32
//
33
//     ~condition_variable_any();
34
//
35
//     Requires: There shall be no thread blocked on *this.
36
//     [Note: That is, all threads shall have been notified; they may
37
//            subsequently block on the lock specified in the wait. This relaxes
38
//            the usual rules, which would have required all wait calls to
39
//            happen before destruction. Only the notification to unblock the
40
//            wait needs to happen before destruction. The user should take care
41
//            to ensure that no threads wait on *this once the destructor has
42
//            been started, especially when the waiting threads are calling the
43
//            wait functions in a loop or using the overloads of wait, wait_for,
44
//            or wait_until that take a predicate.
45
//      end note]
46
//
47
// That big long note means ~condition_variable_any() can execute before a
48
// signaled thread returns from a wait.
49
// If this happens with condition_variable_any, that waiting thread will attempt
50
// to lock the destructed mutex mut.
51
// To fix this, there must be shared ownership of the data member mut between
52
// the condition_variable_any  and the member functions wait (wait_for, etc.).
53
//
54
// libc++'s implementation gets this right:
55
//  https://github.com/llvm-mirror/libcxx/blob/master/include/condition_variable
56
//
57
// It holds the data member mutex with a shared_ptr<mutex> instead of mutex
58
// directly, and the wait functions create a local shared_ptr<mutex> copy on
59
// entry so that if *this destructs out from under the thread executing
60
// the wait function, the mutex stays alive until the wait function returns.
61
//
62
// Nico, after fixed by Anthony:
63
//  Thanks, but if I now use the cv_any implementation, fixed by Anthony,
64
//  I still get a core dump:
65
//    https://wandbox.org/permlink/VvG1UKubY69yAK7g
66
//  (#ifdef for both CV implementations)
67
//  So, either the test or the fix seems to be wrong.
68
//
69
// HH:
70
//  I'm guessing that to reliably test this, one is going to have to rebuild
71
//  your condition_variable_any with an internal mutex that checks for unlock-
72
//  after-destruction.
73
//  And the problem with that is now you no no longer have a std::mutex to put
74
//  into your internal std::condition_variable...
75
//
76
//  _Maybe_ you could test it by making your internal std::condition_variable a
77
//  std::condition_variable_any then you could put a debugging mutex into it.
78
//  But I'm not sure, because this is getting pretty weird and I have not
79
//  actually tried this.
80
///////////////////////////////////////////////////////////////////////////////
81

82
// Original test case from HH:
83
//
84
// hpx::condition_variable_any* cv = nullptr;
85
// hpx::mutex m;
86
// bool f_ready = false;
87
// bool g_ready = false;
88
//
89
// void f()
90
// {
91
//     m.lock();
92
//     f_ready = true;
93
//     cv->notify_one();
94
//     cv->~condition_variable_any();
95
//     std::memset(cv, 0x55, sizeof(*cv));    // UB but OK to ensure the check
96
//     m.unlock();
97
// }
98
//
99
// void g()
100
// {
101
//     m.lock();
102
//     g_ready = true;
103
//     cv->notify_one();
104
//     while (!f_ready)
105
//     {
106
//         cv->wait(m);
107
//     }
108
//     m.unlock();
109
// }
110
//
111
// void test_cv_any_mutex()
112
// {
113
//     // AW 9.11.18:
114
//     // Writing over the deleted memory is undefined behavior. In particular,
115
//     // it can destroy the heap data structure, and cause other problems.
116
//     // If you replace new/delete with malloc and free, then it's OK
117
//     void* raw = std::malloc(sizeof(hpx::condition_variable_any));
118
//     cv = new (raw) hpx::condition_variable_any;
119
//
120
//     hpx::thread th2(g);
121
//     m.lock();
122
//     while (!g_ready)
123
//         cv->wait(m);
124
//     m.unlock();
125
//
126
//     hpx::thread th1(f);
127
//     th1.join();
128
//
129
//     th2.join();
130
//     std::free(raw);
131
// }
132

133
void test_cv_mutex()
1✔
134
{
135
    void* raw = std::malloc(sizeof(hpx::condition_variable));
1✔
136
    hpx::condition_variable* cv = new (raw) hpx::condition_variable;
1✔
137

138
    hpx::mutex m;
1✔
139
    std::atomic<bool> f_ready{false};
1✔
140
    std::atomic<bool> g_ready{false};
1✔
141

142
    hpx::thread t2([&] {
2✔
143
        std::unique_lock<hpx::mutex> ul{m};
1✔
144
        g_ready = true;
1✔
145
        cv->notify_one();
1✔
146
        while (!f_ready)
2✔
147
        {
148
            cv->wait(ul);
1✔
149
        }
150
    });
1✔
151

152
    {
153
        std::unique_lock<hpx::mutex> ul{m};
1✔
154
        while (!g_ready)
2✔
155
        {
156
            cv->wait(ul);
1✔
157
        }
158
    }
1✔
159

160
    hpx::thread t1([&] {
2✔
161
        std::unique_lock<hpx::mutex> ul{m};
1✔
162
        f_ready = true;
1✔
163
        cv->notify_one();
1✔
164
        std::destroy_at(cv);
1✔
165
        // NOLINTNEXTLINE(bugprone-undefined-memory-manipulation)
166
        std::memset(
1✔
167
            (void*) cv, 0x55, sizeof(*cv));    // UB but OK to ensure the check
1✔
168
    });
1✔
169

170
    t1.join();
1✔
171
    t2.join();
1✔
172

173
    std::free(raw);
1✔
174
}
1✔
175

176
void test_cv_any_mutex()
1✔
177
{
178
    void* raw = std::malloc(sizeof(hpx::condition_variable_any));
1✔
179
    hpx::condition_variable_any* cv = new (raw) hpx::condition_variable_any;
1✔
180

181
    hpx::mutex m;
1✔
182
    std::atomic<bool> f_ready{false};
1✔
183
    std::atomic<bool> g_ready{false};
1✔
184

185
    hpx::thread t2([&] {
2✔
186
        std::unique_lock<hpx::mutex> ul{m};
1✔
187
        g_ready = true;
1✔
188
        cv->notify_one();
1✔
189
        while (!f_ready)
2✔
190
        {
191
            cv->wait(ul);
1✔
192
        }
193
    });
1✔
194

195
    {
196
        std::unique_lock<hpx::mutex> ul{m};
1✔
197
        while (!g_ready)
2✔
198
        {
199
            cv->wait(ul);
1✔
200
        }
201
    }
1✔
202

203
    hpx::thread t1([&] {
2✔
204
        std::unique_lock<hpx::mutex> ul{m};
1✔
205
        f_ready = true;
1✔
206
        cv->notify_one();
1✔
207
        std::destroy_at(cv);
1✔
208
        // NOLINTNEXTLINE(bugprone-undefined-memory-manipulation)
209
        std::memset(
1✔
210
            (void*) cv, 0x55, sizeof(*cv));    // UB but OK to ensure the check
1✔
211
    });
1✔
212

213
    t1.join();
1✔
214
    t2.join();
1✔
215

216
    std::free(raw);
1✔
217
}
1✔
218

219
///////////////////////////////////////////////////////////////////////////////
220
int hpx_main()
1✔
221
{
222
    std::set_terminate([]() { HPX_TEST(false); });
2✔
223
    try
224
    {
225
        test_cv_mutex();
1✔
226
        test_cv_any_mutex();
1✔
227
    }
1✔
228
    catch (...)
229
    {
230
        HPX_TEST(false);
×
231
    }
×
232

233
    return hpx::local::finalize();
1✔
234
}
×
235

236
int main(int argc, char* argv[])
1✔
237
{
238
    HPX_TEST_EQ_MSG(hpx::local::init(hpx_main, argc, argv), 0,
1✔
239
        "HPX main exited with non-zero status");
240

241
    return hpx::util::report_errors();
1✔
242
}
×
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