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

STEllAR-GROUP / hpx / #882

31 Aug 2023 07:44PM UTC coverage: 41.798% (-44.7%) from 86.546%
#882

push

19442 of 46514 relevant lines covered (41.8%)

126375.38 hits per line

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

31.74
/libs/core/threading/src/thread.cpp
1
//  Copyright (c) 2007-2025 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/modules/errors.hpp>
9
#include <hpx/modules/functional.hpp>
10
#include <hpx/modules/futures.hpp>
11
#include <hpx/modules/lock_registration.hpp>
12
#include <hpx/modules/memory.hpp>
13
#include <hpx/modules/thread_support.hpp>
14
#include <hpx/modules/threading.hpp>
15
#include <hpx/modules/threading_base.hpp>
16
#include <hpx/modules/timing.hpp>
17

18
#include <cstddef>
19
#include <exception>
20
#include <functional>
21
#include <mutex>
22
#include <utility>
23

24
#if defined(__ANDROID__) || defined(ANDROID)
25
#include <cpu-features.h>
26
#endif
27

28
namespace hpx {
29

30
    namespace detail {
31

32
        static thread_termination_handler_type thread_termination_handler;
33
    }
34

35
    void set_thread_termination_handler(thread_termination_handler_type f)
36
    {
37
        detail::thread_termination_handler = HPX_MOVE(f);
38
    }
39

40
    thread::thread() noexcept
64✔
41
      : id_(hpx::threads::invalid_thread_id)
42
    {
43
    }
64✔
44

45
    thread::thread(thread&& rhs) noexcept
×
46
    {
47
        std::lock_guard l(rhs.mtx_);
48
        id_ = rhs.id_;
×
49
        rhs.id_ = threads::invalid_thread_id;
50
    }
7✔
51

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

68
    thread::~thread()
69
    {
70
        if (joinable())
×
71
        {
72
            if (detail::thread_termination_handler)
73
            {
19✔
74
                try
75
                {
19✔
76
                    HPX_THROW_EXCEPTION(hpx::error::invalid_status,
77
                        "thread::~thread", "destroying running thread");
×
78
                }
79
                catch (...)
80
                {
81
                    detail::thread_termination_handler(
×
82
                        std::current_exception());
83
                }
84
            }
×
85
            else
86
            {
87
                std::terminate();
×
88
            }
×
89
        }
90

91
        HPX_ASSERT(id_ == threads::invalid_thread_id);
92
    }
×
93

94
    void thread::swap(thread& rhs) noexcept
95
    {
96
        std::lock_guard l(mtx_);
97
        std::lock_guard l2(rhs.mtx_);
19✔
98
        std::swap(id_, rhs.id_);
99
    }
×
100

101
    static void run_thread_exit_callbacks()
×
102
    {
×
103
        threads::thread_id_type id = threads::get_self_id();
104
        if (id == threads::invalid_thread_id)
×
105
        {
106
            HPX_THROW_EXCEPTION(hpx::error::null_thread_id,
12✔
107
                "run_thread_exit_callbacks", "null thread id encountered");
108
        }
12✔
109
        threads::run_thread_exit_callbacks(id);
12✔
110
        threads::free_thread_exit_callbacks(id);
111
    }
×
112

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

132
            // run all callbacks attached to the exit event for this thread
133
            run_thread_exit_callbacks();
134

135
            throw;    // rethrow any exception except 'thread_interrupted'
136
        }
137

×
138
        // Verify that there are no more registered locks for this OS-thread.
139
        // This will throw if there are still any locks held.
×
140
        util::force_error_on_lock();
×
141

142
        // run all callbacks attached to the exit event for this thread
143
        run_thread_exit_callbacks();
144

145
        return threads::thread_result_type(
146
            threads::thread_schedule_state::terminated,
147
            threads::invalid_thread_id);
12✔
148
    }
149

150
    thread::id thread::get_id() const noexcept
151
    {
12✔
152
        return id(native_handle());
153
    }
154

×
155
    unsigned int thread::hardware_concurrency() noexcept
156
    {
×
157
        return hpx::threads::hardware_concurrency();
158
    }
159

×
160
    void thread::start_thread(
161
        threads::thread_pool_base* pool, hpx::move_only_function<void()>&& func)
