xref: /dragonfly/contrib/gcc-4.7/libgomp/env.c (revision d4ef6694)
1 /* Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011
2    Free Software Foundation, Inc.
3    Contributed by Richard Henderson <rth@redhat.com>.
4 
5    This file is part of the GNU OpenMP Library (libgomp).
6 
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.
11 
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.
16 
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.
20 
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/>.  */
25 
26 /* This file defines the OpenMP internal control variables, and arranges
27    for them to be initialized from environment variables at startup.  */
28 
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>
47 
48 #ifndef HAVE_STRTOULL
49 # define strtoull(ptr, eptr, base) strtoul (ptr, eptr, base)
50 #endif
51 
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
58 };
59 
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;
71 
72 /* Parse the OMP_SCHEDULE environment variable.  */
73 
74 static void
75 parse_schedule (void)
76 {
77   char *env, *end;
78   unsigned long value;
79 
80   env = getenv ("OMP_SCHEDULE");
81   if (env == NULL)
82     return;
83 
84   while (isspace ((unsigned char) *env))
85     ++env;
86   if (strncasecmp (env, "static", 6) == 0)
87     {
88       gomp_global_icv.run_sched_var = GFS_STATIC;
89       env += 6;
90     }
91   else if (strncasecmp (env, "dynamic", 7) == 0)
92     {
93       gomp_global_icv.run_sched_var = GFS_DYNAMIC;
94       env += 7;
95     }
96   else if (strncasecmp (env, "guided", 6) == 0)
97     {
98       gomp_global_icv.run_sched_var = GFS_GUIDED;
99       env += 6;
100     }
101   else if (strncasecmp (env, "auto", 4) == 0)
102     {
103       gomp_global_icv.run_sched_var = GFS_AUTO;
104       env += 4;
105     }
106   else
107     goto unknown;
108 
109   while (isspace ((unsigned char) *env))
110     ++env;
111   if (*env == '\0')
112     {
113       gomp_global_icv.run_sched_modifier
114 	= gomp_global_icv.run_sched_var != GFS_STATIC;
115       return;
116     }
117   if (*env++ != ',')
118     goto unknown;
119   while (isspace ((unsigned char) *env))
120     ++env;
121   if (*env == '\0')
122     goto invalid;
123 
124   errno = 0;
125   value = strtoul (env, &end, 10);
126   if (errno)
127     goto invalid;
128 
129   while (isspace ((unsigned char) *end))
130     ++end;
131   if (*end != '\0')
132     goto invalid;
133 
134   if ((int)value != value)
135     goto invalid;
136 
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;
141 
142  unknown:
143   gomp_error ("Unknown value for environment variable OMP_SCHEDULE");
144   return;
145 
146  invalid:
147   gomp_error ("Invalid value for chunk size in "
148 	      "environment variable OMP_SCHEDULE");
149   return;
150 }
151 
152 /* Parse an unsigned long environment variable.  Return true if one was
153    present and it was successfully parsed.  */
154 
155 static bool
156 parse_unsigned_long (const char *name, unsigned long *pvalue, bool allow_zero)
157 {
158   char *env, *end;
159   unsigned long value;
160 
161   env = getenv (name);
162   if (env == NULL)
163     return false;
164 
165   while (isspace ((unsigned char) *env))
166     ++env;
167   if (*env == '\0')
168     goto invalid;
169 
170   errno = 0;
171   value = strtoul (env, &end, 10);
172   if (errno || (long) value <= 0 - allow_zero)
173     goto invalid;
174 
175   while (isspace ((unsigned char) *end))
176     ++end;
177   if (*end != '\0')
178     goto invalid;
179 
180   *pvalue = value;
181   return true;
182 
183  invalid:
184   gomp_error ("Invalid value for environment variable %s", name);
185   return false;
186 }
187 
188 /* Parse an unsigned long list environment variable.  Return true if one was
189    present and it was successfully parsed.  */
190 
191 static bool
192 parse_unsigned_long_list (const char *name, unsigned long *p1stvalue,
193 			  unsigned long **pvalues,
194 			  unsigned long *pnvalues)
195 {
196   char *env, *end;
197   unsigned long value, *values = NULL;
198 
199   env = getenv (name);
200   if (env == NULL)
201     return false;
202 
203   while (isspace ((unsigned char) *env))
204     ++env;
205   if (*env == '\0')
206     goto invalid;
207 
208   errno = 0;
209   value = strtoul (env, &end, 10);
210   if (errno || (long) value <= 0)
211     goto invalid;
212 
213   while (isspace ((unsigned char) *end))
214     ++end;
215   if (*end != '\0')
216     {
217       if (*end == ',')
218 	{
219 	  unsigned long nvalues = 0, nalloced = 0;
220 
221 	  do
222 	    {
223 	      env = end + 1;
224 	      if (nvalues == nalloced)
225 		{
226 		  unsigned long *n;
227 		  nalloced = nalloced ? nalloced * 2 : 16;
228 		  n = realloc (values, nalloced * sizeof (unsigned long));
229 		  if (n == NULL)
230 		    {
231 		      free (values);
232 		      gomp_error ("Out of memory while trying to parse"
233 				  " environment variable %s", name);
234 		      return false;
235 		    }
236 		  values = n;
237 		  if (nvalues == 0)
238 		    values[nvalues++] = value;
239 		}
240 
241 	      while (isspace ((unsigned char) *env))
242 		++env;
243 	      if (*env == '\0')
244 		goto invalid;
245 
246 	      errno = 0;
247 	      value = strtoul (env, &end, 10);
248 	      if (errno || (long) value <= 0)
249 		goto invalid;
250 
251 	      values[nvalues++] = value;
252 	      while (isspace ((unsigned char) *end))
253 		++end;
254 	      if (*end == '\0')
255 		break;
256 	      if (*end != ',')
257 		goto invalid;
258 	    }
259 	  while (1);
260 	  *p1stvalue = values[0];
261 	  *pvalues = values;
262 	  *pnvalues = nvalues;
263 	  return true;
264 	}
265       goto invalid;
266     }
267 
268   *p1stvalue = value;
269   return true;
270 
271  invalid:
272   free (values);
273   gomp_error ("Invalid value for environment variable %s", name);
274   return false;
275 }
276 
277 /* Parse the OMP_STACKSIZE environment varible.  Return true if one was
278    present and it was successfully parsed.  */
279 
280 static bool
281 parse_stacksize (const char *name, unsigned long *pvalue)
282 {
283   char *env, *end;
284   unsigned long value, shift = 10;
285 
286   env = getenv (name);
287   if (env == NULL)
288     return false;
289 
290   while (isspace ((unsigned char) *env))
291     ++env;
292   if (*env == '\0')
293     goto invalid;
294 
295   errno = 0;
296   value = strtoul (env, &end, 10);
297   if (errno)
298     goto invalid;
299 
300   while (isspace ((unsigned char) *end))
301     ++end;
302   if (*end != '\0')
303     {
304       switch (tolower ((unsigned char) *end))
305 	{
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;
319 	}
320       ++end;
321       while (isspace ((unsigned char) *end))
322 	++end;
323       if (*end != '\0')
324 	goto invalid;
325     }
326 
327   if (((value << shift) >> shift) != value)
328     goto invalid;
329 
330   *pvalue = value << shift;
331   return true;
332 
333  invalid:
334   gomp_error ("Invalid value for environment variable %s", name);
335   return false;
336 }
337 
338 /* Parse the GOMP_SPINCOUNT environment varible.  Return true if one was
339    present and it was successfully parsed.  */
340 
341 static bool
342 parse_spincount (const char *name, unsigned long long *pvalue)
343 {
344   char *env, *end;
345   unsigned long long value, mult = 1;
346 
347   env = getenv (name);
348   if (env == NULL)
349     return false;
350 
351   while (isspace ((unsigned char) *env))
352     ++env;
353   if (*env == '\0')
354     goto invalid;
355 
356   if (strncasecmp (env, "infinite", 8) == 0
357       || strncasecmp (env, "infinity", 8) == 0)
358     {
359       value = ~0ULL;
360       end = env + 8;
361       goto check_tail;
362     }
363 
364   errno = 0;
365   value = strtoull (env, &end, 10);
366   if (errno)
367     goto invalid;
368 
369   while (isspace ((unsigned char) *end))
370     ++end;
371   if (*end != '\0')
372     {
373       switch (tolower ((unsigned char) *end))
374 	{
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;
389 	}
390       ++end;
391      check_tail:
392       while (isspace ((unsigned char) *end))
393 	++end;
394       if (*end != '\0')
395 	goto invalid;
396     }
397 
398   if (value > ~0ULL / mult)
399     value = ~0ULL;
400   else
401     value *= mult;
402 
403   *pvalue = value;
404   return true;
405 
406  invalid:
407   gomp_error ("Invalid value for environment variable %s", name);
408   return false;
409 }
410 
411 /* Parse a boolean value for environment variable NAME and store the
412    result in VALUE.  */
413 
414 static void
415 parse_boolean (const char *name, bool *value)
416 {
417   const char *env;
418 
419   env = getenv (name);
420   if (env == NULL)
421     return;
422 
423   while (isspace ((unsigned char) *env))
424     ++env;
425   if (strncasecmp (env, "true", 4) == 0)
426     {
427       *value = true;
428       env += 4;
429     }
430   else if (strncasecmp (env, "false", 5) == 0)
431     {
432       *value = false;
433       env += 5;
434     }
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);
441 }
442 
443 /* Parse the OMP_WAIT_POLICY environment variable and store the
444    result in gomp_active_wait_policy.  */
445 
446 static int
447 parse_wait_policy (void)
448 {
449   const char *env;
450   int ret = -1;
451 
452   env = getenv ("OMP_WAIT_POLICY");
453   if (env == NULL)
454     return -1;
455 
456   while (isspace ((unsigned char) *env))
457     ++env;
458   if (strncasecmp (env, "active", 6) == 0)
459     {
460       ret = 1;
461       env += 6;
462     }
463   else if (strncasecmp (env, "passive", 7) == 0)
464     {
465       ret = 0;
466       env += 7;
467     }
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;
476 }
477 
478 /* Parse the GOMP_CPU_AFFINITY environment varible.  Return true if one was
479    present and it was successfully parsed.  */
480 
481 static bool
482 parse_affinity (void)
483 {
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;
488 
489   env = getenv ("GOMP_CPU_AFFINITY");
490   if (env == NULL)
491     return false;
492 
493   do
494     {
495       while (*env == ' ' || *env == '\t')
496 	env++;
497 
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;
503 
504       env = end;
505       if (*env == '-')
506 	{
507 	  cpu_end = strtoul (++env, &end, 0);
508 	  if (env == end || cpu_end >= 65536 || cpu_end < cpu_beg)
509 	    goto invalid;
510 
511 	  env = end;
512 	  if (*env == ':')
513 	    {
514 	      cpu_stride = strtoul (++env, &end, 0);
515 	      if (env == end || cpu_stride == 0 || cpu_stride >= 65536)
516 		goto invalid;
517 
518 	      env = end;
519 	    }
520 	}
521 
522       needed = (cpu_end - cpu_beg) / cpu_stride + 1;
523       if (used + needed >= allocated)
524 	{
525 	  unsigned short *new_cpus;
526 
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)
535 	    {
536 	      free (cpus);
537 	      gomp_error ("not enough memory to store GOMP_CPU_AFFINITY list");
538 	      return false;
539 	    }
540 
541 	  cpus = new_cpus;
542 	}
543 
544       while (needed--)
545 	{
546 	  cpus[used++] = cpu_beg;
547 	  cpu_beg += cpu_stride;
548 	}
549 
550       while (*env == ' ' || *env == '\t')
551 	env++;
552 
553       if (*env == ',')
554 	env++;
555       else if (*env == '\0')
556 	break;
557     }
558   while (1);
559 
560   gomp_cpu_affinity = cpus;
561   gomp_cpu_affinity_len = used;
562   return true;
563 
564  invalid:
565   gomp_error ("Invalid value for enviroment variable GOMP_CPU_AFFINITY");
566   return false;
567 }
568 
569 static void __attribute__((constructor))
570 initialize_env (void)
571 {
572   unsigned long stacksize;
573   int wait_policy;
574   bool bind_var = false;
575 
576   /* Do a compile time check that mkomp_h.pl did good job.  */
577   omp_check_defines ();
578 
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))
602     {
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;
613     }
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;
622 
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);
626 
627   if (parse_stacksize ("OMP_STACKSIZE", &stacksize)
628       || parse_stacksize ("GOMP_STACKSIZE", &stacksize))
629     {
630       int err;
631 
632       err = pthread_attr_setstacksize (&gomp_thread_attr, stacksize);
633 
634 #ifdef PTHREAD_STACK_MIN
635       if (err == EINVAL)
636 	{
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");
643 	}
644       else
645 #endif
646       if (err != 0)
647 	gomp_error ("Stack size change failed: %s", strerror (err));
648     }
649 }
650 
651 
652 /* The public OpenMP API routines that access these variables.  */
653 
654 void
655 omp_set_num_threads (int n)
656 {
657   struct gomp_task_icv *icv = gomp_icv (true);
658   icv->nthreads_var = (n > 0 ? n : 1);
659 }
660 
661 void
662 omp_set_dynamic (int val)
663 {
664   struct gomp_task_icv *icv = gomp_icv (true);
665   icv->dyn_var = val;
666 }
667 
668 int
669 omp_get_dynamic (void)
670 {
671   struct gomp_task_icv *icv = gomp_icv (false);
672   return icv->dyn_var;
673 }
674 
675 void
676 omp_set_nested (int val)
677 {
678   struct gomp_task_icv *icv = gomp_icv (true);
679   icv->nest_var = val;
680 }
681 
682 int
683 omp_get_nested (void)
684 {
685   struct gomp_task_icv *icv = gomp_icv (false);
686   return icv->nest_var;
687 }
688 
689 void
690 omp_set_schedule (omp_sched_t kind, int modifier)
691 {
692   struct gomp_task_icv *icv = gomp_icv (true);
693   switch (kind)
694     {
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;
710     }
711   icv->run_sched_var = kind;
712 }
713 
714 void
715 omp_get_schedule (omp_sched_t *kind, int *modifier)
716 {
717   struct gomp_task_icv *icv = gomp_icv (false);
718   *kind = icv->run_sched_var;
719   *modifier = icv->run_sched_modifier;
720 }
721 
722 int
723 omp_get_max_threads (void)
724 {
725   struct gomp_task_icv *icv = gomp_icv (false);
726   return icv->nthreads_var;
727 }
728 
729 int
730 omp_get_thread_limit (void)
731 {
732   return gomp_thread_limit_var > INT_MAX ? INT_MAX : gomp_thread_limit_var;
733 }
734 
735 void
736 omp_set_max_active_levels (int max_levels)
737 {
738   if (max_levels >= 0)
739     gomp_max_active_levels_var = max_levels;
740 }
741 
742 int
743 omp_get_max_active_levels (void)
744 {
745   return gomp_max_active_levels_var;
746 }
747 
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)
759