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

STEllAR-GROUP / hpx / #857

28 Dec 2022 11:12PM UTC coverage: 86.543% (-0.06%) from 86.602%
#857

push

StellarBot
Merge #6118

6118: Modernize modules from level 17, 18, 19, and 20 r=hkaiser a=hkaiser

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

Modules:
- core/threading_base
- full/command_line_handling
- core/io_service
- core/schedulers
- core/synchronization
- core/futures
- core/thread_pools
- core/lcos_local
- core/pack_traversal
- core/resource_partitioner
- core/threading
- full/naming_base


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

849 of 849 new or added lines in 98 files covered. (100.0%)

174389 of 201505 relevant lines covered (86.54%)

1916353.25 hits per line

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

58.02
/libs/core/threading/src/thread.cpp
1
//  Copyright (c) 2007-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
#include <hpx/assert.hpp>
8
#include <hpx/functional/bind.hpp>
9
#include <hpx/functional/bind_front.hpp>
10
#include <hpx/functional/move_only_function.hpp>
11
#include <hpx/futures/detail/future_data.hpp>
12
#include <hpx/futures/future.hpp>
13
#include <hpx/lock_registration/detail/register_locks.hpp>
14
#include <hpx/modules/errors.hpp>
15
#include <hpx/modules/memory.hpp>
16
#include <hpx/modules/threading.hpp>
17
#include <hpx/thread_support/unlock_guard.hpp>
18
#include <hpx/threading_base/thread_helpers.hpp>
19
#include <hpx/threading_base/thread_init_data.hpp>
20
#include <hpx/threading_base/thread_pool_base.hpp>
21
#include <hpx/timing/steady_clock.hpp>
22

23
#include <cstddef>
24
#include <exception>
25
#include <functional>
26
#include <mutex>
27
#include <utility>
28

29
#if defined(__ANDROID__) || defined(ANDROID)
30
#include <cpu-features.h>
31
#endif
32

