1 /* Copyright (C) 2005-2013 Free Software Foundation, Inc. 2 Contributed by Richard Henderson <rth@redhat.com>. 3 4 This file is part of the GNU OpenMP Library (libgomp). 5 6 Libgomp is free software; you can redistribute it and/or modify it 7 under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 3, or (at your option) 9 any later version. 10 11 Libgomp is distributed in the hope that it will be useful, but WITHOUT ANY 12 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14 more details. 15 16 Under Section 7 of GPL version 3, you are granted additional 17 permissions described in the GCC Runtime Library Exception, version 18 3.1, as published by the Free Software Foundation. 19 20 You should have received a copy of the GNU General Public License and 21 a copy of the GCC Runtime Library Exception along with this program; 22 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 23 <http://www.gnu.org/licenses/>. */ 24 25 /* This file defines the OpenMP internal control variables, and arranges 26 for them to be initialized from environment variables at startup. */ 27 28 #include "libgomp.h" 29 #include "libgomp_f.h" 30 #include <ctype.h> 31 #include <stdlib.h> 32 #ifdef STRING_WITH_STRINGS 33 # include <string.h> 34 # include <strings.h> 35 #else 36 # ifdef HAVE_STRING_H 37 # include <string.h> 38 # else 39 # ifdef HAVE_STRINGS_H 40 # include <strings.h> 41 # endif 42 # endif 43 #endif 44 #include <limits.h> 45 #include <errno.h> 46 47 #ifndef HAVE_STRTOULL 48 # define strtoull(ptr, eptr, base) strtoul (ptr, eptr, base) 49 #endif 50 51 struct gomp_task_icv gomp_global_icv = { 52 .nthreads_var = 1, 53 .run_sched_var = GFS_DYNAMIC, 54 .run_sched_modifier = 1, 55 .dyn_var = false, 56 .nest_var = false 57 }; 58 59 unsigned short *gomp_cpu_affinity; 60 size_t gomp_cpu_affinity_len; 61 unsigned long gomp_max_active_levels_var = INT_MAX; 62 unsigned long gomp_thread_limit_var = ULONG_MAX; 63 unsigned long gomp_remaining_threads_count; 64 #ifndef HAVE_SYNC_BUILTINS 65 gomp_mutex_t gomp_remaining_threads_lock; 66 #endif 67 unsigned long gomp_available_cpus = 1, gomp_managed_threads = 1; 68 unsigned long long gomp_spin_count_var, gomp_throttled_spin_count_var; 69 unsigned long *gomp_nthreads_var_list, gomp_nthreads_var_list_len; 70 71 /* Parse the OMP_SCHEDULE environment variable. */ 72 73 static void 74 parse_schedule (void) 75 { 76 char *env, *end; 77 unsigned long value; 78 79 env = getenv ("OMP_SCHEDULE"); 80 if (env == NULL) 81 return; 82 83 while (isspace ((unsigned char) *env)) 84 ++env; 85 if (strncasecmp (env, "static", 6) == 0) 86 { 87 gomp_global_icv.run_sched_var = GFS_STATIC; 88 env += 6; 89 } 90 else if (strncasecmp (env, "dynamic", 7) == 0) 91 { 92 gomp_global_icv.run_sched_var = GFS_DYNAMIC; 93 env += 7; 94 } 95 else if (strncasecmp (env, "guided", 6) == 0) 96 { 97 gomp_global_icv.run_sched_var = GFS_GUIDED; 98 env += 6; 99 } 100 else if (strncasecmp (env, "auto", 4) == 0) 101 { 102 gomp_global_icv.run_sched_var = GFS_AUTO; 103 env += 4; 104 } 105 else 106 goto unknown; 107 108 while (isspace ((unsigned char) *env)) 109 ++env; 110 if (*env == '\0') 111 { 112 gomp_global_icv.run_sched_modifier 113 = gomp_global_icv.run_sched_var != GFS_STATIC; 114 return; 115 } 116 if (*env++ != ',') 117 goto unknown; 118 while (isspace ((unsigned char) *env)) 119 ++env; 120 if (*env == '\0') 121 goto invalid; 122 123 errno = 0; 124 value = strtoul (env, &end, 10); 125 if (errno) 126 goto invalid; 127 128 while (isspace ((unsigned char) *end)) 129 ++end; 130 if (*end != '\0') 131 goto invalid; 132 133 if ((int)value != value) 134 goto invalid; 135 136 if (value == 0 && gomp_global_icv.run_sched_var != GFS_STATIC) 137 value = 1; 138 gomp_global_icv.run_sched_modifier = value; 139 return; 140 141 unknown: 142 gomp_error ("Unknown value for environment variable OMP_SCHEDULE"); 143 return; 144 145 invalid: 146 gomp_error ("Invalid value for chunk size in " 147 "environment variable OMP_SCHEDULE"); 148 return; 149 } 150 151 /* Parse an unsigned long environment variable. Return true if one was 152 present and it was successfully parsed. */ 153 154 static bool 155 parse_unsigned_long (const char *name, unsigned long *pvalue, bool allow_zero) 156 { 157 char *env, *end; 158 unsigned long value; 159 160 env = getenv (name); 161 if (env == NULL) 162 return false; 163 164 while (isspace ((unsigned char) *env)) 165 ++env; 166 if (*env == '\0') 167 goto invalid; 168 169 errno = 0; 170 value = strtoul (env, &end, 10); 171 if (errno || (long) value <= 0 - allow_zero) 172 goto invalid; 173 174 while (isspace ((unsigned char) *end)) 175 ++end; 176 if (*end != '\0') 177 goto invalid; 178 179 *pvalue = value; 180 return true; 181 182 invalid: 183 gomp_error ("Invalid value for environment variable %s", name); 184 return false; 185 } 186 187 /* Parse an unsigned long list environment variable. Return true if one was 188 present and it was successfully parsed. */ 189 190 static bool 191 parse_unsigned_long_list (const char *name, unsigned long *p1stvalue, 192 unsigned long **pvalues, 193 unsigned long *pnvalues) 194 { 195 char *env, *end; 196 unsigned long value, *values = NULL; 197 198 env = getenv (name); 199 if (env == NULL) 200 return false; 201 202 while (isspace ((unsigned char) *env)) 203 ++env; 204 if (*env == '\0') 205 goto invalid; 206 207 errno = 0; 208 value = strtoul (env, &end, 10); 209 if (errno || (long) value <= 0) 210 goto invalid; 211 212 while (isspace ((unsigned char) *end)) 213 ++end; 214 if (*end != '\0') 215 { 216 if (*end == ',') 217 { 218 unsigned long nvalues = 0, nalloced = 0; 219 220 do 221 { 222 env = end + 1; 223 if (nvalues == nalloced) 224 { 225 unsigned long *n; 226 nalloced = nalloced ? nalloced * 2 : 16; 227 n = realloc (values, nalloced * sizeof (unsigned long)); 228 if (n == NULL) 229 { 230 free (values); 231 gomp_error ("Out of memory while trying to parse" 232 " environment variable %s", name); 233 return false; 234 } 235 values = n; 236 if (nvalues == 0) 237 values[nvalues++] = value; 238 } 239 240 while (isspace ((unsigned char) *env)) 241 ++env; 242 if (*env == '\0') 243 goto invalid; 244 245 errno = 0; 246 value = strtoul (env, &end, 10); 247 if (errno || (long) value <= 0) 248 goto invalid; 249 250 values[nvalues++] = value; 251 while (isspace ((unsigned char) *end)) 252 ++end; 253 if (*end == '\0') 254 break; 255 if (*end != ',') 256 goto invalid; 257 } 258 while (1); 259 *p1stvalue = values[0]; 260 *pvalues = values; 261 *pnvalues = nvalues; 262 return true; 263 } 264 goto invalid; 265 } 266 267 *p1stvalue = value; 268 return true; 269 270 invalid: 271 free (values); 272 gomp_error ("Invalid value for environment variable %s", name); 273 return false; 274 } 275 276 /* Parse the OMP_STACKSIZE environment varible. Return true if one was 277 present and it was successfully parsed. */ 278 279 static bool 280 parse_stacksize (const char *name, unsigned long *pvalue) 281 { 282 char *env, *end; 283 unsigned long value, shift = 10; 284 285 env = getenv (name); 286 if (env == NULL) 287 return false; 288 289 while (isspace ((unsigned char) *env)) 290 ++env; 291 if (*env == '\0') 292 goto invalid; 293 294 errno = 0; 295 value = strtoul (env, &end, 10); 296 if (errno) 297 goto invalid; 298 299 while (isspace ((unsigned char) *end)) 300 ++end; 301 if (*end != '\0') 302 { 303 switch (tolower ((unsigned char) *end)) 304 { 305 case 'b': 306 shift = 0; 307 break; 308 case 'k': 309 break; 310 case 'm': 311 shift = 20; 312 break; 313 case 'g': 314 shift = 30; 315 break; 316 default: 317 goto invalid; 318 } 319 ++end; 320 while (isspace ((unsigned char) *end)) 321 ++end; 322 if (*end != '\0') 323 goto invalid; 324 } 325 326 if (((value << shift) >> shift) != value) 327 goto invalid; 328 329 *pvalue = value << shift; 330 return true; 331 332 invalid: 333 gomp_error ("Invalid value for environment variable %s", name); 334 return false; 335 } 336 337 /* Parse the GOMP_SPINCOUNT environment varible. Return true if one was 338 present and it was successfully parsed. */ 339 340 static bool 341 parse_spincount (const char *name, unsigned long long *pvalue) 342 { 343 char *env, *end; 344 unsigned long long value, mult = 1; 345 346 env = getenv (name); 347 if (env == NULL) 348 return false; 349 350 while (isspace ((unsigned char) *env)) 351 ++env; 352 if (*env == '\0') 353 goto invalid; 354 355 if (strncasecmp (env, "infinite", 8) == 0 356 || strncasecmp (env, "infinity", 8) == 0) 357 { 358 value = ~0ULL; 359 end = env + 8; 360 goto check_tail; 361 } 362 363 errno = 0; 364 value = strtoull (env, &end, 10); 365 if (errno) 366 goto invalid; 367 368 while (isspace ((unsigned char) *end)) 369 ++end; 370 if (*end != '\0') 371 { 372 switch (tolower ((unsigned char) *end)) 373 { 374 case 'k': 375 mult = 1000LL; 376 break; 377 case 'm': 378 mult = 1000LL * 1000LL; 379 break; 380 case 'g': 381 mult = 1000LL * 1000LL * 1000LL; 382 break; 383 case 't': 384 mult = 1000LL * 1000LL * 1000LL * 1000LL; 385 break; 386 default: 387 goto invalid; 388 } 389 ++end; 390 check_tail: 391 while (isspace ((unsigned char) *end)) 392 ++end; 393 if (*end != '\0') 394 goto invalid; 395 } 396 397 if (value > ~0ULL / mult) 398 value = ~0ULL; 399 else 400 value *= mult; 401 402 *pvalue = value; 403 return true; 404 405 invalid: 406 gomp_error ("Invalid value for environment variable %s", name); 407 return false; 408 } 409 410 /* Parse a boolean value for environment variable NAME and store the 411 result in VALUE. */ 412 413 static void 414 parse_boolean (const char *name, bool *value) 415 { 416 const char *env; 417 418 env = getenv (name); 419 if (env == NULL) 420 return; 421 422 while (isspace ((unsigned char) *env)) 423 ++env; 424 if (strncasecmp (env, "true", 4) == 0) 425 { 426 *value = true; 427 env += 4; 428 } 429 else if (strncasecmp (env, "false", 5) == 0) 430 { 431 *value = false; 432 env += 5; 433 } 434 else 435 env = "X"; 436 while (isspace ((unsigned char) *env)) 437 ++env; 438 if (*env != '\0') 439 gomp_error ("Invalid value for environment variable %s", name); 440 } 441 442 /* Parse the OMP_WAIT_POLICY environment variable and store the 443 result in gomp_active_wait_policy. */ 444 445 static int 446 parse_wait_policy (void) 447 { 448 const char *env; 449 int ret = -1; 450 451 env = getenv ("OMP_WAIT_POLICY"); 452 if (env == NULL) 453 return -1; 454 455 while (isspace ((unsigned char) *env)) 456 ++env; 457 if (strncasecmp (env, "active", 6) == 0) 458 { 459 ret = 1; 460 env += 6; 461 } 462 else if (strncasecmp (env, "passive", 7) == 0) 463 { 464 ret = 0; 465 env += 7; 466 } 467 else 468 env = "X"; 469 while (isspace ((unsigned char) *env)) 470 ++env; 471 if (*env == '\0') 472 return ret; 473 gomp_error ("Invalid value for environment variable OMP_WAIT_POLICY"); 474 return -1; 475 } 476 477 /* Parse the GOMP_CPU_AFFINITY environment varible. Return true if one was 478 present and it was successfully parsed. */ 479 480 static bool 481 parse_affinity (void) 482 { 483 char *env, *end; 484 unsigned long cpu_beg, cpu_end, cpu_stride; 485 unsigned short *cpus = NULL; 486 size_t allocated = 0, used = 0, needed; 487 488 env = getenv ("GOMP_CPU_AFFINITY"); 489 if (env == NULL) 490 return false; 491 492 do 493 { 494 while (*env == ' ' || *env == '\t') 495 env++; 496 497 cpu_beg = strtoul (env, &end, 0); 498 cpu_end = cpu_beg; 499 cpu_stride = 1; 500 if (env == end || cpu_beg >= 65536) 501 goto invalid; 502 503 env = end; 504 if (*env == '-') 505 { 506 cpu_end = strtoul (++env, &end, 0); 507 if (env == end || cpu_end >= 65536 || cpu_end < cpu_beg) 508 goto invalid; 509 510 env = end; 511 if (*env == ':') 512 { 513 cpu_stride = strtoul (++env, &end, 0); 514 if (env == end || cpu_stride == 0 || cpu_stride >= 65536) 515 goto invalid; 516 517 env = end; 518 } 519 } 520 521 needed = (cpu_end - cpu_beg) / cpu_stride + 1; 522 if (used + needed >= allocated) 523 { 524 unsigned short *new_cpus; 525 526 if (allocated < 64) 527 allocated = 64; 528 if (allocated > needed) 529 allocated <<= 1; 530 else 531 allocated += 2 * needed; 532 new_cpus = realloc (cpus, allocated * sizeof (unsigned short)); 533 if (new_cpus == NULL) 534 { 535 free (cpus); 536 gomp_error ("not enough memory to store GOMP_CPU_AFFINITY list"); 537 return false; 538 } 539 540 cpus = new_cpus; 541 } 542 543 while (needed--) 544 { 545 cpus[used++] = cpu_beg; 546 cpu_beg += cpu_stride; 547 } 548 549 while (*env == ' ' || *env == '\t') 550 env++; 551 552 if (*env == ',') 553 env++; 554 else if (*env == '\0') 555 break; 556 } 557 while (1); 558 559 gomp_cpu_affinity = cpus; 560 gomp_cpu_affinity_len = used; 561 return true; 562 563 invalid: 564 gomp_error ("Invalid value for enviroment variable GOMP_CPU_AFFINITY"); 565 return false; 566 } 567 568 static void __attribute__((constructor)) 569 initialize_env (void) 570 { 571 unsigned long stacksize; 572 int wait_policy; 573 bool bind_var = false; 574 575 /* Do a compile time check that mkomp_h.pl did good job. */ 576 omp_check_defines (); 577 578 parse_schedule (); 579 parse_boolean ("OMP_DYNAMIC", &gomp_global_icv.dyn_var); 580 parse_boolean ("OMP_NESTED", &gomp_global_icv.nest_var); 581 parse_boolean ("OMP_PROC_BIND", &bind_var); 582 parse_unsigned_long ("OMP_MAX_ACTIVE_LEVELS", &gomp_max_active_levels_var, 583 true); 584 parse_unsigned_long ("OMP_THREAD_LIMIT", &gomp_thread_limit_var, false); 585 if (gomp_thread_limit_var != ULONG_MAX) 586 gomp_remaining_threads_count = gomp_thread_limit_var - 1; 587 #ifndef HAVE_SYNC_BUILTINS 588 gomp_mutex_init (&gomp_remaining_threads_lock); 589 #endif 590 gomp_init_num_threads (); 591 gomp_available_cpus = gomp_global_icv.nthreads_var; 592 if (!parse_unsigned_long_list ("OMP_NUM_THREADS", 593 &gomp_global_icv.nthreads_var, 594 &gomp_nthreads_var_list, 595 &gomp_nthreads_var_list_len)) 596 gomp_global_icv.nthreads_var = gomp_available_cpus; 597 if (parse_affinity () || bind_var) 598 gomp_init_affinity (); 599 wait_policy = parse_wait_policy (); 600 if (!parse_spincount ("GOMP_SPINCOUNT", &gomp_spin_count_var)) 601 { 602 /* Using a rough estimation of 100000 spins per msec, 603 use 5 min blocking for OMP_WAIT_POLICY=active, 604 3 msec blocking when OMP_WAIT_POLICY is not specificed 605 and 0 when OMP_WAIT_POLICY=passive. 606 Depending on the CPU speed, this can be e.g. 5 times longer 607 or 5 times shorter. */ 608 if (wait_policy > 0) 609 gomp_spin_count_var = 30000000000LL; 610 else if (wait_policy < 0) 611 gomp_spin_count_var = 300000LL; 612 } 613 /* gomp_throttled_spin_count_var is used when there are more libgomp 614 managed threads than available CPUs. Use very short spinning. */ 615 if (wait_policy > 0) 616 gomp_throttled_spin_count_var = 1000LL; 617 else if (wait_policy < 0) 618 gomp_throttled_spin_count_var = 100LL; 619 if (gomp_throttled_spin_count_var > gomp_spin_count_var) 620 gomp_throttled_spin_count_var = gomp_spin_count_var; 621 622 /* Not strictly environment related, but ordering constructors is tricky. */ 623 pthread_attr_init (&gomp_thread_attr); 624 pthread_attr_setdetachstate (&gomp_thread_attr, PTHREAD_CREATE_DETACHED); 625 626 if (parse_stacksize ("OMP_STACKSIZE", &stacksize) 627 || parse_stacksize ("GOMP_STACKSIZE", &stacksize)) 628 { 629 int err; 630 631 err = pthread_attr_setstacksize (&gomp_thread_attr, stacksize); 632 633 #ifdef PTHREAD_STACK_MIN 634 if (err == EINVAL) 635 { 636 if (stacksize < PTHREAD_STACK_MIN) 637 gomp_error ("Stack size less than minimum of %luk", 638 PTHREAD_STACK_MIN / 1024ul 639 + (PTHREAD_STACK_MIN % 1024 != 0)); 640 else 641 gomp_error ("Stack size larger than system limit"); 642 } 643 else 644 #endif 645 if (err != 0) 646 gomp_error ("Stack size change failed: %s", strerror (err)); 647 } 648 } 649 650 651 /* The public OpenMP API routines that access these variables. */ 652 653 void 654 omp_set_num_threads (int n) 655 { 656 struct gomp_task_icv *icv = gomp_icv (true); 657 icv->nthreads_var = (n > 0 ? n : 1); 658 } 659 660 void 661 omp_set_dynamic (int val) 662 { 663 struct gomp_task_icv *icv = gomp_icv (true); 664 icv->dyn_var = val; 665 } 666 667 int 668 omp_get_dynamic (void) 669 { 670 struct gomp_task_icv *icv = gomp_icv (false); 671 return icv->dyn_var; 672 } 673 674 void 675 omp_set_nested (int val) 676 { 677 struct gomp_task_icv *icv = gomp_icv (true); 678 icv->nest_var = val; 679 } 680 681 int 682 omp_get_nested (void) 683 { 684 struct gomp_task_icv *icv = gomp_icv (false); 685 return icv->nest_var; 686 } 687 688 void 689 omp_set_schedule (omp_sched_t kind, int modifier) 690 { 691 struct gomp_task_icv *icv = gomp_icv (true); 692 switch (kind) 693 { 694 case omp_sched_static: 695 if (modifier < 1) 696 modifier = 0; 697 icv->run_sched_modifier = modifier; 698 break; 699 case omp_sched_dynamic: 700 case omp_sched_guided: 701 if (modifier < 1) 702 modifier = 1; 703 icv->run_sched_modifier = modifier; 704 break; 705 case omp_sched_auto: 706 break; 707 default: 708 return; 709 } 710 icv->run_sched_var = kind; 711 } 712 713 void 714 omp_get_schedule (omp_sched_t *kind, int *modifier) 715 { 716 struct gomp_task_icv *icv = gomp_icv (false); 717 *kind = icv->run_sched_var; 718 *modifier = icv->run_sched_modifier; 719 } 720 721 int 722 omp_get_max_threads (void) 723 { 724 struct gomp_task_icv *icv = gomp_icv (false); 725 return icv->nthreads_var; 726 } 727 728 int 729 omp_get_thread_limit (void) 730 { 731 return gomp_thread_limit_var > INT_MAX ? INT_MAX : gomp_thread_limit_var; 732 } 733 734 void 735 omp_set_max_active_levels (int max_levels) 736 { 737 if (max_levels >= 0) 738 gomp_max_active_levels_var = max_levels; 739 } 740 741 int 742 omp_get_max_active_levels (void) 743 { 744 return gomp_max_active_levels_var; 745 } 746 747 ialias (omp_set_dynamic) 748 ialias (omp_set_nested) 749 ialias (omp_set_num_threads) 750 ialias (omp_get_dynamic) 751 ialias (omp_get_nested) 752 ialias (omp_set_schedule) 753 ialias (omp_get_schedule) 754 ialias (omp_get_max_threads) 755 ialias (omp_get_thread_limit) 756 ialias (omp_set_max_active_levels) 757 ialias (omp_get_max_active_levels) 758