×
162
    {
163
        HPX_ASSERT(pool);
164

12✔
165
        threads::thread_init_data data(
166
            util::one_shot(
167
                hpx::bind(&thread::thread_function_nullary, HPX_MOVE(func))),
168
            "thread::thread_function_nullary",
169
            threads::thread_priority::default_, threads::thread_schedule_hint(),
170
            threads::thread_stacksize::default_,
12✔
171
            threads::thread_schedule_state::pending, true);
12✔
172

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

185
    static void resume_thread(threads::thread_id_ref_type const& id)
186
    {
187
        threads::set_thread_state(
188
            id.noref(), threads::thread_schedule_state::pending);
189
    }
9✔
190

191
    void thread::join()
9✔
192
    {
9✔
193
        std::unique_lock l(mtx_);
9✔
194

195
        if (!joinable_locked())
12✔
196
        {
197
            l.unlock();
12✔
198
            HPX_THROW_EXCEPTION(hpx::error::invalid_status, "thread::join",
199
                "trying to join a non joinable thread");
12✔
200
        }
201

×
202
        // keep ourselves alive while being suspended
×
203
        threads::thread_id_ref_type this_id = threads::get_self_id();
204
        if (this_id == id_)
205
        {
206
            l.unlock();
207
            HPX_THROW_EXCEPTION(hpx::error::thread_resource_error,
24✔
208
                "thread::join", "hpx::thread: trying joining itself");
12✔
209
            return;
210
        }
×
211
        this_thread::interruption_point();
×
212

213
        // register callback function to be called when thread exits
214
        if (threads::add_thread_exit_callback(id_.noref(),
215
                hpx::bind_front(&resume_thread, HPX_MOVE(this_id))))
12✔
216
        {
217
            // wait for thread to be terminated
218
            unlock_guard ul(l);
12✔
219
            this_thread::suspend(
12✔
220
                threads::thread_schedule_state::suspended, "thread::join");
221
        }
222

223
        detach_locked();    // invalidate this object
9✔
224
    }
225

226
    // extensions
227
    void thread::interrupt(bool flag)
228
    {
229
        threads::interrupt_thread(native_handle(), flag);
230
    }
231

×
232
    bool thread::interruption_requested() const
233
    {
×
234
        return threads::get_thread_interruption_requested(native_handle());
×
235
    }
236

×
237
    void thread::interrupt(thread::id id, bool flag)
238
    {
×
239
        threads::interrupt_thread(id.id_, flag);
240
    }
241

×
242
    std::size_t thread::get_thread_data() const
243
    {
×
244
        return threads::get_thread_data(native_handle());
×
245
    }
246
    std::size_t thread::set_thread_data(std::size_t data)
×
247
    {
248
        return threads::set_thread_data(native_handle(), data);
×
249
    }
250

×
251
#if defined(HPX_HAVE_LIBCDS)
252
    std::size_t thread::get_libcds_data() const
×
253
    {
254
        return threads::get_libcds_data(native_handle());
255
    }
256
    std::size_t thread::set_libcds_data(std::size_t data)
257
    {
258
        return threads::set_libcds_data(native_handle(), data);
259
    }
260

261
    std::size_t thread::get_libcds_hazard_pointer_data() const
262
    {
263
        return threads::get_libcds_hazard_pointer_data(native_handle());
264
    }
265
    std::size_t thread::set_libcds_hazard_pointer_data(std::size_t data)
266
    {
267
        return threads::set_libcds_hazard_pointer_data(native_handle(), data);
268
    }
269

270
    std::size_t thread::get_libcds_dynamic_hazard_pointer_data() const
271
    {
272
        return threads::get_libcds_dynamic_hazard_pointer_data(native_handle());
273
    }
274
    std::size_t thread::set_libcds_dynamic_hazard_pointer_data(std::size_t data)
275
    {
276
        return threads::set_libcds_dynamic_hazard_pointer_data(
277
            native_handle(), data);
278
    }
279
#endif
280

281
    ///////////////////////////////////////////////////////////////////////////
282
    namespace detail {
283

284
        struct thread_task_base : lcos::detail::future_data<void>
285
        {
286
        private:
287
            using future_base_type = hpx::intrusive_ptr<thread_task_base>;
288

289
        protected:
290
            using base_type = lcos::detail::future_data<void>;
291
            using result_type = base_type::result_type;
292

293
            using base_type::mtx_;
294

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

306
            bool valid() const noexcept
307
            {
308
                return id_ != threads::invalid_thread_id;
×
309
            }
310

311
            // cancellation support
312
            bool cancelable() const noexcept override
313
            {
314
                return true;
315
            }
316

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

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

×
339
        private:
×
340
            threads::thread_id_ref_type id_;
341
        };
×
342
    }    // namespace detail
343

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

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

363
        using traits::future_access;
364
        return future_access<hpx::future<void>>::create(HPX_MOVE(base));
×
365
    }
366

367
    ///////////////////////////////////////////////////////////////////////////
