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

STEllAR-GROUP / hpx / #862

10 Jan 2023 05:30PM UTC coverage: 86.582% (-0.05%) from 86.634%
#862

push

StellarBot
Merge #6130

6130: Remove the mutex lock in the critical path of get_partitioner. r=hkaiser a=JiakunYan

Remove the mutex lock in the critical path of hpx::resource::detail::get_partitioner.

The protected variable `partitioner_ref` is only set once during initialization.

Co-authored-by: Jiakun Yan <jiakunyan1998@gmail.com>

6 of 6 new or added lines in 1 file covered. (100.0%)

174767 of 201851 relevant lines covered (86.58%)

2069816.07 hits per line

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

66.6
/libs/core/command_line_handling_local/src/command_line_handling_local.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/config.hpp>
8
#include <hpx/assert.hpp>
9
#include <hpx/command_line_handling_local/command_line_handling_local.hpp>
10
#include <hpx/command_line_handling_local/parse_command_line_local.hpp>
11
#include <hpx/functional/detail/reset_function.hpp>
12
#include <hpx/modules/asio.hpp>
13
#include <hpx/modules/debugging.hpp>
14
#include <hpx/modules/format.hpp>
15
#include <hpx/modules/program_options.hpp>
16
#include <hpx/modules/runtime_configuration.hpp>
17
#include <hpx/modules/string_util.hpp>
18
#include <hpx/modules/topology.hpp>
19
#include <hpx/modules/util.hpp>
20
#include <hpx/preprocessor/stringize.hpp>
21
#include <hpx/type_support/unused.hpp>
22
#include <hpx/util/from_string.hpp>
23
#include <hpx/version.hpp>
24

25
#include <algorithm>
26
#include <cstddef>
27
#include <cstdint>
28
#include <fstream>
29
#include <iomanip>
30
#include <iostream>
31
#include <iterator>
32
#include <memory>
33
#include <sstream>
34
#include <string>
35
#include <utility>
36
#include <vector>
37

