xref: /netbsd-src/external/gpl3/gcc.old/dist/libgomp/env.c (revision d909946ca08dceb44d7d0f22ec9488679695d976)
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