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