38
namespace hpx::local::detail {
39

40
    std::string runtime_configuration_string(command_line_handling const& cfg)
×
41
    {
42
        std::ostringstream strm;
×
43

44
        // default scheduler used for this run
45
        strm << "  {scheduler}: " << cfg.queuing_ << "\n";
×
46

47
        // amount of threads and cores configured for this run
48
        strm << "  {os-threads}: " << cfg.num_threads_ << "\n";
×
49
        strm << "  {cores}: " << cfg.num_cores_ << "\n";
×
50

51
        return strm.str();
×
52
    }
×
53

54
    ///////////////////////////////////////////////////////////////////////
55
    int print_version(std::ostream& out)
×
56
    {
57
        out << std::endl << hpx::copyright() << std::endl;
×
58
        out << hpx::complete_version() << std::endl;
×
59
        return 1;
×
60
    }
×
61

62
    int print_info(std::ostream& out, command_line_handling const& cfg)
×
63
    {
64
        out << "Static configuration:\n---------------------\n";
×
65
        out << hpx::configuration_string() << std::endl;
×
66

67
        out << "Runtime configuration:\n----------------------\n";
×
68
        out << runtime_configuration_string(cfg) << std::endl;
×
69

70
        return 1;
×
71
    }
×
72

73
    ///////////////////////////////////////////////////////////////////////
74
    inline void encode(
6,028✔
75
        std::string& str, char s, char const* r, std::size_t inc = 1ull)
76
    {
77
        std::string::size_type pos = 0;
6,028✔
78
        while ((pos = str.find_first_of(s, pos)) != std::string::npos)
6,028✔
79
        {
80
            str.replace(pos, 1, r);
×
81
            pos += inc;
×
82
        }
83
    }
6,028✔
84

85
    inline std::string encode_string(std::string str)
×
86
    {
87
        encode(str, '\n', "\\n");
×
88
        return str;
×
89
    }
90

91
    inline std::string encode_and_enquote(std::string str)
6,028✔
92
    {
93
        encode(str, '\"', "\\\"", 2);
6,028✔
94
        return enquote(HPX_MOVE(str));
6,028✔
95
    }
×
96

97
    ///////////////////////////////////////////////////////////////////////
98
    std::string convert_to_log_file(std::string const& dest)
9,568✔
99
    {
100
        if (dest.empty())
9,568✔
101
            return "cout";
×
102

103
        if (dest == "cout" || dest == "cerr" || dest == "console")
9,568✔
104
            return dest;
×
105
#if defined(ANDROID) || defined(__ANDROID__)
106
        if (dest == "android_log")
107
            return dest;
108
#endif
109
        // everything else is assumed to be a file name
110
        return "file(" + dest + ")";
9,568✔
111
    }
9,568✔
112

113
    std::string handle_queuing(util::manage_config& cfgmap,
2,446✔
114
        hpx::program_options::variables_map& vm, std::string const& default_)
115
    {
116
        // command line options is used preferred
117
        if (vm.count("hpx:queuing"))
2,446✔
118
            return vm["hpx:queuing"].as<std::string>();
16✔
119

120
        // use either cfgmap value or default
121
        return cfgmap.get_value<std::string>("hpx.scheduler", default_);
2,430✔
122
    }
2,446✔
123

124
    std::string handle_affinity(util::manage_config& cfgmap,
2,446✔
125
        hpx::program_options::variables_map& vm, std::string const& default_)
126
    {
127
        // command line options is used preferred
128
        if (vm.count("hpx:affinity"))
2,446✔
129
            return vm["hpx:affinity"].as<std::string>();
×
130

131
        // use either cfgmap value or default
132
        return cfgmap.get_value<std::string>("hpx.affinity", default_);
2,446✔
133
    }
2,446✔
134

135
    std::string handle_affinity_bind(util::manage_config& cfgmap,
2,446✔
136
        hpx::program_options::variables_map& vm, std::string const& default_)
137
    {
138
        // command line options is used preferred
139
        if (vm.count("hpx:bind"))
2,446✔
140
        {
141
            std::string affinity_desc;
×
142

143
            std::vector<std::string> bind_affinity =
144
                vm["hpx:bind"].as<std::vector<std::string>>();
×
145
            for (std::string const& s : bind_affinity)
×
146
            {
147
                if (!affinity_desc.empty())
×
148
                    affinity_desc += ";";
×
149
                affinity_desc += s;
×
150
            }
151

152
            return affinity_desc;
×
153
        }
×
154

155
        // use either cfgmap value or default
156
        return cfgmap.get_value<std::string>("hpx.bind", default_);
2,446✔
157
    }
2,446✔
158

159
    std::size_t handle_pu_step(util::manage_config& cfgmap,
2,446✔
160
        hpx::program_options::variables_map& vm, std::size_t default_)
161
    {
162
        // command line options is used preferred
163
        if (vm.count("hpx:pu-step"))
2,446✔
164
            return vm["hpx:pu-step"].as<std::size_t>();
×
165

166
        // use either cfgmap value or default
167
        return cfgmap.get_value<std::size_t>("hpx.pu_step", default_);
2,446✔
168
    }
2,446✔
169

170
    std::size_t handle_pu_offset(util::manage_config& cfgmap,
2,446✔
171
        hpx::program_options::variables_map& vm, std::size_t default_)
172
    {
173
        // command line options is used preferred
174
        if (vm.count("hpx:pu-offset"))
2,446✔
175
            return vm["hpx:pu-offset"].as<std::size_t>();
×
176

177
        // use either cfgmap value or default
178
        return cfgmap.get_value<std::size_t>("hpx.pu_offset", default_);
2,446✔
179
    }
2,446✔
180

181
    std::size_t handle_numa_sensitive(util::manage_config& cfgmap,
2,446✔
182
        hpx::program_options::variables_map& vm, std::size_t default_)
183
    {
184
        if (vm.count("hpx:numa-sensitive") != 0)
2,446✔
185
        {
186
            std::size_t numa_sensitive =
×
187
                vm["hpx:numa-sensitive"].as<std::size_t>();
×
188
            if (numa_sensitive > 2)
×
189
            {
190
                throw hpx::detail::command_line_error(
×
191
                    "Invalid argument value for --hpx:numa-sensitive. Allowed "
192
                    "values are 0, 1, or 2");
193
            }
194
            return numa_sensitive;
×
195
        }
196

197
        // use either cfgmap value or default
198
        return cfgmap.get_value<std::size_t>("hpx.numa_sensitive", default_);
2,446✔
199
    }
2,446✔
200

201
    ///////////////////////////////////////////////////////////////////////
202
    std::size_t get_number_of_default_threads(bool use_process_mask)
3,642✔
203
    {
204
        if (use_process_mask)
3,642✔
205
        {
206
            threads::topology& top = threads::create_topology();
×
207
            return threads::count(top.get_cpubind_mask());
×
208
        }
209
        return static_cast<std::size_t>(threads::hardware_concurrency());
3,642✔
210
    }
3,642✔
211

212
    std::size_t get_number_of_default_cores(bool use_process_mask)
7,284✔
213
    {
214
        threads::topology& top = threads::create_topology();
7,284✔
215

216
        std::size_t num_cores = top.get_number_of_cores();
7,284✔
217

218
        if (use_process_mask)
7,284✔
219
        {
220
            threads::mask_type proc_mask = top.get_cpubind_mask();
×
221
            std::size_t num_cores_proc_mask = 0;
×
222

223
            for (std::size_t num_core = 0; num_core < num_cores; ++num_core)
×
224
            {
225
                threads::mask_type core_mask =
226
                    top.init_core_affinity_mask_from_core(num_core);
×
227
                if (threads::bit_and(core_mask, proc_mask))
×
228
                {
229
                    ++num_cores_proc_mask;
×
230
                }
×
231
            }
×
232

233
            return num_cores_proc_mask;
×
234
        }
×
235

236
        return num_cores;
7,284✔
237
    }
7,284✔
238

239
    ///////////////////////////////////////////////////////////////////////
240
    std::size_t handle_num_threads(util::manage_config& cfgmap,
2,446✔
241
        hpx::util::runtime_configuration const& rtcfg,
242
        hpx::program_options::variables_map& vm, bool use_process_mask)
243
    {
244
        // If using the process mask we override "cores" and "all" options but
245
        // keep explicit numeric values.
246
        std::size_t const init_threads =
2,446✔
247
            get_number_of_default_threads(use_process_mask);
2,446✔
248
        std::size_t const init_cores =
2,446✔
249
            get_number_of_default_cores(use_process_mask);
2,446✔
250

251
        std::string threads_str = cfgmap.get_value<std::string>(
4,892✔
252
            "hpx.os_threads",
2,446✔
253
            rtcfg.get_entry("hpx.os_threads", std::to_string(init_threads)));
2,446✔
254

255
        std::size_t threads = 0;
2,446✔
256
        if ("cores" == threads_str)
2,446✔
257
        {
258
            threads = init_cores;
1,612✔
259
        }
1,612✔
260
        else if ("all" == threads_str)
834✔
261
        {
262
            threads = init_threads;
700✔
263
        }
700✔
264
        else
265
        {
266
            threads = cfgmap.get_value<std::size_t>("hpx.os_threads",
268✔
267
                hpx::util::from_string<std::size_t>(threads_str));
134✔
268
        }
269

270
        if (vm.count("hpx:threads"))
2,446✔
271
        {
272
            threads_str = vm["hpx:threads"].as<std::string>();
2,416✔
273
            if ("all" == threads_str)
2,416✔
274
            {
275
                threads = init_threads;
20✔
276
            }
20✔
277
            else if ("cores" == threads_str)
2,396✔
278
            {
279
                threads = init_cores;
2✔
280
            }
2✔
281
            else
282
            {
283
                threads = hpx::util::from_string<std::size_t>(threads_str);
2,394✔
284
            }
285

286
            if (threads == 0)
2,416✔
287
            {
288
                throw hpx::detail::command_line_error(
×
289
                    "Number of --hpx:threads must be greater than 0");
290
            }
291

292
#if defined(HPX_HAVE_MAX_CPU_COUNT)
293
            if (threads > HPX_HAVE_MAX_CPU_COUNT)
294
            {
295
                // clang-format off
296
                throw hpx::detail::command_line_error("Requested more than "
297
                    HPX_PP_STRINGIZE(HPX_HAVE_MAX_CPU_COUNT)" --hpx:threads "
298
                    "to use for this application, use the option "
299
                    "-DHPX_WITH_MAX_CPU_COUNT=<N> when configuring HPX.");
300
                // clang-format on
301
            }
302
#endif
303
        }
2,416✔
304

305
        // make sure minimal requested number of threads is observed
306
        std::size_t min_os_threads =
2,446✔
307
            cfgmap.get_value<std::size_t>("hpx.force_min_os_threads", threads);
2,446✔
308

309
        if (min_os_threads == 0)
2,446✔
310
        {
311
            throw hpx::detail::command_line_error(
×
312
                "Number of hpx.force_min_os_threads must be greater than 0");
313
        }
314

315
#if defined(HPX_HAVE_MAX_CPU_COUNT)
316
        if (min_os_threads > HPX_HAVE_MAX_CPU_COUNT)
317
        {
318
            // clang-format off
319
            throw hpx::detail::command_line_error(
320
                "Requested more than " HPX_PP_STRINGIZE(HPX_HAVE_MAX_CPU_COUNT)
321
                " hpx.force_min_os_threads to use for this application, use the "
322
                "option -DHPX_WITH_MAX_CPU_COUNT=<N> when configuring HPX.");
323
            // clang-format on
324
        }
325
#endif
326
        threads = (std::max)(threads, min_os_threads);
2,446✔
327

328
        return threads;
2,446✔
329
    }
2,446✔
330

331
    std::size_t handle_num_cores(util::manage_config& cfgmap,
3,642✔
332
        hpx::program_options::variables_map& vm, std::size_t num_threads,
333
        std::size_t num_default_cores)
334
    {
335
        std::string cores_str = cfgmap.get_value<std::string>("hpx.cores", "");
3,642✔
336
        if ("all" == cores_str)
3,642✔
337
        {
338
            cfgmap.config_["hpx.cores"] = std::to_string(num_default_cores);
×
339
        }
×
340

341
        std::size_t num_cores =
3,642✔
342
            cfgmap.get_value<std::size_t>("hpx.cores", num_threads);
3,642✔
343
        if (vm.count("hpx:cores"))
3,642✔
344
        {
345
            cores_str = vm["hpx:cores"].as<std::string>();
2✔
346
            if ("all" == cores_str)
2✔
347
            {
348
                num_cores = num_default_cores;
×
349
            }
×
350
            else
351
            {
352
                num_cores = hpx::util::from_string<std::size_t>(cores_str);
2✔
353
            }
354
        }
2✔
355

356
        return num_cores;
3,642✔
357
    }
3,642✔
358

359
    std::size_t handle_num_cores(util::manage_config& cfgmap,
2,446✔
360
        hpx::program_options::variables_map& vm, std::size_t num_threads,
361
        bool use_process_mask)
362
    {
363
        return handle_num_cores(cfgmap, vm, num_threads,
4,892✔
364
            get_number_of_default_cores(use_process_mask));
2,446✔
365
    }
366

367
    void print_config(std::vector<std::string> const& ini_config)
×
368
    {
369
        std::cerr << "Configuration before runtime start:\n";
×
370
        std::cerr << "-----------------------------------\n";
×
371
        for (std::string const& s : ini_config)
×
372
        {
373
            std::cerr << s << std::endl;
×
374
        }
375
        std::cerr << "-----------------------------------\n";
×
376
    }
×
377

378
    ///////////////////////////////////////////////////////////////////////
379
    command_line_handling::command_line_handling(
1,223✔
380
        hpx::util::runtime_configuration rtcfg,
381
        std::vector<std::string> ini_config,
382
        hpx::function<int(hpx::program_options::variables_map& vm)> hpx_main_f)
383
      : rtcfg_(HPX_MOVE(rtcfg))
1,223✔
384
      , ini_config_(HPX_MOVE(ini_config))
1,223✔
385
      , hpx_main_f_(HPX_MOVE(hpx_main_f))
1,223✔
386
      , num_threads_(1)
1,223✔
387
      , num_cores_(1)
1,223✔
388
      , pu_step_(1)
1,223✔
389
      , pu_offset_(std::size_t(-1))
1,223✔
390
      , numa_sensitive_(0)
1,223✔
391
      , use_process_mask_(false)
1,223✔
392
      , cmd_line_parsed_(false)
1,223✔
393
      , info_printed_(false)
1,223✔
394
      , version_printed_(false)
1,223✔
395
    {
396
    }
1,223✔
397

398
    void command_line_handling::check_affinity_domain() const
2,446✔
399
    {
400
        if (affinity_domain_ != "pu")
2,446✔
401
        {
402
            if (0 != std::string("pu").find(affinity_domain_) &&
×
403
                0 != std::string("core").find(affinity_domain_) &&
×
404
                0 != std::string("numa").find(affinity_domain_) &&
×
405
                0 != std::string("machine").find(affinity_domain_))
×
406
            {
407
                throw hpx::detail::command_line_error(
×
408
                    "Invalid command line option --hpx:affinity, value must be "
409
                    "one of: pu, core, numa, or machine.");
410
            }
411
        }
×
412
    }
2,446✔
413

414
    void command_line_handling::check_affinity_description() const
2,446✔
415
    {
416
        if (affinity_bind_.empty())
2,446✔
417
        {
418
            return;
×
419
        }
420

421
        if (!(pu_offset_ == std::size_t(-1) || pu_offset_ == std::size_t(0)) ||
4,892✔
422
            pu_step_ != 1 || affinity_domain_ != "pu")
2,446✔
423
        {
424
            throw hpx::detail::command_line_error(
×
425
                "Command line option --hpx:bind should not be used with "
426
                "--hpx:pu-step, --hpx:pu-offset, or --hpx:affinity.");
427
        }
428
    }
2,446✔
429

430
    void command_line_handling::check_pu_offset() const
2,446✔
431
    {
432
        if (pu_offset_ != std::size_t(-1) &&
2,446✔
433
            pu_offset_ >=
×
434
                static_cast<std::size_t>(hpx::threads::hardware_concurrency()))
×
435
        {
436
            throw hpx::detail::command_line_error(
×
437
                "Invalid command line option --hpx:pu-offset, value must be "
438
                "smaller than number of available processing units.");
439
        }
440
    }
2,446✔
441

442
    void command_line_handling::check_pu_step() const
2,446✔
443
    {
444
        if (hpx::threads::hardware_concurrency() > 1 &&
4,892✔
445
            (pu_step_ == 0 ||
2,446✔
446
                pu_step_ >= static_cast<std::size_t>(
4,892✔
447
                                hpx::threads::hardware_concurrency())))
2,446✔
448
        {
449
            throw hpx::detail::command_line_error(
×
450
                "Invalid command line option --hpx:pu-step, value must be "
451
                "non-zero and smaller than number of available processing "
452
                "units.");
453
        }
454
    }
2,446✔
455

456
    void command_line_handling::handle_high_priority_threads(
3,642✔
457
        hpx::program_options::variables_map& vm,
458
        std::vector<std::string>& ini_config)
459
    {
460
        if (vm_.count("hpx:high-priority-threads"))
3,642✔
461
        {
462
            std::size_t num_high_priority_queues =
×
463
                vm["hpx:high-priority-threads"].as<std::size_t>();
×
464
            if (num_high_priority_queues != std::size_t(-1) &&
×
465
                num_high_priority_queues > num_threads_)
×
466
            {
467
                throw hpx::detail::command_line_error(
×
468
                    "Invalid command line option: number of high priority "
469
                    "threads (--hpx:high-priority-threads), should not be "
470
                    "larger than number of threads (--hpx:threads)");
471
            }
472

473
            if (!(queuing_ == "local-priority" || queuing_ == "abp-priority"))
×
474
            {
475
                throw hpx::detail::command_line_error(
×
476
                    "Invalid command line option --hpx:high-priority-threads, "
477
                    "valid for --hpx:queuing=local-priority and "
478
                    "--hpx:queuing=abp-priority only");
479
            }
480

481
            ini_config.emplace_back("hpx.thread_queue.high_priority_queues!=" +
×
482
                std::to_string(num_high_priority_queues));
×
483
        }
×
484
    }
3,642✔
485

486
    ///////////////////////////////////////////////////////////////////////////
487
    bool command_line_handling::handle_arguments(util::manage_config& cfgmap,
2,446✔
488
        hpx::program_options::variables_map& vm,
489
        std::vector<std::string>& ini_config)
490
    {
491
        bool debug_clp = vm.count("hpx:debug-clp");
2,446✔
492

493
        if (vm.count("hpx:ini"))
2,446✔
494
        {
495
            std::vector<std::string> cfg =
496
                vm["hpx:ini"].as<std::vector<std::string>>();
570✔
497
            std::copy(cfg.begin(), cfg.end(), std::back_inserter(ini_config));
570✔
498
            cfgmap.add(cfg);
570✔
499
        }
570✔
500

501
        use_process_mask_ =
2,446✔
502
            (cfgmap.get_value<int>("hpx.use_process_mask", 0) > 0) ||
4,892✔
503
            (vm.count("hpx:use-process-mask") > 0);
2,446✔
504

505
#if defined(__APPLE__)
506
        if (use_process_mask_)
507
        {
508
            std::cerr
509
                << "Warning: enabled process mask for thread binding, but "
510
                   "thread binding is not supported on macOS. Ignoring option."
511
                << std::endl;
512
            use_process_mask_ = false;
513
        }
514
#endif
515

516
        ini_config.emplace_back(
2,446✔
517
            "hpx.use_process_mask!=" + std::to_string(use_process_mask_));
2,446✔
518

519
        // handle setting related to schedulers
520
        queuing_ = detail::handle_queuing(cfgmap, vm, "local-priority-fifo");
2,446✔
521
        ini_config.emplace_back("hpx.scheduler=" + queuing_);
2,446✔
522

523
        affinity_domain_ = detail::handle_affinity(cfgmap, vm, "pu");
2,446✔
524
        ini_config.emplace_back("hpx.affinity=" + affinity_domain_);
2,446✔
525

526
        check_affinity_domain();
2,446✔
527

528
        affinity_bind_ = detail::handle_affinity_bind(cfgmap, vm, "");
2,446✔
529
        if (!affinity_bind_.empty())
2,446✔
530
        {
531
#if defined(__APPLE__)
532
            std::cerr << "Warning: thread binding set to \"" << affinity_bind_
533
                      << "\" but thread binding is not supported on macOS. "
534
                         "Ignoring option."
535
                      << std::endl;
536
            affinity_bind_ = "";
537
#else
538
            ini_config.emplace_back("hpx.bind!=" + affinity_bind_);
×
539
#endif
540
        }
×
541

542
        pu_step_ = detail::handle_pu_step(cfgmap, vm, 1);
2,446✔
543
#if defined(__APPLE__)
544
        if (pu_step_ != 1)
545
        {
546
            std::cerr << "Warning: PU step set to \"" << pu_step_
547
                      << "\" but thread binding is not supported on macOS. "
548
                         "Ignoring option."
549
                      << std::endl;
550
            pu_step_ = 1;
551
        }
552
#endif
553
        ini_config.emplace_back("hpx.pu_step=" + std::to_string(pu_step_));
2,446✔
554

555
        check_pu_step();
2,446✔
556

557
        pu_offset_ = detail::handle_pu_offset(cfgmap, vm, std::size_t(-1));
2,446✔
558

559
        // NOLINTNEXTLINE(bugprone-branch-clone)
560
        if (pu_offset_ != std::size_t(-1))
2,446✔
561
        {
562
#if defined(__APPLE__)
563
            std::cerr << "Warning: PU offset set to \"" << pu_offset_
564
                      << "\" but thread binding is not supported on macOS. "
565
                         "Ignoring option."
566
                      << std::endl;
567
            pu_offset_ = std::size_t(-1);
568
            ini_config.emplace_back("hpx.pu_offset=0");
569
#else
570
            ini_config.emplace_back(
×
571
                "hpx.pu_offset=" + std::to_string(pu_offset_));
×
572
#endif
573
        }
×
574
        else
575
        {
576
            ini_config.emplace_back("hpx.pu_offset=0");
2,446✔
577
        }
578

579
        check_pu_offset();
2,446✔
580

581
        numa_sensitive_ = detail::handle_numa_sensitive(
2,446✔
582
            cfgmap, vm, affinity_bind_.empty() ? 0 : 1);
2,446✔
583
        ini_config.emplace_back(
2,446✔
584
            "hpx.numa_sensitive=" + std::to_string(numa_sensitive_));
2,446✔
585

586
        // default affinity mode is now 'balanced' (only if no pu-step or
587
        // pu-offset is given)
588
        if (pu_step_ == 1 && pu_offset_ == std::size_t(-1) &&
2,446✔
589
            affinity_bind_.empty())
2,446✔
590
        {
591
#if defined(__APPLE__)
592
            affinity_bind_ = "none";
593
#else
594
            affinity_bind_ = "balanced";
2,446✔
595
#endif
596
            ini_config.emplace_back("hpx.bind!=" + affinity_bind_);
2,446✔
597
        }
2,446✔
598

599
        check_affinity_description();
2,446✔
600

601
        // handle number of cores and threads
602
        num_threads_ =
2,446✔
603
            detail::handle_num_threads(cfgmap, rtcfg_, vm, use_process_mask_);
2,446✔
604
        num_cores_ = detail::handle_num_cores(
2,446✔
605
            cfgmap, vm, num_threads_, use_process_mask_);
2,446✔
606

607
        // Set number of cores and OS threads in configuration.
608
        ini_config.emplace_back(
2,446✔
609
            "hpx.os_threads=" + std::to_string(num_threads_));
2,446✔
610
        ini_config.emplace_back("hpx.cores=" + std::to_string(num_cores_));
2,446✔
611

612
        // handle high-priority threads
613
        handle_high_priority_threads(vm, ini_config);
2,446✔
614

615
        enable_logging_settings(vm, ini_config);
2,446✔
616

617
        if (debug_clp)
2,446✔
618
        {
619
            print_config(ini_config);
×
620
        }
×
621

622
        return true;
2,446✔
623
    }
×
624

625
    ///////////////////////////////////////////////////////////////////////////
626
    void command_line_handling::enable_logging_settings(
4,865✔
627
        hpx::program_options::variables_map& vm,
628
        [[maybe_unused]] std::vector<std::string>& ini_config)
629
    {
630
#if defined(HPX_HAVE_LOGGING)
631
        if (vm.count("hpx:debug-hpx-log"))
4,865✔
632
        {
633
            ini_config.emplace_back("hpx.logging.console.destination=" +
9,568✔
634
                detail::convert_to_log_file(
4,784✔
635
                    vm["hpx:debug-hpx-log"].as<std::string>()));
4,784✔
636
            ini_config.emplace_back("hpx.logging.destination=" +
9,568✔
637
                detail::convert_to_log_file(
4,784✔
638
                    vm["hpx:debug-hpx-log"].as<std::string>()));
4,784✔
639
            ini_config.emplace_back("hpx.logging.console.level=5");
4,784✔
640
            ini_config.emplace_back("hpx.logging.level=5");
4,784✔
641
        }
4,784✔
642

643
        if (vm.count("hpx:debug-timing-log"))
4,865✔
644
        {
645
            ini_config.emplace_back("hpx.logging.console.timing.destination=" +
×
646
                detail::convert_to_log_file(
×
647
                    vm["hpx:debug-timing-log"].as<std::string>()));
×
648
            ini_config.emplace_back("hpx.logging.timing.destination=" +
×
649
                detail::convert_to_log_file(
×
650
                    vm["hpx:debug-timing-log"].as<std::string>()));
×
651
            ini_config.emplace_back("hpx.logging.console.timing.level=1");
×
652
            ini_config.emplace_back("hpx.logging.timing.level=1");
×
653
        }
×
654

655
        if (vm.count("hpx:debug-app-log"))
4,865✔
656
        {
657
            ini_config.emplace_back(
×
658
                "hpx.logging.console.application.destination=" +
×
659
                detail::convert_to_log_file(
×
660
                    vm["hpx:debug-app-log"].as<std::string>()));
×
661
            ini_config.emplace_back("hpx.logging.application.destination=" +
×
662
                detail::convert_to_log_file(
×
663
                    vm["hpx:debug-app-log"].as<std::string>()));
×
664
            ini_config.emplace_back("hpx.logging.console.application.level=5");
×
665
            ini_config.emplace_back("hpx.logging.application.level=5");
×
666
        }
×
667
#else
668
        if (vm.count("hpx:debug-hpx-log") || vm.count("hpx:debug-timing-log") ||
669
            vm.count("hpx:debug-app-log"))
670
        {
671
            // clang-format off
672
            throw hpx::detail::command_line_error(
673
                "Command line option error: can't enable logging while it "
674
                "was disabled at configuration time. Please re-configure "
675
                "HPX using the option -DHPX_WITH_LOGGING=On.");
676
            // clang-format on
677
        }
678
#endif
679
    }
4,865✔
680

681
    ///////////////////////////////////////////////////////////////////////////
682
    void command_line_handling::store_command_line(int argc, char** argv)
1,223✔
683
    {
684
        // Collect the command line for diagnostic purposes.
685
        std::string command;
1,223✔
686
        std::string cmd_line;
1,223✔
687
        std::string options;
1,223✔
688
        for (int i = 0; i < argc; ++i)
6,022✔
689
        {
690
            // quote only if it contains whitespace
691
            std::string arg = detail::encode_and_enquote(argv[i]);    //-V108
4,799✔
692

693
            cmd_line += arg;
4,799✔
694
            if (i == 0)
4,799✔
695
            {
696
                command = arg;
1,223✔
697
            }
1,223✔
698
            else
699
            {
700
                options += " " + arg;
3,576✔
701
            }
702

703
            if ((i + 1) != argc)
4,799✔
704
            {
705
                cmd_line += " ";
3,576✔
706
            }
3,576✔
707
        }
4,799✔
708

709
        // Store the program name and the command line.
710
        ini_config_.emplace_back("hpx.cmd_line!=" + cmd_line);
1,223✔
711
        ini_config_.emplace_back("hpx.commandline.command!=" + command);
1,223✔
712
        ini_config_.emplace_back("hpx.commandline.options!=" + options);
1,223✔
713
    }
1,223✔
714

715
    ///////////////////////////////////////////////////////////////////////////
716
    void command_line_handling::store_unregistered_options(
1,223✔
717
        std::string const& cmd_name,
718
        std::vector<std::string> const& unregistered_options)
719
    {
720
        std::string unregistered_options_cmd_line;
1,223✔
721

722
        if (!unregistered_options.empty())
1,223✔
723
        {
724
            typedef std::vector<std::string>::const_iterator iterator_type;
725

726
            iterator_type end = unregistered_options.end();
3✔
727
            for (iterator_type it = unregistered_options.begin(); it != end;
6✔
728
                 ++it)
3✔
729
                unregistered_options_cmd_line +=
3✔
730
                    " " + detail::encode_and_enquote(*it);
3✔
731

732
            ini_config_.emplace_back("hpx.unknown_cmd_line!=" +
9✔
733
                detail::encode_and_enquote(cmd_name) +
6✔
734
                unregistered_options_cmd_line);
735
        }
3✔
736

737
        ini_config_.emplace_back("hpx.program_name!=" + cmd_name);
1,223✔
738
        ini_config_.emplace_back("hpx.reconstructed_cmd_line!=" +
3,669✔
739
            encode_and_enquote(cmd_name) + " " + reconstruct_command_line(vm_) +
2,446✔
740
            " " + unregistered_options_cmd_line);
1,223✔
741
    }
1,223✔
742

743
    ///////////////////////////////////////////////////////////////////////////
744
    bool command_line_handling::handle_help_options(
1,223✔
745
        hpx::program_options::options_description const& help)
746
    {
747
        if (vm_.count("hpx:help"))
1,223✔
748
        {
749
            std::string help_option(vm_["hpx:help"].as<std::string>());
3✔
750
            if (0 == std::string("minimal").find(help_option))
3✔
751
            {
752
                // print static help only
753
                std::cout << help << std::endl;
3✔
754
                return true;
3✔
755
            }
756
            else if (0 == std::string("full").find(help_option))
×
757
            {
758
                // defer printing help until after dynamic part has been
759
                // acquired
760
                std::ostringstream strm;
×
761
                strm << help << std::endl;
×
762
                ini_config_.emplace_back(
×
763
                    "hpx.cmd_line_help!=" + detail::encode_string(strm.str()));
×
764
                ini_config_.emplace_back(
×
765
                    "hpx.cmd_line_help_option!=" + help_option);
×
766
            }
×
767
            else
768
            {
769
                throw hpx::detail::command_line_error(hpx::util::format(
×
770
                    "Invalid argument for option --hpx:help: '{1}', allowed "
×
771
                    "values: 'minimal' (default) and 'full'",
772
                    help_option));
773
            }
774
        }
3✔
775
        return false;
1,220✔
776
    }
1,223✔
777

778
    void command_line_handling::handle_attach_debugger()
1,223✔
779
    {
780
#if defined(_POSIX_VERSION) || defined(HPX_WINDOWS)
781
        if (vm_.count("hpx:attach-debugger"))
1,223✔
782
        {
783
            std::string option = vm_["hpx:attach-debugger"].as<std::string>();
×
784
            if (option != "off" && option != "startup" &&
×
785
                option != "exception" && option != "test-failure")
×
786
            {
787
                // clang-format off
788
                std::cerr <<
×
789
                    "hpx::init: command line warning: --hpx:attach-debugger: "
790
                    "invalid option: " << option << ". Allowed values are "
×
791
                    "'off', 'startup', 'exception' or 'test-failure'" << std::endl;
×
792
                // clang-format on
793
            }
×
794
            else
795
            {
796
                if (option == "startup")
×
797
                    util::attach_debugger();
×
798

799
                ini_config_.emplace_back("hpx.attach_debugger!=" + option);
×
800
            }
801
        }
×
802
#endif
803
    }
1,223✔
804

805
    ///////////////////////////////////////////////////////////////////////////
806
    // separate command line arguments from configuration settings
807
    std::vector<std::string> command_line_handling::preprocess_config_settings(
1,223✔
808
        int argc, char** argv)
809
    {
810
        std::vector<std::string> options;
1,223✔
811
        options.reserve(static_cast<std::size_t>(argc) + ini_config_.size());
1,223✔
812

813
        // extract all command line arguments from configuration settings and
814
        // remove them from this list
815
        auto it = std::stable_partition(ini_config_.begin(), ini_config_.end(),
1,223✔
816
            [](std::string const& e) { return e.find("--hpx:") != 0; });
953✔
817

818
        std::move(it, ini_config_.end(), std::back_inserter(options));
1,223✔
819
        ini_config_.erase(it, ini_config_.end());
1,223✔
820

821
        // store the command line options that came from the configuration
822
        // settings in the registry
823
        if (!options.empty())
1,223✔
824
        {
825
            std::string config_options;
16✔
826
            for (auto const& option : options)
34✔
827
            {
828
                config_options += " " + option;
18✔
829
            }
830

831
            rtcfg_.add_entry("hpx.commandline.config_options", config_options);
16✔
832
        }
16✔
833

834
        // now append all original command line options
835
        for (int i = 1; i != argc; ++i)
4,799✔
836
        {
837
            options.emplace_back(argv[i]);
3,576✔
838
        }
3,576✔
839

840
        return options;
1,223✔
841
    }
1,223✔
842

843
    ///////////////////////////////////////////////////////////////////////////
844
    std::vector<std::string> prepend_options(
1,223✔
845
        std::vector<std::string>&& args, std::string&& options)
846
    {
847
        if (options.empty())
1,223✔
848
        {
849
            return HPX_MOVE(args);
1,223✔
850
        }
851

852
        hpx::string_util::escaped_list_separator sep('\\', ' ', '\"');
×
853
        hpx::string_util::tokenizer tok(options, sep);
×
854

855
        std::vector<std::string> result(tok.begin(), tok.end());
×
856
        std::move(args.begin(), args.end(), std::back_inserter(result));
×
857
        return result;
×
858
    }
1,223✔
859

860
    ///////////////////////////////////////////////////////////////////////////
861
    void command_line_handling::reconfigure(
1,223✔
862
        util::manage_config& cfgmap, hpx::program_options::variables_map& prevm)
863
    {
864
        // re-initialize runtime configuration object
865
        if (prevm.count("hpx:config"))
1,223✔
866
            rtcfg_.reconfigure(prevm["hpx:config"].as<std::string>());
1,223✔
867
        else
868
            rtcfg_.reconfigure("");
×
869

870
        // Make sure any aliases defined on the command line get used for the
871
        // option analysis below.
872
        std::vector<std::string> cfg;
1,223✔
873
        if (prevm.count("hpx:ini"))
1,223✔
874
        {
875
            cfg = prevm["hpx:ini"].as<std::vector<std::string>>();
285✔
876
            cfgmap.add(cfg);
285✔
877
        }
285✔
878

879
        // append ini options from command line
880
        std::copy(
1,223✔
881
            ini_config_.begin(), ini_config_.end(), std::back_inserter(cfg));
1,223✔
882

883
        // enable logging if invoked requested from command line
884
        std::vector<std::string> ini_config_logging;
1,223✔
885
        enable_logging_settings(prevm, ini_config_logging);
1,223✔
886

887
        std::copy(ini_config_logging.begin(), ini_config_logging.end(),
1,223✔
888
            std::back_inserter(cfg));
1,223✔
889

890
        rtcfg_.reconfigure(cfg);
1,223✔
891
    }
1,223✔
892

893
    int command_line_handling::call(
625✔
894
        hpx::program_options::options_description const& desc_cmdline, int argc,
895
        char** argv)
896
    {
897
        // set the flag signaling that command line parsing has been done
898
        cmd_line_parsed_ = true;
625✔
899

900
        // separate command line arguments from configuration settings
901
        std::vector<std::string> args = preprocess_config_settings(argc, argv);
625✔
902

903
        util::manage_config cfgmap(ini_config_);
625✔
904

905
        // insert the pre-configured ini settings before loading modules
906
        for (std::string const& e : ini_config_)
1,023✔
907
            rtcfg_.parse("<user supplied config>", e, true, false);
398✔
908

909
        // support re-throwing command line exceptions for testing purposes
910
        util::commandline_error_mode error_mode =
625✔
911
            util::commandline_error_mode::allow_unregistered;
912
        if (cfgmap.get_value("hpx.commandline.rethrow_errors", 0) != 0)
625✔
913
        {
914
            error_mode |= util::commandline_error_mode::rethrow_on_error;
1✔
915
        }
1✔
916

917
        // The cfg registry may hold command line options to prepend to the
918
        // real command line.
919
        std::string prepend_command_line =
920
            rtcfg_.get_entry("hpx.commandline.prepend_options");
625✔
921

922
        args = prepend_options(HPX_MOVE(args), HPX_MOVE(prepend_command_line));
625✔
923

924
        // Initial analysis of the command line options. This is preliminary as
925
        // it will not take into account any aliases as defined in any of the
926
        // runtime configuration files.
927
        {
928
            // Boost V1.47 and before do not properly reset a variables_map when
929
            // calling vm.clear(). We work around that problems by creating a
930
            // separate instance just for the preliminary command line handling.
931
            hpx::program_options::variables_map prevm;
625✔
932
            if (!parse_commandline(
625✔
933
                    rtcfg_, desc_cmdline, argv[0], args, prevm, error_mode))
625✔
934
            {
935
                return -1;
×
936
            }
937

938
            // handle all --hpx:foo options
939
            std::vector<std::string> ini_config;    // discard
625✔
940
            if (!handle_arguments(cfgmap, prevm, ini_config))
625✔
941
            {
942
                return -2;
×
943
            }
944

945
            reconfigure(cfgmap, prevm);
625✔
946
        }
625✔
947

948
        // Re-run program option analysis, ini settings (such as aliases) will
949
        // be considered now.
950
        hpx::program_options::options_description help;
625✔
951
        std::vector<std::string> unregistered_options;
625✔
952

953
        error_mode |= util::commandline_error_mode::report_missing_config_file;
625✔
954
        if (!parse_commandline(rtcfg_, desc_cmdline, argv[0], args, vm_,
625✔
955
                error_mode, &help, &unregistered_options))
625✔
956
        {
957
            return -1;
×
958
        }
959

960
        // break into debugger, if requested
961
        handle_attach_debugger();
625✔
962

963
        // handle all --hpx:foo options
964
        if (!handle_arguments(cfgmap, vm_, ini_config_))
625✔
965
        {
966
            return -2;
×
967
        }
968

969
        return finalize_commandline_handling(
625✔
970
            argc, argv, help, unregistered_options);
625✔
971
    }
625✔
972

973
    int command_line_handling::finalize_commandline_handling(int argc,
1,223✔
974
        char** argv, hpx::program_options::options_description const& help,
975
        std::vector<std::string> const& unregistered_options)
976
    {
977
        // store unregistered command line and arguments
978
        store_command_line(argc, argv);
1,223✔
979
        store_unregistered_options(argv[0], unregistered_options);
1,223✔
980

981
        // add all remaining ini settings to the global configuration
982
        rtcfg_.reconfigure(ini_config_);
1,223✔
983

984
        // help can be printed only after the runtime mode has been set
985
        if (handle_help_options(help))
1,223✔
986
        {
987
            return 1;    // exit application gracefully
3✔
988
        }
989

990
        // print version/copyright information
991
        if (vm_.count("hpx:version"))
1,220✔
992
        {
993
            if (!version_printed_)
×
994
            {
995
                detail::print_version(std::cout);
×
996
                version_printed_ = true;
×
997
            }
×
998

999
            return 1;
×
1000
        }
1001

1002
        // print configuration information (static and dynamic)
1003
        if (vm_.count("hpx:info"))
1,220✔
1004
        {
1005
            if (!info_printed_)
×
1006
            {
1007
                detail::print_info(std::cout, *this);
×
1008
                info_printed_ = true;
×
1009
            }
×
1010

1011
            return 1;
×
1012
        }
1013

1014
        // all is good
1015
        return 0;
1,220✔
1016
    }
1,223✔
1017
}    // namespace hpx::local::detail
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

© 2026 Coveralls, Inc