368
    namespace this_thread {
369

370
        void yield_to(thread::id id) noexcept
371
        {
372
            this_thread::suspend(threads::thread_schedule_state::pending,
373
                id.native_handle(), "this_thread::yield_to");
374
        }
5✔
375

376
        void yield() noexcept
5✔
377
        {
378
            this_thread::suspend(
5✔
379
                threads::thread_schedule_state::pending, "this_thread::yield");
380
        }
1,072,510✔
381

382
        thread::id get_id() noexcept
1,072,510✔
383
        {
384
            return thread::id(threads::get_self_id());
1,072,510✔
385
        }
386

5✔
387
        // extensions
388
        threads::thread_priority get_priority() noexcept
5✔
389
        {
390
            return threads::get_thread_priority(threads::get_self_id());
391
        }
392

3✔
393
        std::ptrdiff_t get_stack_size() noexcept
394
        {
3✔
395
            return threads::get_stack_size(threads::get_self_id());
396
        }
397

41,751✔
398
        void interruption_point()
399
        {
41,751✔
400
            threads::interruption_point(threads::get_self_id());
401
        }
402

12✔
403
        bool interruption_enabled()
404
        {
12✔
405
            return threads::get_thread_interruption_enabled(
12✔
406
                threads::get_self_id());
407
        }
×
408

409
        bool interruption_requested()
×
410
        {
×
411
            return threads::get_thread_interruption_requested(
412
                threads::get_self_id());
413
        }
×
414

415
        void interrupt()
×
416
        {
×
417
            threads::interrupt_thread(threads::get_self_id());
418
            threads::interruption_point(threads::get_self_id());
419
        }
×
420

421
        void sleep_until(hpx::chrono::steady_time_point const& abs_time)
×
422
        {
×
423
            this_thread::suspend(abs_time, "this_thread::sleep_until");
×
424
        }
425

129✔
426
        std::size_t get_thread_data()
427
        {
129✔
428
            return threads::get_thread_data(threads::get_self_id());
129✔
429
        }
430

×
431
        std::size_t set_thread_data(std::size_t data)
432
        {
×
433
            return threads::set_thread_data(threads::get_self_id(), data);
434
        }
435

×
436
#if defined(HPX_HAVE_LIBCDS)
437
        std::size_t get_libcds_data()
×
438
        {
439
            return threads::get_libcds_data(threads::get_self_id());
440
        }
441

442
        std::size_t set_libcds_data(std::size_t data)
443
        {
444
            return threads::set_libcds_data(threads::get_self_id(), data);
445
        }
446

447
        std::size_t get_libcds_hazard_pointer_data()
448
        {
449
            return threads::get_libcds_hazard_pointer_data(
450
                threads::get_self_id());
451
        }
452

453
        std::size_t set_libcds_hazard_pointer_data(std::size_t data)
454
        {
455
            return threads::set_libcds_hazard_pointer_data(
456
                threads::get_self_id(), data);
457
        }
458

459
        std::size_t get_libcds_dynamic_hazard_pointer_data()
460
        {
461
            return threads::get_libcds_dynamic_hazard_pointer_data(
462
                threads::get_self_id());
463
        }
464

465
        std::size_t set_libcds_dynamic_hazard_pointer_data(std::size_t data)
466
        {
467
            return threads::set_libcds_dynamic_hazard_pointer_data(
468
                threads::get_self_id(), data);
469
        }
470
#endif
471
        ///////////////////////////////////////////////////////////////////////
472
        disable_interruption::disable_interruption()
473
          : interruption_was_enabled_(interruption_enabled())
474
        {
475
            if (interruption_was_enabled_)
476
            {
×
477
                interruption_was_enabled_ =
×
478
                    threads::set_thread_interruption_enabled(
479
                        threads::get_self_id(), false);
×
480
            }
481
        }
×
482

×
483
        disable_interruption::~disable_interruption()
×
484
        {
485
            threads::thread_self* p = threads::get_self_ptr();
×
486
            if (p)
487
            {
×
488
                threads::set_thread_interruption_enabled(
489
                    threads::get_self_id(), interruption_was_enabled_);
×
490
            }
×
491
        }
492

×
493
        ///////////////////////////////////////////////////////////////////////
×
494
        restore_interruption::restore_interruption(disable_interruption& d)
495
          : interruption_was_enabled_(d.interruption_was_enabled_)
×
496
        {
497
            if (!interruption_was_enabled_)
498
            {
×
499
                interruption_was_enabled_ =
×
500
                    threads::set_thread_interruption_enabled(
501
                        threads::get_self_id(), true);
×
502
            }
503
        }
×
504

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