33
namespace hpx {
34

35
    namespace detail {
36

37
        static thread_termination_handler_type thread_termination_handler;
1,251✔
38
    }
39

40
    void set_thread_termination_handler(thread_termination_handler_type f)
1,221✔
41
    {
42
        detail::thread_termination_handler = HPX_MOVE(f);
1,221✔
43
    }
1,221✔
44

45
    thread::thread() noexcept
5✔
46
      : id_(hpx::threads::invalid_thread_id)
5✔
47
    {
48
    }
5✔
49

50
    thread::thread(thread&& rhs) noexcept
203✔
51
    {
52
        std::lock_guard l(rhs.mtx_);
203✔
53
        id_ = rhs.id_;
203✔
54
        rhs.id_ = threads::invalid_thread_id;
203✔
55
    }
203✔
56

57
    thread& thread::operator=(thread&& rhs) noexcept
5✔
58
    {
59
        std::unique_lock l(mtx_);
5✔
60
        std::unique_lock l2(rhs.mtx_);
5✔
61
        if (joinable_locked())
5✔
62
        {
63
            l2.unlock();
×
64
            l.unlock();
×
65
            HPX_THROW_EXCEPTION(hpx::error::invalid_status,
×
66
                "thread::operator=", "destroying running thread");
67
        }
68
        id_ = rhs.id_;
5✔
69
        rhs.id_ = threads::invalid_thread_id;
5✔
70
        return *this;
71
    }
5✔
72

73
    thread::~thread()
1,343✔
74
    {
75
        if (joinable())
1,343✔
76
        {
77
            if (detail::thread_termination_handler)
×
78
            {
79
                try
80
                {
81
                    HPX_THROW_EXCEPTION(hpx::error::invalid_status,
×
82
                        "thread::~thread", "destroying running thread");
83
                }
×
84
                catch (...)
85
                {
86
                    detail::thread_termination_handler(
×
87
                        std::current_exception());
×
88
                }
×
89
            }
×
90
            else
91
            {
92
                std::terminate();
×
93
            }
94
        }
×
95

96
        HPX_ASSERT(id_ == threads::invalid_thread_id);
1,343✔
97
    }
1,343✔
98

99
    void thread::swap(thread& rhs) noexcept
2✔
100
    {
101
        std::lock_guard l(mtx_);
2✔
102
        std::lock_guard l2(rhs.mtx_);
2✔
103
        std::swap(id_, rhs.id_);
2✔
104
    }
2✔
105

106
    static void run_thread_exit_callbacks()
1,135✔
107
    {
108
        threads::thread_id_type id = threads::get_self_id();
1,135✔
109
        if (id == threads::invalid_thread_id)
1,135✔
110
        {
111
            HPX_THROW_EXCEPTION(hpx::error::null_thread_id,
×
112
                "run_thread_exit_callbacks", "null thread id encountered");
113
        }
114
        threads::run_thread_exit_callbacks(id);
1,135✔
115
        threads::free_thread_exit_callbacks(id);
1,135✔
116
    }
1,135✔
117

118
    threads::thread_result_type thread::thread_function_nullary(
1,135✔
119
        hpx::move_only_function<void()> const& func)
120
    {
121
        try
122
        {
123
            // Now notify our calling thread that we started execution.
124
            func();
1,135✔
125
        }
1,135✔
126
        catch (hpx::thread_interrupted const&)
127
        {    //-V565
128
            /* swallow this exception */
129
        }
1✔
130
        catch (hpx::exception const&)
131
        {
132
            // Verify that there are no more registered locks for this
133
            // OS-thread. This will throw if there are still any locks held.
134
            util::force_error_on_lock();
×
135

136
            // run all callbacks attached to the exit event for this thread
137
            run_thread_exit_callbacks();
×
138

139
            throw;    // rethrow any exception except 'thread_interrupted'
×
140
        }
1✔
141

142
        // Verify that there are no more registered locks for this OS-thread.
143
        // This will throw if there are still any locks held.
144
        util::force_error_on_lock();
1,135✔
145

146
        // run all callbacks attached to the exit event for this thread
147
        run_thread_exit_callbacks();
1,135✔
148

149
        return threads::thread_result_type(
1,135✔
150
            threads::thread_schedule_state::terminated,
1,135✔
151
            threads::invalid_thread_id);
152
    }
1✔
153

154
    thread::id thread::get_id() const noexcept
86✔
155
    {
156
        return id(native_handle());
86✔
157
    }
158

159
    unsigned int thread::hardware_concurrency() noexcept
2✔
160
    {
161
        return hpx::threads::hardware_concurrency();
2✔
162
    }
163

164
    void thread::start_thread(
1,135✔
165
        threads::thread_pool_base* pool, hpx::move_only_function<void()>&& func)
166
    {
167
        HPX_ASSERT(pool);
1,135✔
168

169
        threads::thread_init_data data(
1,135✔
170
            util::one_shot(
1,135✔
171
                hpx::bind(&thread::thread_function_nullary, HPX_MOVE(func))),
1,135✔
172
            "thread::thread_function_nullary",
1,135✔
173
            threads::thread_priority::default_, threads::thread_schedule_hint(),
1,135✔
174
            threads::thread_stacksize::default_,
175
            threads::thread_schedule_state::pending, true);
176

177
        // create the new thread, note that id_ is guaranteed to be valid before
178
        // the thread function is executed
179
        error_code ec(throwmode::lightweight);
1,135✔
180
        pool->create_thread(data, id_, ec);
1,135✔
181
        if (ec)
1,135✔
182
        {
183
            HPX_THROW_EXCEPTION(hpx::error::thread_resource_error,
×
184
                "thread::start_thread", "Could not create thread");
185
            return;
186
        }
187
    }
1,135✔
188

189
    static void resume_thread(threads::thread_id_ref_type const& id)
385✔
190
    {
191
        threads::set_thread_state(
385✔
192
            id.noref(), threads::thread_schedule_state::pending);
385✔
193
    }
385✔
194

195
    void thread::join()
1,135✔
196
    {
197
        std::unique_lock l(mtx_);
1,135✔
198

199
        if (!joinable_locked())
1,135✔
200
        {
201
            l.unlock();
1✔
202
            HPX_THROW_EXCEPTION(hpx::error::invalid_status, "thread::join",
1✔
203
                "trying to join a non joinable thread");
204
        }
205

206
        // keep ourselves alive while being suspended
207
        threads::thread_id_ref_type this_id = threads::get_self_id();
1,134✔
208
        if (this_id == id_)
1,134✔
209
        {
210
            l.unlock();
×
211
            HPX_THROW_EXCEPTION(hpx::error::thread_resource_error,
×
212
                "thread::join", "hpx::thread: trying joining itself");
213
            return;
214
        }
215
        this_thread::interruption_point();
1,134✔
216

217
        // register callback function to be called when thread exits
218
        if (threads::add_thread_exit_callback(id_.noref(),
2,268✔
219
                hpx::bind_front(&resume_thread, HPX_MOVE(this_id))))
1,134✔
220
        {
221
            // wait for thread to be terminated
222
            unlock_guard ul(l);
385✔
223
            this_thread::suspend(
385✔
224
                threads::thread_schedule_state::suspended, "thread::join");
385✔
225
        }
385✔
226

227
        detach_locked();    // invalidate this object
1,134✔
228
    }
1,136✔
229

230
    // extensions
231
    void thread::interrupt(bool flag)
4✔
232
    {
233
        threads::interrupt_thread(native_handle(), flag);
4✔
234
    }
4✔
235

236
    bool thread::interruption_requested() const
×
237
    {
238
        return threads::get_thread_interruption_requested(native_handle());
×
239
    }
240

241
    void thread::interrupt(thread::id id, bool flag)
2✔
242
    {
243
        threads::interrupt_thread(id.id_, flag);
2✔
244
    }
2✔
245

246
    std::size_t thread::get_thread_data() const
×
247
    {
248
        return threads::get_thread_data(native_handle());
×
249
    }
250
    std::size_t thread::set_thread_data(std::size_t data)
×
251
    {
252
        return threads::set_thread_data(native_handle(), data);
×
253
    }
254

255
#if defined(HPX_HAVE_LIBCDS)
256
    std::size_t thread::get_libcds_data() const
257
    {
258
        return threads::get_libcds_data(native_handle());
259
    }
260
    std::size_t thread::set_libcds_data(std::size_t data)
261
    {
262
        return threads::set_libcds_data(native_handle(), data);
263
    }
264

265
    std::size_t thread::get_libcds_hazard_pointer_data() const
266
    {
267
        return threads::get_libcds_hazard_pointer_data(native_handle());
268
    }
269
    std::size_t thread::set_libcds_hazard_pointer_data(std::size_t data)
270
    {
271
        return threads::set_libcds_hazard_pointer_data(native_handle(), data);
272
    }
273

274
    std::size_t thread::get_libcds_dynamic_hazard_pointer_data() const
275
    {
276
        return threads::get_libcds_dynamic_hazard_pointer_data(native_handle());
277
    }
278
    std::size_t thread::set_libcds_dynamic_hazard_pointer_data(std::size_t data)
279
    {
280
        return threads::set_libcds_dynamic_hazard_pointer_data(
281
            native_handle(), data);
282
    }
283
#endif
284

285
    ///////////////////////////////////////////////////////////////////////////
286
    namespace detail {
287

288
        struct thread_task_base : lcos::detail::future_data<void>
×
289
        {
290
        private:
291
            using future_base_type = hpx::intrusive_ptr<thread_task_base>;
292

293
        protected:
294
            using base_type = lcos::detail::future_data<void>;
295
            using result_type = base_type::result_type;
296

297
            using base_type::mtx_;
298

299
        public:
300
            thread_task_base(threads::thread_id_ref_type const& id)
×
301
            {
×
302
                if (threads::add_thread_exit_callback(id.noref(),
×
303
                        hpx::bind_front(&thread_task_base::thread_exit_function,
×
304
                            future_base_type(this))))
×
305
                {
306
                    id_ = id;
×
307
                }
×
308
            }
×
309

310
            bool valid() const noexcept
×
311
            {
312
                return id_ != threads::invalid_thread_id;
×
313
            }
314

315
            // cancellation support
316
            bool cancelable() const noexcept override
×
317
            {
318
                return true;
×
319
            }
320

321
            void cancel() override
×
322
            {
323
                std::lock_guard l(mtx_);
×
324
                if (!this->is_ready())
×
325
                {
326
                    threads::interrupt_thread(id_.noref());
×
327
                    this->set_error(hpx::error::thread_cancelled,
×
328
                        "thread_task_base::cancel", "future has been canceled");
329
                    id_ = threads::invalid_thread_id;
×
330
                }
×
331
            }
×
332

333
        protected:
334
            void thread_exit_function()
×
335
            {
336
                // might have been finished or canceled
337
                std::lock_guard l(mtx_);
×
338
                if (!this->is_ready())
×
339
                    this->set_data(result_type());
×
340
                id_ = threads::invalid_thread_id;
×
341
            }
×
342

343
        private:
344
            threads::thread_id_ref_type id_;
345
        };
346
    }    // namespace detail
347

348
    hpx::future<void> thread::get_future(error_code& ec)
×
349
    {
350
        if (id_ == threads::invalid_thread_id)
×
351
        {
352
            HPX_THROWS_IF(ec, hpx::error::null_thread_id, "thread::get_future",
×
353
                "null thread id encountered");
354
            return hpx::future<void>();
×
355
        }
356

357
        detail::thread_task_base* p = new detail::thread_task_base(id_);
×
358
        hpx::intrusive_ptr<lcos::detail::future_data<void>> base(p);
×
359
        if (!p->valid())
×
360
        {
361
            HPX_THROWS_IF(ec, hpx::error::thread_resource_error,
×
362
                "thread::get_future",
363
                "Could not create future as thread has been terminated.");
364
            return hpx::future<void>();
×
365
        }
366

367
        using traits::future_access;
368
        return future_access<hpx::future<void>>::create(HPX_MOVE(base));
×
369
    }
×
370

371
    ///////////////////////////////////////////////////////////////////////////
372
    namespace this_thread {
373

374
        void yield_to(thread::id id) noexcept
2,535✔
375
        {
376
            this_thread::suspend(threads::thread_schedule_state::pending,
2,535✔
377
                id.native_handle(), "this_thread::yield_to");
2,535✔
378
        }
2,535✔
379

380
        void yield() noexcept
1,641,622✔
381
        {
382
            this_thread::suspend(
1,641,622✔
383
                threads::thread_schedule_state::pending, "this_thread::yield");
1,641,622✔
384
        }
1,641,756✔
385

386
        thread::id get_id() noexcept
14,468✔
387
        {
388
            return thread::id(threads::get_self_id());
14,468✔
389
        }
390

391
        // extensions
392
        threads::thread_priority get_priority() noexcept
18✔
393
        {
394
            return threads::get_thread_priority(threads::get_self_id());
18✔
395
        }
396

397
        std::ptrdiff_t get_stack_size() noexcept
1,585,395✔
398
        {
399
            return threads::get_stack_size(threads::get_self_id());
1,585,395✔
400
        }
401

402
        void interruption_point()
1,136✔
403
        {
404
            threads::interruption_point(threads::get_self_id());
1,136✔
405
        }
1,136✔
406

407
        bool interruption_enabled()
1✔
408
        {
409
            return threads::get_thread_interruption_enabled(
1✔
410
                threads::get_self_id());
1✔
411
        }
412

413
        bool interruption_requested()
×
414
        {
415
            return threads::get_thread_interruption_requested(
×
416
                threads::get_self_id());
×
417
        }
418

419
        void interrupt()
×
420
        {
421
            threads::interrupt_thread(threads::get_self_id());
×
422
            threads::interruption_point(threads::get_self_id());
×
423
        }
×
424

425
        void sleep_until(hpx::chrono::steady_time_point const& abs_time)
2,578✔
426
        {
427
            this_thread::suspend(abs_time, "this_thread::sleep_until");
2,578✔
428
        }
2,578✔
429

430
        std::size_t get_thread_data()
×
431
        {
432
            return threads::get_thread_data(threads::get_self_id());
×
433
        }
434

435
        std::size_t set_thread_data(std::size_t data)
×
436
        {
437
            return threads::set_thread_data(threads::get_self_id(), data);
×
438
        }
439

440
#if defined(HPX_HAVE_LIBCDS)
441
        std::size_t get_libcds_data()
442
        {
443
            return threads::get_libcds_data(threads::get_self_id());
444
        }
445

446
        std::size_t set_libcds_data(std::size_t data)
447
        {
448
            return threads::set_libcds_data(threads::get_self_id(), data);
449
        }
450

451
        std::size_t get_libcds_hazard_pointer_data()
452
        {
453
            return threads::get_libcds_hazard_pointer_data(
454
                threads::get_self_id());
455
        }
456

457
        std::size_t set_libcds_hazard_pointer_data(std::size_t data)
458
        {
459
            return threads::set_libcds_hazard_pointer_data(
460
                threads::get_self_id(), data);
461
        }
462

463
        std::size_t get_libcds_dynamic_hazard_pointer_data()
464
        {
465
            return threads::get_libcds_dynamic_hazard_pointer_data(
466
                threads::get_self_id());
467
        }
468

469
        std::size_t set_libcds_dynamic_hazard_pointer_data(std::size_t data)
470
        {
471
            return threads::set_libcds_dynamic_hazard_pointer_data(
472
                threads::get_self_id(), data);
473
        }
474
#endif
475
        ///////////////////////////////////////////////////////////////////////
476
        disable_interruption::disable_interruption()
1✔
477
          : interruption_was_enabled_(interruption_enabled())
1✔
478
        {
479
            if (interruption_was_enabled_)
1✔
480
            {
481
                interruption_was_enabled_ =
1✔
482
                    threads::set_thread_interruption_enabled(
1✔
483
                        threads::get_self_id(), false);
1✔
484
            }
1✔
485
        }
1✔
486

487
        disable_interruption::~disable_interruption()
1✔
488
        {
489
            threads::thread_self* p = threads::get_self_ptr();
1✔
490
            if (p)
1✔
491
            {
492
                threads::set_thread_interruption_enabled(
1✔
493
                    threads::get_self_id(), interruption_was_enabled_);
1✔
494
            }
1✔
495
        }
1✔
496

497
        ///////////////////////////////////////////////////////////////////////
498
        restore_interruption::restore_interruption(disable_interruption& d)
×
499
          : interruption_was_enabled_(d.interruption_was_enabled_)
×
500
        {
501
            if (!interruption_was_enabled_)
×
502
            {
503
                interruption_was_enabled_ =
×
504
                    threads::set_thread_interruption_enabled(
×
505
                        threads::get_self_id(), true);
×
506
            }
×
507
        }
×
508

509
        restore_interruption::~restore_interruption()
×
510
        {
511
            threads::thread_self* p = threads::get_self_ptr();
×
512
            if (p)
×
513
            {
514
                threads::set_thread_interruption_enabled(
×
515
                    threads::get_self_id(), interruption_was_enabled_);
×
516
            }
×
517
        }
×
518
    }    // namespace this_thread
519
}    // namespace hpx
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